diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj
index f05397e884c83a..57ba4f3ec52983 100644
--- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj
@@ -4,6 +4,7 @@
$(NetCoreAppCurrent)
true
deltascript.json
+ true
diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
index e533377e673ac2..2a953d19eece86 100644
--- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
@@ -18,6 +18,7 @@ namespace System.Reflection.Metadata
public class ApplyUpdateTest
{
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54617", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))]
void StaticMethodBodyUpdate()
{
ApplyUpdateUtil.TestCase(static () =>
diff --git a/src/mono/mono.proj b/src/mono/mono.proj
index e5d4bb2970aa33..e459f872177169 100644
--- a/src/mono/mono.proj
+++ b/src/mono/mono.proj
@@ -294,7 +294,6 @@
<_MonoCMakeArgs Include="-DENABLE_INTERP_LIB=1"/>
<_MonoCMakeArgs Include="-DDISABLE_ICALL_TABLES=1"/>
<_MonoCMakeArgs Include="-DDISABLE_CRASH_REPORTING=1"/>
- <_MonoCMakeArgs Include="-DDISABLE_COMPONENTS=1"/>
<_MonoCMakeArgs Include="-DENABLE_ICALL_EXPORT=1"/>
<_MonoCMakeArgs Include="-DENABLE_LAZY_GC_THREAD_CREATION=1"/>
<_MonoCMakeArgs Include="-DENABLE_LLVM_RUNTIME=1"/>
diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile
index 9f6d055511193c..86288cd674e703 100644
--- a/src/mono/wasm/Makefile
+++ b/src/mono/wasm/Makefile
@@ -48,12 +48,20 @@ provision-wasm: .stamp-wasm-install-and-select-$(EMSCRIPTEN_VERSION)
@echo "----------------------------------------------------------"
@echo "Installed emsdk into EMSDK_PATH=$(TOP)/src/mono/wasm/emsdk"
+# FIXME: When https://github.com/dotnet/runtime/issues/54565 is fixed, and the WasmApp targets are updated to use mono runtime components, remove this
+MONO_COMPONENT_LIBS= \
+ $(MONO_BIN_DIR)/libmono-component-hot_reload-static.a \
+ $(MONO_BIN_DIR)/libmono-component-diagnostics_tracing-stub-static.a
+
MONO_OBJ_DIR=$(OBJDIR)/mono/Browser.wasm.$(CONFIG)
MONO_INCLUDE_DIR=$(MONO_BIN_DIR)/include/mono-2.0
BUILDS_OBJ_DIR=$(MONO_OBJ_DIR)/wasm
+# libmonosgen-2.0 is in MONO_LIBS twice because the components and the runtime are depend on each other
MONO_LIBS = \
$(MONO_BIN_DIR)/libmono-ee-interp.a \
$(MONO_BIN_DIR)/libmonosgen-2.0.a \
+ $(MONO_COMPONENT_LIBS) \
+ $(MONO_BIN_DIR)/libmonosgen-2.0.a \
$(MONO_BIN_DIR)/libmono-ilgen.a \
$(MONO_BIN_DIR)/libmono-icall-table.a \
$(MONO_BIN_DIR)/libmono-profiler-aot.a \
diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj
index 489d88259f5f3e..ffc2fb92825ba5 100644
--- a/src/mono/wasm/wasm.proj
+++ b/src/mono/wasm/wasm.proj
@@ -189,6 +189,8 @@
+
+
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj
new file mode 100644
index 00000000000000..46e260f24e155f
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj
@@ -0,0 +1,30 @@
+
+
+ true
+ deltascript.json
+ library
+ false
+ true
+
+ false
+ true
+
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs
new file mode 100644
index 00000000000000..9e98604b921d21
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace ApplyUpdateReferencedAssembly
+{
+ public class MethodBody1 {
+ public static string StaticMethod1 () {
+ return "OLD STRING";
+ }
+ }
+}
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs
new file mode 100644
index 00000000000000..4aab1e81dade09
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace ApplyUpdateReferencedAssembly
+{
+ public class MethodBody1 {
+ public static string StaticMethod1 () {
+ return "NEW STRING";
+ }
+ }
+}
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs
new file mode 100644
index 00000000000000..83f0142e55f892
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace ApplyUpdateReferencedAssembly
+{
+ public class MethodBody1 {
+ public static string StaticMethod1 () {
+ return "NEWEST STRING";
+ }
+ }
+}
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json
new file mode 100644
index 00000000000000..8e738364bc7475
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/ApplyUpdateReferencedAssembly/deltascript.json
@@ -0,0 +1,7 @@
+{
+ "changes": [
+ {"document": "MethodBody1.cs", "update": "MethodBody1_v1.cs"},
+ {"document": "MethodBody1.cs", "update": "MethodBody1_v2.cs"},
+ ]
+}
+
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs
new file mode 100644
index 00000000000000..cb005b090887d5
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Sample
+{
+ public class Test
+ {
+ public static void Main(string[] args)
+ {
+ Console.WriteLine ("Hello, World!");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static int TestMeaning()
+ {
+ const int success = 42;
+ const int failure = 1;
+
+ var ty = typeof(System.Reflection.Metadata.AssemblyExtensions);
+ var mi = ty.GetMethod("GetApplyUpdateCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Array.Empty());
+
+ if (mi == null)
+ return failure;
+
+ var caps = mi.Invoke(null, null) as string;
+
+ if (String.IsNullOrEmpty(caps))
+ return failure;
+
+ var assm = typeof (ApplyUpdateReferencedAssembly.MethodBody1).Assembly;
+
+ var r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1();
+ if ("OLD STRING" != r)
+ return failure;
+
+ ApplyUpdate(assm);
+
+ r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1();
+ if ("NEW STRING" != r)
+ return failure;
+
+ ApplyUpdate(assm);
+
+ r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1();
+ if ("NEWEST STRING" != r)
+ return failure;
+
+ return success;
+ }
+
+ private static System.Collections.Generic.Dictionary assembly_count = new();
+
+ internal static void ApplyUpdate (System.Reflection.Assembly assm)
+ {
+ int count;
+ if (!assembly_count.TryGetValue(assm, out count))
+ count = 1;
+ else
+ count++;
+ assembly_count [assm] = count;
+
+ /* FIXME WASM: Location is empty on wasm. Make up a name based on Name */
+ string basename = assm.Location;
+ if (basename == "")
+ basename = assm.GetName().Name + ".dll";
+ Console.Error.WriteLine($"Apply Delta Update for {basename}, revision {count}");
+
+ string dmeta_name = $"{basename}.{count}.dmeta";
+ string dil_name = $"{basename}.{count}.dil";
+ byte[] dmeta_data = System.IO.File.ReadAllBytes(dmeta_name);
+ byte[] dil_data = System.IO.File.ReadAllBytes(dil_name);
+ byte[] dpdb_data = null; // TODO also use the dpdb data
+
+ System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assm, dmeta_data, dil_data, dpdb_data);
+ }
+ }
+}
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj
new file mode 100644
index 00000000000000..2ba05527c2a42d
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj
@@ -0,0 +1,52 @@
+
+
+ true
+ false
+ false
+ true
+ WasmTestOnBrowser
+ 42
+ runtime.js
+ false
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html
new file mode 100644
index 00000000000000..ad7cc2164ba485
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+ TESTS
+
+
+
+
+
+ Result from Sample.Test.TestMeaning:
+
+
+
+
+
+
+
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js
new file mode 100644
index 00000000000000..4859991a626f4a
--- /dev/null
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+var Module = {
+
+ config: null,
+
+ preInit: async function() {
+ Module.config = await MONO.mono_wasm_load_config("./mono-config.json");
+ },
+
+ onRuntimeInitialized: function () {
+ if (!Module.config || Module.config.error) {
+ console.log("No config found");
+ test_exit(1);
+ throw(Module.config.error);
+ }
+
+ Module.config.loaded_cb = function () {
+ try {
+ App.init ();
+ } catch (error) {
+ test_exit(1);
+ throw (error);
+ }
+ };
+ Module.config.fetch_file_cb = function (asset) {
+ return fetch (asset, { credentials: 'same-origin' });
+ }
+
+ if (Module.config.environment_variables !== undefined) {
+ console.log ("expected environment variables to be undefined, but they're: ", Module.config.environment_variables);
+ test_exit(1);
+ }
+ Module.config.environment_variables = {
+ "DOTNET_MODIFIABLE_ASSEMBLIES": "debug"
+ };
+
+ try
+ {
+ MONO.mono_load_runtime_and_bcl_args (Module.config);
+ } catch (error) {
+ test_exit(1);
+ throw(error);
+ }
+ },
+};