diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 52fe4a6b17284..763b369167db5 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -190,7 +190,8 @@ diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index c5e8d064a7ba7..89f3a4b754d54 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -14,7 +14,7 @@ - $HARNESS_RUNNER wasm test --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js -v --output-directory=$XHARNESS_OUT -- --run WasmTestRunner.dll $(AssemblyName).dll + $HARNESS_RUNNER wasm test --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js -v --output-directory=$XHARNESS_OUT -- --enable-zoneinfo --run WasmTestRunner.dll $(AssemblyName).dll diff --git a/src/libraries/Native/native-binplace.proj b/src/libraries/Native/native-binplace.proj index 7f2a188824b66..da3083061a05a 100644 --- a/src/libraries/Native/native-binplace.proj +++ b/src/libraries/Native/native-binplace.proj @@ -24,6 +24,7 @@ + diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 3194d628f15bb..0ceedddb64c3f 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -16,7 +16,7 @@ MONO_BIN_DIR?=$(BINDIR)/mono/Browser.wasm.$(CONFIG) SYS_NATIVE_DIR?=$(OBJDIR)/native/net5.0-Browser-$(CONFIG)-wasm/System.Native BUILDS_BIN_DIR?=$(BINDIR)/native/net5.0-Browser-$(CONFIG)-wasm -all: build-native +all: build-native timezone-data # # EMSCRIPTEN SETUP @@ -56,7 +56,7 @@ MONO_LIBS = \ $(MONO_BIN_DIR)/libmono-icall-table.a \ ${SYS_NATIVE_DIR}/libSystem.Native.a -EMCC_FLAGS=--profiling-funcs -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s BINARYEN=1 -s ALIASING_FUNCTION_POINTERS=0 -s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'FS_createPath', 'FS_createDataFile', 'cwrap', 'setValue', 'getValue', 'UTF8ToString', 'addFunction']" -s "EXPORTED_FUNCTIONS=['_putchar']" --source-map-base http://example.com -emit-llvm -s FORCE_FILESYSTEM=1 -s USE_ZLIB=1 +EMCC_FLAGS=--profiling-funcs -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s BINARYEN=1 -s ALIASING_FUNCTION_POINTERS=0 -s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'FS_createPath', 'FS_createDataFile', 'cwrap', 'setValue', 'getValue', 'UTF8ToString', 'UTF8ArrayToString', 'addFunction']" -s "EXPORTED_FUNCTIONS=['_putchar']" --source-map-base http://example.com -emit-llvm -s FORCE_FILESYSTEM=1 -s USE_ZLIB=1 EMCC_DEBUG_FLAGS =-g -Os -s ASSERTIONS=1 -DENABLE_NETCORE=1 -DDEBUG=1 EMCC_RELEASE_FLAGS=-Oz --llvm-opts 2 -DENABLE_NETCORE=1 @@ -112,6 +112,9 @@ clean: # Helper targets .PHONY: runtime +timezone-data: + cp runtime/dotnet.timezones.blat $(BUILDS_BIN_DIR) + runtime: EMSDK_PATH=$(TOP)/src/mono/wasm/emsdk $(TOP)/build.sh --subset mono --arch wasm --os Browser -c $(CONFIG) diff --git a/src/mono/wasm/runtime-test.js b/src/mono/wasm/runtime-test.js index d41ed2abfa147..dfbd2bca32b0d 100644 --- a/src/mono/wasm/runtime-test.js +++ b/src/mono/wasm/runtime-test.js @@ -93,12 +93,12 @@ function fail_exec (reason) { } function inspect_object (o) { - var r = ""; - for(var p in o) { - var t = typeof o[p]; - r += "'" + p + "' => '" + t + "', "; - } - return r; + var r = ""; + for(var p in o) { + var t = typeof o[p]; + r += "'" + p + "' => '" + t + "', "; + } + return r; } // Preprocess arguments @@ -132,7 +132,7 @@ while (true) { args = args.slice (1); } else if (args [0] == "--enable-zoneinfo") { enable_zoneinfo = true; - args = args.slice (1); + args = args.slice (1); } else { break; } @@ -149,7 +149,7 @@ function writeContentToFile(content, path) if (typeof window == "undefined") load ("mono-config.js"); -var Module = { +var Module = { mainScriptUrlOrBlob: "dotnet.js", print: print, @@ -205,37 +205,25 @@ var Module = { Module.ccall ('mono_wasm_enable_on_demand_gc', 'void', ['number'], [0]); } if (enable_zoneinfo) { - // Load the zoneinfo data into the VFS rooted at /zoneinfo - FS.mkdir("zoneinfo"); - Module['FS_createPath']('/', 'zoneinfo', true, true); - Module['FS_createPath']('/zoneinfo', 'Indian', true, true); - Module['FS_createPath']('/zoneinfo', 'Atlantic', true, true); - Module['FS_createPath']('/zoneinfo', 'US', true, true); - Module['FS_createPath']('/zoneinfo', 'Brazil', true, true); - Module['FS_createPath']('/zoneinfo', 'Pacific', true, true); - Module['FS_createPath']('/zoneinfo', 'Arctic', true, true); - Module['FS_createPath']('/zoneinfo', 'America', true, true); - Module['FS_createPath']('/zoneinfo/America', 'Indiana', true, true); - Module['FS_createPath']('/zoneinfo/America', 'Argentina', true, true); - Module['FS_createPath']('/zoneinfo/America', 'Kentucky', true, true); - Module['FS_createPath']('/zoneinfo/America', 'North_Dakota', true, true); - Module['FS_createPath']('/zoneinfo', 'Australia', true, true); - Module['FS_createPath']('/zoneinfo', 'Etc', true, true); - Module['FS_createPath']('/zoneinfo', 'Asia', true, true); - Module['FS_createPath']('/zoneinfo', 'Antarctica', true, true); - Module['FS_createPath']('/zoneinfo', 'Europe', true, true); - Module['FS_createPath']('/zoneinfo', 'Mexico', true, true); - Module['FS_createPath']('/zoneinfo', 'Africa', true, true); - Module['FS_createPath']('/zoneinfo', 'Chile', true, true); - Module['FS_createPath']('/zoneinfo', 'Canada', true, true); - var zoneInfoData = read ('zoneinfo.data', 'binary'); - var metadata = JSON.parse(read ("mono-webassembly-zoneinfo-fs-smd.js.metadata", 'utf-8')); - var files = metadata.files; - for (var i = 0; i < files.length; ++i) { - var byteArray = zoneInfoData.subarray(files[i].start, files[i].end); - writeContentToFile(byteArray, files[i].filename); - } + // The timezone file is generated by https://github.com/dotnet/blazor/tree/master/src/TimeZoneData. + // The file format of the TZ file look like so + // + // [4-byte magic number] + // [4 - byte length of manifest] + // [json manifest] + // [data bytes] + // + // The json manifest is an array that looks like so: + // + // [...["America/Fort_Nelson",2249],["America/Glace_Bay",2206]..] + // + // where the first token in each array is the relative path of the file on disk, and the second is the + // length of the file. The starting offset of a file can be calculated using the lengths of all files + // that appear prior to it. + var zonedata = new Uint8Array(read ("dotnet.timezones.blat", "binary")); + MONO.mono_wasm_load_data (zonedata, "/usr/share/zoneinfo"); } + MONO.mono_load_runtime_and_bcl ( config.vfs_prefix, config.deploy_prefix, @@ -282,7 +270,7 @@ if (typeof window == "undefined") const IGNORE_PARAM_COUNT = -1; var App = { - init: function () { + init: function () { var assembly_load = Module.cwrap ('mono_wasm_assembly_load', 'number', ['string']) var find_class = Module.cwrap ('mono_wasm_assembly_find_class', 'number', ['number', 'string', 'string']) @@ -399,5 +387,5 @@ var App = { } else { fail_exec ("Unhanded argument: " + args [0]); } - }, + }, }; diff --git a/src/mono/wasm/runtime/dotnet.timezones.blat b/src/mono/wasm/runtime/dotnet.timezones.blat new file mode 100644 index 0000000000000..4302a00818494 Binary files /dev/null and b/src/mono/wasm/runtime/dotnet.timezones.blat differ diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 61e6234949d50..b288060dac9f5 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -779,6 +779,49 @@ var MonoSupportLib = { // and nested class names like Foo/Bar to Foo.Bar return className.replace(/\//g, '.').replace(/`\d+/g, ''); }, + + mono_wasm_load_data: function (data, prefix) { + var dataview = new DataView(data.buffer); + var magic = dataview.getUint32(0, true); + // get magic number + if (magic != 0x626c6174) { + throw new Error ("File is of wrong type"); + } + var manifestSize = dataview.getUint32(4, true); + var manifestContent = Module.UTF8ArrayToString(data, 8, manifestSize); + var manifest = JSON.parse(manifestContent); + data = data.slice(manifestSize+8); + + // Create the folder structure + // /usr/share/zoneinfo + // /usr/share/zoneinfo/Africa + // /usr/share/zoneinfo/Asia + // .. + var p = prefix.slice(1).split('/'); + p.forEach((v, i) => { + FS.mkdir(v); + Module['FS_createPath']("/" + p.slice(0, i).join('/'), v, true, true); + }) + var folders = new Set() + manifest.filter(m => { + m = m[0].split('/') + if (m!= null) { + if (m.length > 2) folders.add(m.slice(0,m.length-1).join('/')); + folders.add(m[0]); + } + }); + folders.forEach(folder => { + Module['FS_createPath'](prefix, folder, true, true); + }); + + for (row of manifest) { + var name = row[0]; + var length = row[1]; + var bytes = data.slice(0, length); + Module['FS_createDataFile'](`${prefix}/${name}`, null, bytes, true, true); + data = data.slice(length); + } + } }, mono_wasm_add_typed_value: function (type, str_value, value) { @@ -993,7 +1036,7 @@ var MonoSupportLib = { mono_wasm_fire_bp: function () { console.log ("mono_wasm_fire_bp"); debugger; - } + }, }; autoAddDeps(MonoSupportLib, '$MONO') diff --git a/tools-local/tasks/mobile.tasks/WasmAppBuilder/WasmAppBuilder.cs b/tools-local/tasks/mobile.tasks/WasmAppBuilder/WasmAppBuilder.cs index 31082da4e7f4d..0e38c7a85e13a 100644 --- a/tools-local/tasks/mobile.tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/tools-local/tasks/mobile.tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -71,7 +71,7 @@ public override bool Execute () Directory.CreateDirectory(Path.Join(AppDir, "managed")); foreach (var assembly in _assemblies!.Values) File.Copy(assembly.Location, Path.Join(AppDir, "managed", Path.GetFileName(assembly.Location)), true); - foreach (var f in new string[] { "dotnet.wasm", "dotnet.js" }) + foreach (var f in new string[] { "dotnet.wasm", "dotnet.js", "dotnet.timezones.blat" }) File.Copy(Path.Join (MicrosoftNetCoreAppRuntimePackDir, "native", f), Path.Join(AppDir, f), true); File.Copy(MainJS!, Path.Join(AppDir, "runtime.js"), true);