diff --git a/src/Instance.cs b/src/Instance.cs index 7dadd4c..c82cf70 100644 --- a/src/Instance.cs +++ b/src/Instance.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; @@ -579,6 +580,101 @@ public Instance(Store store, Module module, params object[] imports) return _store.GetCachedExtern(ext.of.global); } + /// + /// Get all exported functions + /// + /// An enumerable of functions exported from this instance + public IEnumerable<(string Name, Function Function)> GetFunctions() + { + for (var i = 0; i < int.MaxValue; i++) + { + if (TryGetExtern(i, ExternKind.Func) is not var (name, @extern)) + { + break; + } + + yield return (name, _store.GetCachedExtern(@extern.of.func)); + } + + GC.KeepAlive(_store); + } + + /// + /// Get all exported tables + /// + /// An enumerable of tables exported from this instance + public IEnumerable<(string Name, Table Table)> GetTables() + { + for (var i = 0; i < int.MaxValue; i++) + { + if (TryGetExtern(i, ExternKind.Table) is not var (name, @extern)) + { + break; + } + + yield return (name, new Table(_store, @extern.of.table)); + } + + GC.KeepAlive(_store); + } + + /// + /// Get all exported memories + /// + /// An enumerable of memories exported from this instance + public IEnumerable<(string Name, Memory Memory)> GetMemories() + { + for (var i = 0; i < int.MaxValue; i++) + { + if (TryGetExtern(i, ExternKind.Memory) is not var (name, @extern)) + { + break; + } + + yield return (name, _store.GetCachedExtern(@extern.of.memory)); + } + + GC.KeepAlive(_store); + } + + /// + /// Get all exported globals + /// + /// An enumerable of globals exported from this instance + public IEnumerable<(string Name, Global Global)> GetGlobals() + { + for (var i = 0; i < int.MaxValue; i++) + { + if (TryGetExtern(i, ExternKind.Global) is not var (name, @extern)) + { + break; + } + + yield return (name, _store.GetCachedExtern(@extern.of.global)); + } + + GC.KeepAlive(_store); + } + + private (string name, Extern @extern)? TryGetExtern(int index, ExternKind? type = null) + { + unsafe + { + if (!Native.wasmtime_instance_export_nth(_store.Context.handle, instance, (UIntPtr)index, out var namePtr, out var nameLen, out var @extern)) + { + return null; + } + + if (type != null && type.Value != @extern.kind) + { + return null; + } + + var name = Encoding.UTF8.GetString(namePtr, checked((int)nameLen)); + return (name, @extern); + } + } + private bool TryGetExtern(StoreContext context, string name, out Extern ext) { using var nameBytes = name.ToUTF8(stackalloc byte[Math.Min(64, name.Length * 2)]); @@ -615,9 +711,6 @@ private static class Native [DllImport(Engine.LibraryName)] [return: MarshalAs(UnmanagedType.I1)] public static extern unsafe bool wasmtime_instance_export_nth(IntPtr context, in ExternInstance instance, UIntPtr index, out byte* name, out UIntPtr len, out Extern ext); - - [DllImport(Engine.LibraryName)] - public static extern void wasmtime_instancetype_delete(IntPtr handle); } private readonly Store _store; diff --git a/tests/InstanceTests.cs b/tests/InstanceTests.cs new file mode 100644 index 0000000..1ea71e9 --- /dev/null +++ b/tests/InstanceTests.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using FluentAssertions; +using Xunit; + +namespace Wasmtime.Tests +{ + public class InstanceFixture + : ModuleFixture + { + protected override string ModuleFileName => "hello.wat"; + } + + public class InstanceTests + : IClassFixture, IDisposable + { + private Store Store { get; set; } + + private Linker Linker { get; set; } + + public InstanceTests(InstanceFixture fixture) + { + Fixture = fixture; + Linker = new Linker(Fixture.Engine); + Store = new Store(Fixture.Engine); + + Linker.DefineFunction("env", "add", (int x, int y) => x + y); + Linker.DefineFunction("env", "swap", (int x, int y) => (y, x)); + Linker.DefineFunction("", "hi", (int x, int y) => (y, x)); + + Linker.DefineFunction("", "hello", () => { }); + } + + private InstanceFixture Fixture { get; } + + [Fact] + public void ItGetsExportedFunctions() + { + var instance = Linker.Instantiate(Store, Fixture.Module); + + var results = instance.GetFunctions(); + + results.Single().Name.Should().Be("run"); + } + + public void Dispose() + { + Store.Dispose(); + Linker.Dispose(); + } + } +}