[browser] new EmccEnableAssertions and EmccEnvironment MSBuild props (#82954)
authorPavel Savara <pavel.savara@gmail.com>
Wed, 15 Mar 2023 10:02:51 +0000 (11:02 +0100)
committerGitHub <noreply@github.com>
Wed, 15 Mar 2023 10:02:51 +0000 (11:02 +0100)
* EmccEnableAssertions and EmccEnvironment MSBuild props
* symbols as separate asset type

Co-authored-by: Ankit Jain <radical@gmail.com>
Co-authored-by: Marek FiĊĦera <mara@neptuo.com>
13 files changed:
src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj
src/mono/wasi/build/WasiApp.Native.targets
src/mono/wasm/build/WasmApp.Native.targets
src/mono/wasm/build/WasmApp.targets
src/mono/wasm/runtime/assets.ts
src/mono/wasm/runtime/dotnet.d.ts
src/mono/wasm/runtime/jiterpreter-jit-call.ts
src/mono/wasm/runtime/logging.ts
src/mono/wasm/runtime/startup.ts
src/mono/wasm/runtime/types.ts
src/mono/wasm/runtime/types/emscripten.ts
src/mono/wasm/wasm.proj
src/tasks/WasmAppBuilder/WasmAppBuilder.cs

index 11eac88..9379e71 100644 (file)
@@ -4,6 +4,9 @@
     <EnableAggressiveTrimming>true</EnableAggressiveTrimming>
     <PublishTrimmed>true</PublishTrimmed>
     <WasmEnableWebcil>true</WasmEnableWebcil>
+    <WasmEmitSymbolMap>true</WasmEmitSymbolMap>
+    <EmccEnableAssertions>true</EmccEnableAssertions>
+    <EmccEnvironment>web</EmccEnvironment>
     <!-- add OpenGL emulation -->
     <EmccExtraLDFlags> -s USE_CLOSURE_COMPILER=1 -s LEGACY_GL_EMULATION=1 -lGL -lSDL -lidbfs.js</EmccExtraLDFlags>
     <!-- just to prove we don't do JS eval() -->
index 95cd07c..03a62b7 100644 (file)
       <!--<_WasmEHLib Condition="'$(WasmEnableExceptionHandling)' != 'true'">libmono-wasm-eh-js.a</_WasmEHLib>-->
       <!--<_WasmEHLibToExclude Condition="'$(WasmEnableExceptionHandling)' == 'true'">libmono-wasm-eh-js.a</_WasmEHLibToExclude>-->
       <!--<_WasmEHLibToExclude Condition="'$(WasmEnableExceptionHandling)' != 'true'">libmono-wasm-eh-wasm.a</_WasmEHLibToExclude>-->
-      <!--<_EmccExportedRuntimeMethods>&quot;[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]&quot;</_EmccExportedRuntimeMethods>-->
-      <!--<_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',')</_EmccExportedFunctions>-->
     <!--</PropertyGroup>-->
     <!--<ItemGroup>-->
       <!--[> order matters <]-->
index d0837e1..7aa3cb7 100644 (file)
 
       <_WasmDevel Condition="'$(_WasmDevel)' == '' and '$(WasmBuildNative)' == 'true' and '$(Configuration)' == 'Debug'">true</_WasmDevel>
 
-      <_EmccAssertionLevelDefault Condition="'$(_EmccAssertionLevelDefault)' == ''">0</_EmccAssertionLevelDefault>
       <_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0</_EmccOptimizationFlagDefault>
       <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(Configuration)' == 'Debug' and '$(WasmBuildingForNestedPublish)' != 'true'">-O1</_EmccOptimizationFlagDefault>
       <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz</_EmccOptimizationFlagDefault>
 
       <!-- Adding optimization flag at the top, so it gets precedence -->
       <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" />
-      <_EmccCFlags Include="-s ASSERTIONS=$(_EmccAssertionLevelDefault)" Condition="'$(_WasmDevel)' == 'true'" />
       <_EmccCFlags Include="@(_EmccCommonFlags)" />
 
       <_EmccCFlags Include="-DDISABLE_PERFTRACING_LISTEN_PORTS=1" />
 
       <!-- Adding optimization flag at the top, so it gets precedence -->
       <_EmccLDFlags Include="$(EmccLinkOptimizationFlag)" />
-      <_EmccLDFlags Include="-s ASSERTIONS=$(_EmccAssertionLevelDefault)" Condition="'$(_WasmDevel)' == 'true'" />
       <_EmccLDFlags Include="@(_EmccCommonFlags)" />
 
       <_DriverCDependencies Include="$(_WasmPInvokeHPath);$(_WasmICallTablePath)" />
       <_WasmEHLib Condition="'$(WasmEnableExceptionHandling)' != 'true'">libmono-wasm-eh-js.a</_WasmEHLib>
       <_WasmEHLibToExclude Condition="'$(WasmEnableExceptionHandling)' == 'true'">libmono-wasm-eh-js.a</_WasmEHLibToExclude>
       <_WasmEHLibToExclude Condition="'$(WasmEnableExceptionHandling)' != 'true'">libmono-wasm-eh-wasm.a</_WasmEHLibToExclude>
+      <_EmccExportedLibraryFunction>&quot;[@(EmccExportedLibraryFunction -> '%27%(Identity)%27', ',')]&quot;</_EmccExportedLibraryFunction>
       <_EmccExportedRuntimeMethods>&quot;[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]&quot;</_EmccExportedRuntimeMethods>
       <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',')</_EmccExportedFunctions>
     </PropertyGroup>
     <ItemGroup>
       <!-- order matters -->
+      <!-- some flags will be duplicated on the commandline and in the .rsp file. The last wins. -->
       <_EmccLDSFlags Include="-s INITIAL_MEMORY=$(EmccInitialHeapSize)" />
       <_EmccLDSFlags Include="-s STACK_SIZE=$(EmccStackSize)" />
       <_EmccLDSFlags Include="-s WASM_BIGINT=1" />
+      <_EmccLDSFlags Condition="'$(EmccEnvironment)' != ''" Include="-s ENVIRONMENT=&quot;$(EmccEnvironment)&quot;" />
+      <_EmccLDSFlags Condition="'$(EmccEnableAssertions)' == 'true'" Include="-s ASSERTIONS=1" />
 
       <_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" />
       <_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />
       <_EmccLinkStepArgs Include="-o &quot;$(_WasmIntermediateOutputPath)dotnet.js&quot;" />
       <_WasmLinkDependencies Include="$(_EmccLinkRsp)" />
 
+      <_EmccLinkStepArgs Include="-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$(_EmccExportedLibraryFunction)" Condition="'$(_EmccExportedLibraryFunction)' != ''" />
       <_EmccLinkStepArgs Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" />
       <_EmccLinkStepArgs Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" />
 
index b5ab71e..796a223 100644 (file)
@@ -49,6 +49,8 @@
       - $(EmccFlags)                        - Emcc flags used for both compiling native files, and linking
       - $(EmccExtraLDFlags)                 - Extra emcc flags for linking
       - $(EmccExtraCFlags)                  - Extra emcc flags for compiling native files
+      - $(EmccEnableAssertions)             - Corresponds to `ASSERTIONS` arg for emcc. Default false.
+      - $(EmccEnvironment)                  - Corresponds to `ENVIRONMENT` arg for emcc. Default is `web,webview,worker,node,shell`.
       - $(EmccInitialHeapSize)              - Initial heap size specified with `emcc`. Default value: 16777216 or size of the DLLs, whichever is larger.
                                               Corresponds to `-s INITIAL_MEMORY=...` emcc arg.
                                               (previously named EmccTotalMemory, which is still kept as an alias)
@@ -90,6 +92,7 @@
                                     <WasmExtraConfig Include="string_val" Value="&quot;abc&quot;" />
                                     <WasmExtraConfig Include="string_with_json" Value="&quot;{ &quot;abc&quot;: 4 }&quot;" />
       - @(EmccExportedRuntimeMethod) - Extra method for emcc flag EXPORTED_RUNTIME_METHODS
+      - @(EmccExportedLibraryFunction) - Extra method for emcc flag DEFAULT_LIBRARY_FUNCS_TO_INCLUDE
       - @(EmccExportedFunction)      - Extra function for emcc flag EXPORTED_FUNCTIONS
   -->
 
                        Condition="'$(WasmEmitSymbolMap)' == 'true' and
                                   '$(_HasDotnetJsSymbols)' != 'true' and
                                   Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.js.symbols')" />
-      <WasmFilesToIncludeInFileSystem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js.symbols'" />
     </ItemGroup>
 
     <ItemGroup Condition="'$(InvariantGlobalization)' != 'true'">
index 2852e1f..b829f03 100644 (file)
@@ -4,6 +4,7 @@
 import cwraps from "./cwraps";
 import { mono_wasm_load_icu_data } from "./icu";
 import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, Module, runtimeHelpers } from "./imports";
+import { parseSymbolMapFile } from "./logging";
 import { mono_wasm_load_bytes_into_heap } from "./memory";
 import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
 import { createPromiseController, PromiseAndController } from "./promise-controller";
@@ -36,6 +37,7 @@ const skipBufferByAssetTypes: {
     [k: string]: boolean
 } = {
     "dotnetwasm": true,
+    "symbols": true,
 };
 
 const containedInSnapshotByAssetTypes: {
@@ -57,6 +59,7 @@ const skipInstantiateByAssetTypes: {
 } = {
     "js-module-threads": true,
     "dotnetwasm": true,
+    "symbols": true,
 };
 
 export function shouldLoadIcuAsset(asset: AssetEntryInternal): boolean {
@@ -119,9 +122,7 @@ export async function mono_download_assets(): Promise<void> {
                 countAndStartDownload(asset);
             } else {
                 // Otherwise cleanup in case we were given pending download. It would be even better if we could abort the download.
-                asset.pendingDownloadInternal = null as any; // GC
-                asset.pendingDownload = null as any; // GC
-                asset.buffer = null as any; // GC
+                cleanupAsset(asset);
                 // tell the debugger it is loaded
                 if (asset.behavior == "resource" || asset.behavior == "assembly" || asset.behavior == "pdb") {
                     const url = resolve_path(asset, "");
@@ -144,15 +145,17 @@ export async function mono_download_assets(): Promise<void> {
                         const url = asset.pendingDownloadInternal!.url;
                         mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array or buffer like");
                         const data = new Uint8Array(asset.buffer!);
-                        asset.pendingDownloadInternal = null as any; // GC
-                        asset.pendingDownload = null as any; // GC
-                        asset.buffer = null as any; // GC
+                        cleanupAsset(asset);
 
                         // wait till after onRuntimeInitialized and after memory snapshot is loaded or skipped
                         await memorySnapshotSkippedOrDone.promise;
                         await beforeOnRuntimeInitialized.promise;
                         _instantiate_asset(asset, url, data);
                     }
+                    if (asset.behavior === "symbols") {
+                        await instantiate_symbols_asset(asset);
+                        cleanupAsset(asset);
+                    }
                 } else {
                     const headersOnly = skipBufferByAssetTypes[asset.behavior];
                     if (!headersOnly) {
@@ -415,6 +418,7 @@ function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
     switch (asset.behavior) {
         case "dotnetwasm":
         case "js-module-threads":
+        case "symbols":
             // do nothing
             break;
         case "resource":
@@ -516,6 +520,18 @@ export async function instantiate_wasm_asset(
     successCallback(compiledInstance, compiledModule);
 }
 
+export async function instantiate_symbols_asset(
+    pendingAsset: AssetEntryInternal,
+): Promise<void> {
+    try {
+        const response = await pendingAsset.pendingDownloadInternal!.response;
+        const text = await response.text();
+        parseSymbolMapFile(text);
+    } catch (error: any) {
+        console.log(`MONO_WASM: Error loading symbol file ${pendingAsset.name}: ${JSON.stringify(error)}`);
+    }
+}
+
 // used from Blazor
 export function mono_wasm_load_data_archive(data: Uint8Array, prefix: string): boolean {
     if (data.length < 8)
@@ -580,3 +596,10 @@ export async function wait_for_all_assets() {
 export function mono_wasm_get_loaded_files(): string[] {
     return runtimeHelpers.loadedFiles;
 }
+
+export function cleanupAsset(asset: AssetEntryInternal) {
+    // give GC chance to collect resources
+    asset.pendingDownloadInternal = null as any; // GC
+    asset.pendingDownload = null as any; // GC
+    asset.buffer = null as any; // GC
+}
\ No newline at end of file
index d80c3a4..f315031 100644 (file)
@@ -59,7 +59,6 @@ declare interface EmscriptenModule {
     UTF8ArrayToString(u8Array: Uint8Array, idx?: number, maxBytesToRead?: number): string;
     FS_createPath(parent: string, path: string, canRead?: boolean, canWrite?: boolean): string;
     FS_createDataFile(parent: string, name: string, data: TypedArray, canRead: boolean, canWrite: boolean, canOwn?: boolean): string;
-    FS_readFile(filename: string, opts: any): any;
     addFunction(fn: Function, signature: string): number;
     stackSave(): VoidPtr;
     stackRestore(stack: VoidPtr): void;
@@ -178,7 +177,7 @@ interface AssetEntry extends ResourceRequest {
      */
     pendingDownload?: LoadingResource;
 }
-type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads";
+type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-threads" | "symbols";
 type GlobalizationMode = "icu" | // load ICU globalization data from any runtime assets with behavior "icu".
 "invariant" | //  operate in invariant globalization mode.
 "auto";
index e84fc62..8cc2d23 100644 (file)
@@ -156,6 +156,9 @@ class TrampolineInfo {
     }
 }
 
+// this is cached replacements for Module.getWasmTableEntry();
+// we could add <EmccExportedLibraryFunction Include="$getWasmTableEntry" /> and <EmccExportedRuntimeMethod Include="getWasmTableEntry" /> 
+// if we need to export the original
 function getWasmTableEntry (index: number) {
     let result = fnCache[index];
     if (!result) {
@@ -172,8 +175,6 @@ function getWasmTableEntry (index: number) {
 export function mono_interp_invoke_wasm_jit_call_trampoline (
     thunkIndex: number, ret_sp: number, sp: number, ftndesc: number, thrown: NativePointer
 ) {
-    // FIXME: It's impossible to get emscripten to export this for some reason
-    // const thunk = <Function>Module.getWasmTableEntry(thunkIndex);
     const thunk = <Function>getWasmTableEntry(thunkIndex);
     try {
         thunk(ret_sp, sp, ftndesc, thrown);
index fb8cb01..a4420b0 100644 (file)
@@ -1,11 +1,10 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-import BuildConfiguration from "consts:configuration";
 import { INTERNAL, Module, runtimeHelpers } from "./imports";
 import { CharPtr, VoidPtr } from "./types/emscripten";
 
-const wasm_func_map = new Map<number, string>();
+export const wasm_func_map = new Map<number, string>();
 const regexes: any[] = [];
 
 // V8
@@ -183,31 +182,15 @@ export function setup_proxy_console(id: string, console: Console, origin: string
         anyConsole[m] = proxyConsoleMethod(`console.${m}`, send, true);
 }
 
-export function readSymbolMapFile(filename: string): void {
-    if (runtimeHelpers.mono_wasm_symbols_are_ready) return;
-    runtimeHelpers.mono_wasm_symbols_are_ready = true;
-    try {
-        const res = Module.FS_readFile(filename, { flags: "r", encoding: "utf8" });
-        res.split(/[\r\n]/).forEach((line: string) => {
-            const parts: string[] = line.split(/:/);
-            if (parts.length < 2)
-                return;
-
-            parts[1] = parts.splice(1).join(":");
-            wasm_func_map.set(Number(parts[0]), parts[1]);
-        });
-        if (BuildConfiguration === "Debug") {
-            console.debug(`MONO_WASM: Loaded ${wasm_func_map.size} symbols`);
-        }
-    } catch (error: any) {
-        if (error.errno == 44) {// NOENT
-            if (BuildConfiguration === "Debug") {
-                console.debug(`MONO_WASM: Could not find symbols file ${filename}. Ignoring.`);
-            }
-        }
-        else {
-            console.log(`MONO_WASM: Error loading symbol file ${filename}: ${JSON.stringify(error)}`);
-        }
-        return;
-    }
-}
+export function parseSymbolMapFile(text: string) {
+    text.split(/[\r\n]/).forEach((line: string) => {
+        const parts: string[] = line.split(/:/);
+        if (parts.length < 2)
+            return;
+
+        parts[1] = parts.splice(1).join(":");
+        wasm_func_map.set(Number(parts[0]), parts[1]);
+    });
+
+    if (runtimeHelpers.diagnosticTracing) console.debug(`MONO_WASM: Loaded ${wasm_func_map.size} symbols`);
+}
\ No newline at end of file
index 970a840..fc795d9 100644 (file)
@@ -21,7 +21,6 @@ import { init_managed_exports } from "./managed-exports";
 import { cwraps_internal } from "./exports-internal";
 import { CharPtr, InstantiateWasmCallBack, InstantiateWasmSuccessCallback } from "./types/emscripten";
 import { instantiate_wasm_asset, mono_download_assets, resolve_asset_path, start_asset_download, wait_for_all_assets } from "./assets";
-import { readSymbolMapFile } from "./logging";
 import { mono_wasm_init_diagnostics } from "./diagnostics";
 import { preAllocatePThreadWorkerPool, instantiateWasmPThreadWorkerPool } from "./pthreads/browser";
 import { export_linker } from "./exports-linker";
@@ -267,7 +266,6 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
 
         bindings_init();
         if (!runtimeHelpers.mono_wasm_runtime_is_ready) mono_wasm_runtime_ready();
-        if (!runtimeHelpers.mono_wasm_symbols_are_ready) readSymbolMapFile("dotnet.js.symbols");
 
         setTimeout(() => {
             // when there are free CPU cycles
index cd8b63d..a1be576 100644 (file)
@@ -206,6 +206,7 @@ export type AssetBehaviours =
     | "vfs" // load asset into the virtual filesystem (for fopen, File.Open, etc)
     | "dotnetwasm" // the binary of the dotnet runtime
     | "js-module-threads" // the javascript module for threads
+    | "symbols" // the symbols for the wasm native code
 
 export type RuntimeHelpers = {
     runtime_interop_module: MonoAssembly;
@@ -216,7 +217,6 @@ export type RuntimeHelpers = {
     _i52_error_scratch_buffer: Int32Ptr;
     mono_wasm_runtime_is_ready: boolean;
     mono_wasm_bindings_is_ready: boolean;
-    mono_wasm_symbols_are_ready: boolean;
 
     loaded_files: string[];
     maxParallelDownloads: number;
index cc8ac64..7c50845 100644 (file)
@@ -50,7 +50,6 @@ export declare interface EmscriptenModule {
     UTF8ArrayToString(u8Array: Uint8Array, idx?: number, maxBytesToRead?: number): string;
     FS_createPath(parent: string, path: string, canRead?: boolean, canWrite?: boolean): string;
     FS_createDataFile(parent: string, name: string, data: TypedArray, canRead: boolean, canWrite: boolean, canOwn?: boolean): string;
-    FS_readFile(filename: string, opts: any): any;
     addFunction(fn: Function, signature: string): number;
     stackSave(): VoidPtr;
     stackRestore(stack: VoidPtr): void;
index 76cab4d..9755e40 100644 (file)
       <EmccExportedRuntimeMethod Include="FS_createDataFile" />
       <EmccExportedRuntimeMethod Include="removeRunDependency" />
       <EmccExportedRuntimeMethod Include="addRunDependency" />
-      <EmccExportedRuntimeMethod Include="FS_readFile" />
       <EmccExportedRuntimeMethod Include="addFunction" />
-      <EmccExportedRuntimeMethod Include="getWasmTableEntry" />
 
       <EmccExportedFunction Include="_malloc" />
       <EmccExportedFunction Include="_memalign" />
       <EmccExportedFunction Include="_free" />
     </ItemGroup>
     <PropertyGroup>
+      <_EmccExportedLibraryFunction>&quot;[@(EmccExportedLibraryFunction -> '%27%(Identity)%27', ',')]&quot;</_EmccExportedLibraryFunction>
       <_EmccExportedRuntimeMethods>&quot;[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]&quot;</_EmccExportedRuntimeMethods>
       <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',')</_EmccExportedFunctions>
       <EmccInitialHeapSize>16777216</EmccInitialHeapSize>
       <_EmccLinkFlags Include="-s ALLOW_TABLE_GROWTH=1" />
       <_EmccLinkFlags Include="-s NO_EXIT_RUNTIME=1" />
       <_EmccLinkFlags Include="-s FORCE_FILESYSTEM=1" />
+      <_EmccLinkFlags Condition="'$(_EmccExportedLibraryFunction)' != ''" Include="-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$(_EmccExportedLibraryFunction)" />
       <_EmccLinkFlags Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" />
       <_EmccLinkFlags Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" />
       <_EmccLinkFlags Include="--source-map-base http://example.com" />
       <CMakeConfigurationEmccFlags Condition="'$(Configuration)' == 'Debug'">-g -Os -s -DDEBUG=1 -DENABLE_AOT_PROFILER=1 -DENABLE_BROWSER_PROFILER=1</CMakeConfigurationEmccFlags>
       <CMakeConfigurationEmccFlags Condition="'$(Configuration)' == 'Release'">-Oz -DENABLE_BROWSER_PROFILER=1</CMakeConfigurationEmccFlags>
 
-      <CMakeConfigurationLinkFlags Condition="'$(Configuration)' == 'Debug'"  >$(CMakeConfigurationEmccFlags)</CMakeConfigurationLinkFlags>
+      <CMakeConfigurationLinkFlags Condition="'$(Configuration)' == 'Debug'"  >$(CMakeConfigurationEmccFlags) -s ASSERTIONS=1 </CMakeConfigurationLinkFlags>
       <CMakeConfigurationLinkFlags Condition="'$(Configuration)' == 'Release'">-O2</CMakeConfigurationLinkFlags>
 
       <CMakeConfigurationLinkFlags>$(CMakeConfigurationLinkFlags) -s EXPORT_ES6=1</CMakeConfigurationLinkFlags>
index c74b581..7d9b47c 100644 (file)
@@ -116,6 +116,11 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask
         public bool LoadRemote { get; set; }
     }
 
+    private sealed class SymbolsData : AssetEntry
+    {
+        public SymbolsData(string name, string hash) : base(name, hash, "symbols") {}
+    }
+
     protected override bool ValidateArguments()
     {
         if (!base.ValidateArguments())
@@ -198,6 +203,10 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask
             {
                 config.Assets.Add(new ThreadsWorkerEntry (name, Utils.ComputeIntegrity(item.ItemSpec)));
             }
+            else if(name == "dotnet.js.symbols")
+            {
+                config.Assets.Add(new SymbolsData(name, Utils.ComputeIntegrity(item.ItemSpec)));
+            }
         }
 
         string packageJsonPath = Path.Combine(AppDir, "package.json");