Skip to content

Commit 5922fd3

Browse files
authored
[browser] fix JSExport in lazy loaded assembly (#117890)
1 parent c87888a commit 5922fd3

File tree

10 files changed

+75
-23
lines changed

10 files changed

+75
-23
lines changed

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,16 +283,14 @@ public static unsafe JSFunctionBinding BindManagedFunction(string fullyQualified
283283
{
284284
var (assemblyName, nameSpace, shortClassName, methodName) = ParseFQN(fullyQualifiedName);
285285

286-
var dllName = assemblyName + ".dll";
287-
288286
IntPtr monoMethod;
289287
Interop.Runtime.GetAssemblyExport(
290288
// FIXME: Pass UTF-16 through directly so C can work with it, doing the conversion
291289
// in C# pulls in a bunch of dependencies we don't need this early in startup.
292290
// I tested removing the UTF8 conversion from this specific call, but other parts
293291
// of startup I can't identify still pull in UTF16->UTF8 conversion, so it's not
294292
// worth it to do that yet.
295-
Marshal.StringToCoTaskMemUTF8(dllName),
293+
Marshal.StringToCoTaskMemUTF8(assemblyName),
296294
Marshal.StringToCoTaskMemUTF8(nameSpace),
297295
Marshal.StringToCoTaskMemUTF8(shortClassName),
298296
Marshal.StringToCoTaskMemUTF8(methodName),

src/mono/browser/runtime/invoke-cs.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ function bind_fn_0V (closure: BindingClosure) {
123123
// call C# side
124124
invoke_sync_jsexport(method, args);
125125
} finally {
126-
Module.stackRestore(sp);
126+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
127+
127128
endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
128129
}
129130
};
@@ -147,7 +148,8 @@ function bind_fn_1V (closure: BindingClosure) {
147148
// call C# side
148149
invoke_sync_jsexport(method, args);
149150
} finally {
150-
Module.stackRestore(sp);
151+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
152+
151153
endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
152154
}
153155
};
@@ -175,7 +177,8 @@ function bind_fn_1R (closure: BindingClosure) {
175177
const js_result = res_converter(args);
176178
return js_result;
177179
} finally {
178-
Module.stackRestore(sp);
180+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
181+
179182
endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
180183
}
181184
};
@@ -208,7 +211,8 @@ function bind_fn_1RA (closure: BindingClosure) {
208211

209212
return promise;
210213
} finally {
211-
Module.stackRestore(sp);
214+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
215+
212216
endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
213217
}
214218
};
@@ -238,7 +242,8 @@ function bind_fn_2R (closure: BindingClosure) {
238242
const js_result = res_converter(args);
239243
return js_result;
240244
} finally {
241-
Module.stackRestore(sp);
245+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
246+
242247
endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
243248
}
244249
};
@@ -273,7 +278,8 @@ function bind_fn_2RA (closure: BindingClosure) {
273278

274279
return promise;
275280
} finally {
276-
Module.stackRestore(sp);
281+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
282+
277283
endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
278284
}
279285
};
@@ -325,7 +331,8 @@ function bind_fn (closure: BindingClosure) {
325331
}
326332
return js_result;
327333
} finally {
328-
Module.stackRestore(sp);
334+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
335+
329336
endMeasure(mark, MeasuredBlock.callCsFunction, fqn);
330337
}
331338
};

src/mono/browser/runtime/managed-exports.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ export function call_entry_point (main_assembly_name: string, program_args: stri
7474

7575
return promise;
7676
} finally {
77-
Module.stackRestore(sp); // synchronously
77+
// synchronously
78+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
7879
}
7980
}
8081

@@ -90,7 +91,8 @@ export function load_satellite_assembly (dll: Uint8Array): void {
9091
marshal_array_to_cs(arg1, dll, MarshalerType.Byte);
9192
invoke_sync_jsexport(managedExports.LoadSatelliteAssembly, args);
9293
} finally {
93-
Module.stackRestore(sp);
94+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
95+
9496
}
9597
}
9698

@@ -109,7 +111,8 @@ export function load_lazy_assembly (dll: Uint8Array, pdb: Uint8Array | null): vo
109111
marshal_array_to_cs(arg2, pdb, MarshalerType.Byte);
110112
invoke_sync_jsexport(managedExports.LoadLazyAssembly, args);
111113
} finally {
112-
Module.stackRestore(sp);
114+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
115+
113116
}
114117
}
115118

@@ -132,7 +135,8 @@ export function release_js_owned_object_by_gc_handle (gc_handle: GCHandle) {
132135
invoke_async_jsexport(runtimeHelpers.ioThreadTID, managedExports.ReleaseJSOwnedObjectByGCHandle, args, size);
133136
}
134137
} finally {
135-
Module.stackRestore(sp);
138+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
139+
136140
}
137141
}
138142

@@ -157,7 +161,8 @@ export function complete_task (holder_gc_handle: GCHandle, error?: any, data?: a
157161
}
158162
invoke_async_jsexport(runtimeHelpers.ioThreadTID, managedExports.CompleteTask, args, size);
159163
} finally {
160-
Module.stackRestore(sp);
164+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
165+
161166
}
162167
}
163168

@@ -203,7 +208,8 @@ export function call_delegate (callback_gc_handle: GCHandle, arg1_js: any, arg2_
203208
return res_converter(res);
204209
}
205210
} finally {
206-
Module.stackRestore(sp);
211+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
212+
207213
}
208214
}
209215

@@ -223,7 +229,8 @@ export function get_managed_stack_trace (exception_gc_handle: GCHandle) {
223229
const res = get_arg(args, 1);
224230
return marshal_string_to_js(res);
225231
} finally {
226-
Module.stackRestore(sp);
232+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
233+
227234
}
228235
}
229236

@@ -341,7 +348,8 @@ export function bind_assembly_exports (assemblyName: string): Promise<void> {
341348
}
342349
return promise;
343350
} finally {
344-
Module.stackRestore(sp); // synchronously
351+
// synchronously
352+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
345353
}
346354
}
347355

src/mono/browser/runtime/memory.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads";
66
import { MemOffset, NumberOrPointer } from "./types/internal";
77
import { VoidPtr, CharPtr } from "./types/emscripten";
88
import cwraps, { I52Error } from "./cwraps";
9-
import { Module, mono_assert, runtimeHelpers } from "./globals";
9+
import { loaderHelpers, Module, mono_assert, runtimeHelpers } from "./globals";
1010
import { utf8ToString } from "./strings";
1111
import { mono_log_warn, mono_log_error } from "./logging";
1212

@@ -327,7 +327,8 @@ export function withStackAlloc<T1, T2, T3, TResult> (bytesWanted: number, f: (pt
327327
try {
328328
return f(ptr, ud1, ud2, ud3);
329329
} finally {
330-
Module.stackRestore(sp);
330+
if (loaderHelpers.is_runtime_running()) Module.stackRestore(sp);
331+
331332
}
332333
}
333334

src/mono/wasm/testassets/WasmBasicTestApp/App/LazyLoadingTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
using System;
66
using System.Text.Json;
77
using System.Runtime.InteropServices.JavaScript;
8+
using System.Diagnostics.CodeAnalysis;
89

910
public partial class LazyLoadingTest
1011
{
1112
[JSExport]
13+
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "LazyLibrary.Foo", "LazyLibrary")]
1214
public static void Run()
1315
{
1416
// System.Text.Json is marked as lazy loaded in the csproj ("BlazorWebAssemblyLazyLoad"), this method can be called only after the assembly is lazy loaded

src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
<ItemGroup>
2121
<ProjectReference Include="..\Json\Json.csproj" />
2222
<ProjectReference Include="..\ResourceLibrary\ResourceLibrary.csproj" />
23+
<ProjectReference Include="..\LazyLibrary\LazyLibrary.csproj" />
2324
<WasmEnvironmentVariable Include="baz" Value="boo" />
2425
</ItemGroup>
2526

2627
<ItemGroup Condition="'$(TestLazyLoading)' == 'true'">
2728
<BlazorWebAssemblyLazyLoad Include="Json$(WasmAssemblyExtension)" />
29+
<BlazorWebAssemblyLazyLoad Include="LazyLibrary$(WasmAssemblyExtension)" />
2830
<WasmExtraFilesToDeploy Include="profiler.js" />
2931
</ItemGroup>
3032
</Project>

src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,16 @@ try {
209209
}
210210

211211
await INTERNAL.loadLazyAssembly(`Json${lazyAssemblyExtension}`);
212+
exports.LazyLoadingTest.Run();
213+
await INTERNAL.loadLazyAssembly(`LazyLibrary${lazyAssemblyExtension}`);
214+
const { LazyLibrary } = await getAssemblyExports("LazyLibrary");
215+
const resLazy = LazyLibrary.Foo.Bar();
216+
exit(resLazy == 42 ? 0 : 1);
217+
}
218+
else {
219+
exports.LazyLoadingTest.Run();
220+
exit(0);
212221
}
213-
exports.LazyLoadingTest.Run();
214-
exit(0);
215222
break;
216223
case "LibraryInitializerTest":
217224
exit(0);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices.JavaScript;
6+
7+
[assembly:System.Runtime.Versioning.SupportedOSPlatform("browser")]
8+
9+
namespace LazyLibrary;
10+
11+
public partial class Foo
12+
{
13+
[JSExport]
14+
public static int Bar()
15+
{
16+
Console.WriteLine("Hello from Foo.Bar!");
17+
return 42;
18+
}
19+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net10.0</TargetFramework>
4+
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
5+
<OutputType>Library</OutputType>
6+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
7+
</PropertyGroup>
8+
</Project>

src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ private void WriteBootConfig(string entryAssemblyName)
388388
endLineNumber: 0,
389389
endColumnNumber: 0,
390390
message: message,
391-
string.Join(";", LazyLoadedAssemblies.Select(a => a.ItemSpec)));
391+
string.Join(";", remainingLazyLoadAssemblies.Select(a => a.ItemSpec)));
392392

393393
return;
394394
}

0 commit comments

Comments
 (0)