Skip to content

Commit f49020d

Browse files
committed
Progress on neutralizing types for persisted assemblies.
1 parent 2d2b6a1 commit f49020d

File tree

4 files changed

+93
-36
lines changed

4 files changed

+93
-36
lines changed

WebAssembly.Tests/ApiQualityTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ static IEnumerable<string> GatherViolations()
100100

101101
#if NET9_0_OR_GREATER
102102
/// <summary>
103-
/// Ensures that there's no collision between the default value of <see cref="Runtime.CompilerConfiguration.TypeName"/> and any compiled type.
103+
/// Ensures that there's no collision between the default value of <see cref="Runtime.PersistedCompilerConfiguration.TypeName"/> and any compiled type.
104104
/// </summary>
105105
[TestMethod]
106106
public void NoTypeMatchingCompilerConfigurationDefaultNameExists()
107107
{
108-
var defaultTypeName = new Runtime.CompilerConfiguration().TypeName;
108+
var defaultTypeName = new Runtime.PersistedCompilerConfiguration(typeof(object).Assembly, typeof(Module).Assembly).TypeName;
109109
Assert.IsNull(typeof(Module).Assembly.GetType(defaultTypeName));
110110
}
111111
#endif

WebAssembly/Runtime/Compile.cs

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -221,14 +221,6 @@ private static void CheckPreamble(Reader reader)
221221
}
222222

223223
#if NET9_0_OR_GREATER
224-
/// <summary>
225-
/// Creates a <see cref="PersistedAssemblyBuilder"/> from a binary WASM source.
226-
/// </summary>
227-
/// <param name="input">The source of data. The stream is left open after reading is complete.</param>
228-
/// <returns>An assembly builder that can be used to further modify or save the assembly.</returns>
229-
public static PersistedAssemblyBuilder CreatePersistedAssembly(Stream input)
230-
=> CreatePersistedAssembly(input, new());
231-
232224
/// <summary>
233225
/// Creates a <see cref="PersistedAssemblyBuilder"/> from a binary WASM source.
234226
/// </summary>
@@ -237,7 +229,7 @@ public static PersistedAssemblyBuilder CreatePersistedAssembly(Stream input)
237229
/// <returns>An assembly builder that can be used to further modify or save the assembly.</returns>
238230
public static PersistedAssemblyBuilder CreatePersistedAssembly(
239231
Stream input,
240-
CompilerConfiguration configuration
232+
PersistedCompilerConfiguration configuration
241233
)
242234
{
243235
ArgumentNullException.ThrowIfNull(configuration, nameof(configuration));
@@ -250,7 +242,7 @@ CompilerConfiguration configuration
250242

251243
var assembly = new PersistedAssemblyBuilder(
252244
new AssemblyName("CompiledWebAssembly"),
253-
typeof(object).Assembly
245+
configuration.CoreAssembly
254246
);
255247

256248
var module = assembly.DefineDynamicModule("CompiledWebAssembly");
@@ -321,11 +313,7 @@ private static ConstructorInfo FromBinary(
321313

322314
var context = new CompilationContext(configuration);
323315
var exportsBuilder = context.CheckedExportsBuilder = module.DefineType(
324-
#if NET9_0_OR_GREATER
325-
configuration.TypeName,
326-
#else
327-
"CompiledExports",
328-
#endif
316+
configuration.CompiledTypeName,
329317
ClassAttributes,
330318
exportContainer);
331319
MethodBuilder? importedMemoryProvider = null;
@@ -336,7 +324,7 @@ private static ConstructorInfo FromBinary(
336324
var instanceConstructor = exportsBuilder.DefineConstructor(
337325
ConstructorAttributes,
338326
CallingConventions.Standard,
339-
[typeof(Func<string, string, RuntimeImport>)]
327+
[configuration.NeutralizeType(typeof(Func<string, string, RuntimeImport>))]
340328
);
341329
instanceConstructor.DefineParameter(1, ParameterAttributes.None, "findImport");
342330
instanceConstructorIL = instanceConstructor.GetILGenerator();
@@ -544,13 +532,13 @@ private static ConstructorInfo FromBinary(
544532

545533
instanceConstructorIL.Emit(OpCodes.Stfld, memory);
546534

547-
exportsBuilder.AddInterfaceImplementation(typeof(IDisposable));
535+
exportsBuilder.AddInterfaceImplementation(configuration.NeutralizeType(typeof(IDisposable)));
548536

549537
var dispose = exportsBuilder.DefineMethod(
550538
"Dispose",
551539
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot,
552540
CallingConventions.HasThis,
553-
typeof(void),
541+
configuration.NeutralizeType(typeof(void)),
554542
emptyTypes
555543
);
556544

WebAssembly/Runtime/CompilerConfiguration.cs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Diagnostics;
3+
using System.Reflection;
34

45
namespace WebAssembly.Runtime;
56

@@ -15,23 +16,9 @@ public CompilerConfiguration()
1516
{
1617
}
1718

18-
#if NET9_0_OR_GREATER
19-
private string typeName = "WebAssembly.CompiledFromWasm";
19+
internal virtual string CompiledTypeName => "CompiledExports";
2020

21-
/// <summary>
22-
/// Gets or sets the name of the type that hosts the compiled code.
23-
/// Defaults to "WebAssembly.CompiledFromWasm".
24-
/// </summary>
25-
public string TypeName
26-
{
27-
get => typeName;
28-
set
29-
{
30-
ArgumentNullException.ThrowIfNull(value);
31-
typeName = value;
32-
}
33-
}
34-
#endif
21+
internal virtual Type NeutralizeType(Type type) => type;
3522

3623
[DebuggerBrowsable(DebuggerBrowsableState.Never)] //Wrapped by a property
3724
private GetDelegateForTypeCallback getDelegateForType = GetStandardDelegateForType;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#if NET9_0_OR_GREATER
2+
3+
using System;
4+
using System.Linq;
5+
using System.Reflection;
6+
7+
namespace WebAssembly.Runtime;
8+
9+
/// <summary>
10+
/// Extended compiler configurations for generation of persisted assemblies.
11+
/// </summary>
12+
public class PersistedCompilerConfiguration : CompilerConfiguration
13+
{
14+
/// <summary>
15+
/// Creates a new <see cref="PersistedCompilerConfiguration"/> with the provided values.
16+
/// </summary>
17+
/// <param name="coreAssembly">A reference to a .NET Standard or .NET core DLL.</param>
18+
/// <param name="webAssembly">A reference to a WebAssembly for .NET primary DLL linked to <paramref name="coreAssembly"/>.</param>
19+
/// <exception cref="ArgumentNullException"><paramref name="coreAssembly"/> and <paramref name="webAssembly"/> cannot be null.</exception>
20+
public PersistedCompilerConfiguration(Assembly coreAssembly, Assembly webAssembly)
21+
{
22+
ArgumentNullException.ThrowIfNull(coreAssembly);
23+
ArgumentNullException.ThrowIfNull(webAssembly);
24+
25+
CoreAssembly = coreAssembly;
26+
WebAssembly = webAssembly;
27+
}
28+
29+
/// <summary>
30+
/// Gets the reference assembly used to find standard types.
31+
/// Defaults to the runtime host of the <see cref="Assembly"/> type.
32+
/// Required when persisted assembly compilation is used to control which version of .NET is required.
33+
/// </summary>
34+
public Assembly CoreAssembly { get; }
35+
36+
/// <summary>
37+
/// Gets the assembly used to find the various WebAssembly for .NET types.
38+
/// Defaults to the runtime host of the <see cref="Compile"/> type.
39+
/// Required when persisted assembly compilation is used to control which version of WebAssembly for .NET is required.
40+
/// </summary>
41+
public Assembly WebAssembly { get; }
42+
43+
private string typeName = "WebAssembly.CompiledFromWasm";
44+
45+
/// <summary>
46+
/// Gets or sets the name of the type that hosts the compiled code.
47+
/// Defaults to "WebAssembly.CompiledFromWasm".
48+
/// Intended for use with persisted assembly compilation.
49+
/// </summary>
50+
public string TypeName
51+
{
52+
get => typeName;
53+
set
54+
{
55+
ArgumentNullException.ThrowIfNull(value);
56+
typeName = value;
57+
}
58+
}
59+
60+
internal sealed override string CompiledTypeName => typeName;
61+
62+
internal override Type NeutralizeType(Type type)
63+
{
64+
switch (type.Namespace)
65+
{
66+
case "System":
67+
if (type.IsGenericType)
68+
{
69+
var genericArguments = type.GetGenericArguments().Select(NeutralizeType).ToArray();
70+
var rawType = CoreAssembly.GetType($"{type.Namespace}.{type.Name}")!;
71+
return rawType.MakeGenericType(genericArguments);
72+
}
73+
74+
return CoreAssembly.GetType($"{type.Namespace}.{type.Name}")!;
75+
case "WebAssembly.Runtime":
76+
return WebAssembly.GetType($"{type.Namespace}.{type.Name}")!;
77+
}
78+
79+
throw new InvalidOperationException($"Failed to neutralize type {type.Namespace}.{type.Name}.");
80+
}
81+
}
82+
#endif

0 commit comments

Comments
 (0)