diff --git a/src/Linker.cs b/src/Linker.cs
index 2e36b29..98a0118 100644
--- a/src/Linker.cs
+++ b/src/Linker.cs
@@ -27,6 +27,11 @@ public Linker(Engine engine)
handle = new Handle(Native.wasmtime_linker_new(engine.NativeHandle));
}
+ private Linker(IntPtr handle)
+ {
+ this.handle = new Handle(handle);
+ }
+
///
/// Configures whether or not the linker allows later definitions to shadow previous definitions.
///
@@ -38,6 +43,14 @@ public bool AllowShadowing
}
}
+ ///
+ /// Clones existing linker.
+ ///
+ public Linker Clone()
+ {
+ return new Linker(Native.wasmtime_linker_clone(handle));
+ }
+
private void Define(string module, string name, T item)
where T : IExternal
{
@@ -511,6 +524,9 @@ internal static class Native
[DllImport(Engine.LibraryName)]
public static extern void wasmtime_linker_allow_shadowing(Handle linker, [MarshalAs(UnmanagedType.I1)] bool allow);
+ [DllImport(Engine.LibraryName)]
+ public static extern IntPtr wasmtime_linker_clone(Handle linker);
+
[DllImport(Engine.LibraryName)]
public static unsafe extern IntPtr wasmtime_linker_define(Handle linker, IntPtr context, byte* module, nuint moduleLen, byte* name, nuint nameLen, in Extern item);
diff --git a/tests/LinkerTests.cs b/tests/LinkerTests.cs
index 6bab78e..ab16d4b 100644
--- a/tests/LinkerTests.cs
+++ b/tests/LinkerTests.cs
@@ -35,6 +35,95 @@ public void ItThrowsWithNullEngine()
Assert.Throws(() => new Linker(null!));
}
+ [Fact]
+ public void CloneCarriesAllowShadowing()
+ {
+ Linker.AllowShadowing = true;
+
+ using var clone = Linker.Clone();
+
+ clone.DefineFunction("", "hello", () => { });
+ clone.DefineFunction("", "hello", () => { });
+ }
+
+ [Fact]
+ public void CloneCarriesDefinitions()
+ {
+ var called = 0;
+ Linker.DefineFunction("", "hello", () => { called++; });
+
+ using var clone = Linker.Clone();
+ clone.GetFunction(Store, "", "hello")!.Invoke();
+
+ Assert.Equal(1, called);
+ }
+
+ [Fact]
+ public void CloneCarriesWasi()
+ {
+ Linker.DefineWasi();
+
+ using var clone = Linker.Clone();
+
+ Assert.Throws(() => clone.DefineWasi());
+ }
+
+ [Fact]
+ public void CloneDisallowsShadowingByDefault()
+ {
+ using var clone = Linker.Clone();
+
+ clone.DefineFunction("", "hello", () => { });
+ Assert.Throws(() => clone.DefineFunction("", "hello", () => { }));
+ }
+
+ [Fact]
+ public void CloneDoesNotShareDefinitionsAddedToTheClone()
+ {
+ using var clone = Linker.Clone();
+ clone.DefineFunction("", "hello", () => { });
+
+ Assert.NotNull(clone.GetFunction(Store, "", "hello"));
+ Assert.Null(Linker.GetFunction(Store, "", "hello"));
+ }
+
+ [Fact]
+ public void CloneDoesNotShareDefinitionsAddedToTheOriginal()
+ {
+ using var clone = Linker.Clone();
+ Linker.DefineFunction("", "hello", () => { });
+
+ Assert.NotNull(Linker.GetFunction(Store, "", "hello"));
+ Assert.Null(clone.GetFunction(Store, "", "hello"));
+ }
+
+ [Fact]
+ public void CloneOutlivesTheOriginal()
+ {
+ var called = 0;
+ var linker = new Linker(Fixture.Engine);
+ linker.DefineFunction("", "hello", () => { called++; });
+
+ using var clone = linker.Clone();
+ linker.Dispose();
+
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+
+ clone.GetFunction(Store, "", "hello")!.Invoke();
+
+ Assert.Equal(1, called);
+ }
+
+ [Fact]
+ public void CloneThrowsWhenDisposed()
+ {
+ var linker = new Linker(Fixture.Engine);
+ linker.Dispose();
+
+ Assert.Throws(() => linker.Clone());
+ }
+
[Fact]
public void DefineThrowsWithNullStore()
{