Skip to content

Commit add75d0

Browse files
authored
[wasm] optimize interop startup and linker hints (#77256)
Prepare for making legacy interop optional and small optimization
1 parent 7cecd48 commit add75d0

File tree

13 files changed

+250
-238
lines changed

13 files changed

+250
-238
lines changed

src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<Compile Include="System\Runtime\InteropServices\JavaScript\Legacy\DataView.cs" />
3030
<Compile Include="System\Runtime\InteropServices\JavaScript\Legacy\Function.cs" />
3131
<Compile Include="System\Runtime\InteropServices\JavaScript\Legacy\Uint8Array.cs" />
32+
<Compile Include="System\Runtime\InteropServices\JavaScript\Legacy\LegacyHostImplementation.cs" />
3233

3334
<Compile Include="System\Runtime\InteropServices\JavaScript\JSHost.cs" />
3435
<Compile Include="System\Runtime\InteropServices\JavaScript\JSMarshalerType.cs" />

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace System.Runtime.InteropServices.JavaScript
99
{
1010
// this maps to src\mono\wasm\runtime\corebindings.ts
11-
// the methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction
11+
// the public methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction
1212
internal static unsafe partial class JavaScriptExports
1313
{
1414
[MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace System.Runtime.InteropServices.JavaScript
1111
{
1212
// this maps to src\mono\wasm\runtime\legacy\corebindings.ts
13-
// the methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction
13+
// the public methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction
1414
internal static unsafe partial class LegacyExports
1515
{
1616
[MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
@@ -56,7 +56,7 @@ public static IntPtr TryGetCSOwnedObjectJSHandleRef(in object rawObj, int should
5656
}
5757

5858
[MethodImplAttribute(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
59-
public static void CreateCSOwnedProxyRef(nint jsHandle, JSHostImplementation.MappedType mappedType, int shouldAddInflight, out JSObject jsObject)
59+
public static void CreateCSOwnedProxyRef(nint jsHandle, LegacyHostImplementation.MappedType mappedType, int shouldAddInflight, out JSObject jsObject)
6060
{
6161
JSObject? res = null;
6262

@@ -69,12 +69,12 @@ public static void CreateCSOwnedProxyRef(nint jsHandle, JSHostImplementation.Map
6969
#pragma warning disable CS0612 // Type or member is obsolete
7070
res = mappedType switch
7171
{
72-
JSHostImplementation.MappedType.JSObject => new JSObject(jsHandle),
73-
JSHostImplementation.MappedType.Array => new Array(jsHandle),
74-
JSHostImplementation.MappedType.ArrayBuffer => new ArrayBuffer(jsHandle),
75-
JSHostImplementation.MappedType.DataView => new DataView(jsHandle),
76-
JSHostImplementation.MappedType.Function => new Function(jsHandle),
77-
JSHostImplementation.MappedType.Uint8Array => new Uint8Array(jsHandle),
72+
LegacyHostImplementation.MappedType.JSObject => new JSObject(jsHandle),
73+
LegacyHostImplementation.MappedType.Array => new Array(jsHandle),
74+
LegacyHostImplementation.MappedType.ArrayBuffer => new ArrayBuffer(jsHandle),
75+
LegacyHostImplementation.MappedType.DataView => new DataView(jsHandle),
76+
LegacyHostImplementation.MappedType.Function => new Function(jsHandle),
77+
LegacyHostImplementation.MappedType.Uint8Array => new Uint8Array(jsHandle),
7878
_ => throw new ArgumentOutOfRangeException(nameof(mappedType))
7979
};
8080
#pragma warning restore CS0612 // Type or member is obsolete
@@ -265,8 +265,8 @@ public static string GetCallSignatureRef(IntPtr _methodHandle, in object objForR
265265
for (int i = 0; i < parmsLength; i++)
266266
{
267267
Type t = parms[i].ParameterType;
268-
var mt = JSHostImplementation.GetMarshalTypeFromType(t);
269-
result[i] = JSHostImplementation.GetCallSignatureCharacterForMarshalType(mt, null);
268+
var mt = LegacyHostImplementation.GetMarshalTypeFromType(t);
269+
result[i] = LegacyHostImplementation.GetCallSignatureCharacterForMarshalType(mt, null);
270270
}
271271

272272
return new string(result);

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -24,61 +24,5 @@ public struct IntPtrAndHandle
2424
[FieldOffset(0)]
2525
internal RuntimeTypeHandle typeHandle;
2626
}
27-
28-
// see src/mono/wasm/driver.c MARSHAL_TYPE_xxx
29-
public enum MarshalType : int
30-
{
31-
NULL = 0,
32-
INT = 1,
33-
FP64 = 2,
34-
STRING = 3,
35-
VT = 4,
36-
DELEGATE = 5,
37-
TASK = 6,
38-
OBJECT = 7,
39-
BOOL = 8,
40-
ENUM = 9,
41-
URI = 22,
42-
SAFEHANDLE = 23,
43-
ARRAY_BYTE = 10,
44-
ARRAY_UBYTE = 11,
45-
ARRAY_UBYTE_C = 12,
46-
ARRAY_SHORT = 13,
47-
ARRAY_USHORT = 14,
48-
ARRAY_INT = 15,
49-
ARRAY_UINT = 16,
50-
ARRAY_FLOAT = 17,
51-
ARRAY_DOUBLE = 18,
52-
FP32 = 24,
53-
UINT32 = 25,
54-
INT64 = 26,
55-
UINT64 = 27,
56-
CHAR = 28,
57-
STRING_INTERNED = 29,
58-
VOID = 30,
59-
ENUM64 = 31,
60-
POINTER = 32
61-
}
62-
63-
// see src/mono/wasm/driver.c MARSHAL_ERROR_xxx
64-
public enum MarshalError : int
65-
{
66-
BUFFER_TOO_SMALL = 512,
67-
NULL_CLASS_POINTER = 513,
68-
NULL_TYPE_POINTER = 514,
69-
UNSUPPORTED_TYPE = 515,
70-
FIRST = BUFFER_TOO_SMALL
71-
}
72-
73-
// please keep BINDING wasm_type_symbol in sync
74-
public enum MappedType
75-
{
76-
JSObject = 0,
77-
Array = 1,
78-
ArrayBuffer = 2,
79-
DataView = 3,
80-
Function = 4,
81-
Uint8Array = 11,
82-
}
8327
}
8428
}

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs

Lines changed: 11 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,12 @@ namespace System.Runtime.InteropServices.JavaScript
1313
internal static partial class JSHostImplementation
1414
{
1515
private const string TaskGetResultName = "get_Result";
16-
private static readonly MethodInfo s_taskGetResultMethodInfo = typeof(Task<>).GetMethod(TaskGetResultName)!;
16+
private static MethodInfo? s_taskGetResultMethodInfo;
1717
// we use this to maintain identity of JSHandle for a JSObject proxy
1818
public static readonly Dictionary<int, WeakReference<JSObject>> s_csOwnedObjects = new Dictionary<int, WeakReference<JSObject>>();
1919
// we use this to maintain identity of GCHandle for a managed object
2020
public static Dictionary<object, IntPtr> s_gcHandleFromJSOwnedObject = new Dictionary<object, IntPtr>(ReferenceEqualityComparer.Instance);
2121

22-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23-
public static void RegisterCSOwnedObject(JSObject proxy)
24-
{
25-
lock (s_csOwnedObjects)
26-
{
27-
s_csOwnedObjects[(int)proxy.JSHandle] = new WeakReference<JSObject>(proxy, trackResurrection: true);
28-
}
29-
}
30-
3122
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3223
public static void ReleaseCSOwnedObject(nint jsHandle)
3324
{
@@ -41,8 +32,7 @@ public static void ReleaseCSOwnedObject(nint jsHandle)
4132
}
4233
}
4334

44-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45-
public static object? GetTaskResult(Task task)
35+
public static object? GetTaskResultDynamic(Task task)
4636
{
4737
MethodInfo method = GetTaskResultMethodInfo(task.GetType());
4838
if (method != null)
@@ -91,145 +81,6 @@ public static RuntimeMethodHandle GetMethodHandleFromIntPtr(IntPtr ptr)
9181
return temp.methodHandle;
9282
}
9383

94-
public static MarshalType GetMarshalTypeFromType(Type type)
95-
{
96-
if (type is null)
97-
return MarshalType.VOID;
98-
99-
var typeCode = Type.GetTypeCode(type);
100-
if (type.IsEnum)
101-
{
102-
switch (typeCode)
103-
{
104-
case TypeCode.Int32:
105-
case TypeCode.UInt32:
106-
return MarshalType.ENUM;
107-
case TypeCode.Int64:
108-
case TypeCode.UInt64:
109-
return MarshalType.ENUM64;
110-
default:
111-
throw new JSException($"Unsupported enum underlying type {typeCode}");
112-
}
113-
}
114-
115-
switch (typeCode)
116-
{
117-
case TypeCode.SByte:
118-
case TypeCode.Int16:
119-
case TypeCode.Int32:
120-
return MarshalType.INT;
121-
case TypeCode.Byte:
122-
case TypeCode.UInt16:
123-
case TypeCode.UInt32:
124-
return MarshalType.UINT32;
125-
case TypeCode.Boolean:
126-
return MarshalType.BOOL;
127-
case TypeCode.Int64:
128-
return MarshalType.INT64;
129-
case TypeCode.UInt64:
130-
return MarshalType.UINT64;
131-
case TypeCode.Single:
132-
return MarshalType.FP32;
133-
case TypeCode.Double:
134-
return MarshalType.FP64;
135-
case TypeCode.String:
136-
return MarshalType.STRING;
137-
case TypeCode.Char:
138-
return MarshalType.CHAR;
139-
}
140-
141-
if (type.IsArray)
142-
{
143-
if (!type.IsSZArray)
144-
throw new JSException("Only single-dimensional arrays with a zero lower bound can be marshaled to JS");
145-
146-
var elementType = type.GetElementType();
147-
switch (Type.GetTypeCode(elementType))
148-
{
149-
case TypeCode.Byte:
150-
return MarshalType.ARRAY_UBYTE;
151-
case TypeCode.SByte:
152-
return MarshalType.ARRAY_BYTE;
153-
case TypeCode.Int16:
154-
return MarshalType.ARRAY_SHORT;
155-
case TypeCode.UInt16:
156-
return MarshalType.ARRAY_USHORT;
157-
case TypeCode.Int32:
158-
return MarshalType.ARRAY_INT;
159-
case TypeCode.UInt32:
160-
return MarshalType.ARRAY_UINT;
161-
case TypeCode.Single:
162-
return MarshalType.ARRAY_FLOAT;
163-
case TypeCode.Double:
164-
return MarshalType.ARRAY_DOUBLE;
165-
default:
166-
throw new JSException($"Unsupported array element type {elementType}");
167-
}
168-
}
169-
else if (type == typeof(IntPtr))
170-
return MarshalType.POINTER;
171-
else if (type == typeof(UIntPtr))
172-
return MarshalType.POINTER;
173-
else if (type == typeof(SafeHandle))
174-
return MarshalType.SAFEHANDLE;
175-
else if (typeof(Delegate).IsAssignableFrom(type))
176-
return MarshalType.DELEGATE;
177-
else if ((type == typeof(Task)) || typeof(Task).IsAssignableFrom(type))
178-
return MarshalType.TASK;
179-
else if (type.FullName == "System.Uri")
180-
return MarshalType.URI;
181-
else if (type.IsPointer)
182-
return MarshalType.POINTER;
183-
184-
if (type.IsValueType)
185-
return MarshalType.VT;
186-
else
187-
return MarshalType.OBJECT;
188-
}
189-
190-
public static char GetCallSignatureCharacterForMarshalType(MarshalType t, char? defaultValue)
191-
{
192-
switch (t)
193-
{
194-
case MarshalType.BOOL:
195-
return 'b';
196-
case MarshalType.UINT32:
197-
case MarshalType.POINTER:
198-
return 'I';
199-
case MarshalType.INT:
200-
return 'i';
201-
case MarshalType.UINT64:
202-
return 'L';
203-
case MarshalType.INT64:
204-
return 'l';
205-
case MarshalType.FP32:
206-
return 'f';
207-
case MarshalType.FP64:
208-
return 'd';
209-
case MarshalType.STRING:
210-
return 's';
211-
case MarshalType.URI:
212-
return 'u';
213-
case MarshalType.SAFEHANDLE:
214-
return 'h';
215-
case MarshalType.ENUM:
216-
return 'j'; // this is wrong for uint enums
217-
case MarshalType.ENUM64:
218-
return 'k'; // this is wrong for ulong enums
219-
case MarshalType.TASK:
220-
case MarshalType.DELEGATE:
221-
case MarshalType.OBJECT:
222-
return 'o';
223-
case MarshalType.VT:
224-
return 'a';
225-
default:
226-
if (defaultValue.HasValue)
227-
return defaultValue.Value;
228-
else
229-
throw new JSException($"Unsupported marshal type {t}");
230-
}
231-
}
232-
23384
/// <summary>
23485
/// Gets the MethodInfo for the Task{T}.Result property getter.
23586
/// </summary>
@@ -242,15 +93,19 @@ public static char GetCallSignatureCharacterForMarshalType(MarshalType t, char?
24293
/// ensuring that trimming doesn't change the application's behavior.
24394
/// </remarks>
24495
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
245-
Justification = "Task<T>.Result is preserved by the ILLinker because _taskGetResultMethodInfo was initialized with it.")]
96+
Justification = "Task<T>.Result is preserved by the ILLinker because s_taskGetResultMethodInfo was initialized with it.")]
24697
public static MethodInfo GetTaskResultMethodInfo(Type taskType)
24798
{
24899
if (taskType != null)
249100
{
250-
MethodInfo? result = taskType.GetMethod(TaskGetResultName);
251-
if (result != null && result.HasSameMetadataDefinitionAs(s_taskGetResultMethodInfo))
101+
if (s_taskGetResultMethodInfo == null)
102+
{
103+
s_taskGetResultMethodInfo = typeof(Task<>).GetMethod(TaskGetResultName);
104+
}
105+
MethodInfo? getter = taskType.GetMethod(TaskGetResultName);
106+
if (getter != null && getter.HasSameMetadataDefinitionAs(s_taskGetResultMethodInfo!))
252107
{
253-
return result;
108+
return getter;
254109
}
255110
}
256111

@@ -269,7 +124,7 @@ public static void ThrowException(ref JSMarshalerArgument arg)
269124
throw new InvalidProgramException();
270125
}
271126

272-
public static async Task<JSObject> ImportAsync(string moduleName, string moduleUrl, CancellationToken cancellationToken )
127+
public static async Task<JSObject> ImportAsync(string moduleName, string moduleUrl, CancellationToken cancellationToken)
273128
{
274129
Task<JSObject> modulePromise = JavaScriptImports.DynamicImport(moduleName, moduleUrl);
275130
var wrappedTask = CancelationHelper(modulePromise, cancellationToken);

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class Array : JSObject
1818
public Array(params object[] _params)
1919
: base(JavaScriptImports.CreateCSOwnedObject(nameof(Array), _params))
2020
{
21-
JSHostImplementation.RegisterCSOwnedObject(this);
21+
LegacyHostImplementation.RegisterCSOwnedObject(this);
2222
}
2323

2424
/// <summary>

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class ArrayBuffer : JSObject
1313
public ArrayBuffer(int length)
1414
: base(JavaScriptImports.CreateCSOwnedObject(nameof(ArrayBuffer), new object[] { length }))
1515
{
16-
JSHostImplementation.RegisterCSOwnedObject(this);
16+
LegacyHostImplementation.RegisterCSOwnedObject(this);
1717
}
1818

1919
/// <summary>

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class DataView : JSObject
1717
public DataView(ArrayBuffer buffer)
1818
: base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer }))
1919
{
20-
JSHostImplementation.RegisterCSOwnedObject(this);
20+
LegacyHostImplementation.RegisterCSOwnedObject(this);
2121
}
2222

2323
/// <summary>
@@ -28,7 +28,7 @@ public DataView(ArrayBuffer buffer)
2828
public DataView(ArrayBuffer buffer, int byteOffset)
2929
: base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset }))
3030
{
31-
JSHostImplementation.RegisterCSOwnedObject(this);
31+
LegacyHostImplementation.RegisterCSOwnedObject(this);
3232
}
3333

3434
/// <summary>
@@ -40,7 +40,7 @@ public DataView(ArrayBuffer buffer, int byteOffset)
4040
public DataView(ArrayBuffer buffer, int byteOffset, int byteLength)
4141
: base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset, byteLength }))
4242
{
43-
JSHostImplementation.RegisterCSOwnedObject(this);
43+
LegacyHostImplementation.RegisterCSOwnedObject(this);
4444
}
4545

4646
/// <summary>

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class Function : JSObject
1818
public Function(params object[] args)
1919
: base(JavaScriptImports.CreateCSOwnedObject(nameof(Function), args))
2020
{
21-
JSHostImplementation.RegisterCSOwnedObject(this);
21+
LegacyHostImplementation.RegisterCSOwnedObject(this);
2222
}
2323

2424
internal Function(IntPtr jsHandle) : base(jsHandle)

0 commit comments

Comments
 (0)