();
+
+ public void Update (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.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);
+
+ LoadMetadataUpdate (assm, dmeta_data, dil_data);
+ }
+ }
+}
diff --git a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj
new file mode 100644
index 0000000000000..47bb08e4c66e9
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj
@@ -0,0 +1,6 @@
+
+
+ $(NetCoreAppToolCurrent)
+ false
+
+
diff --git a/src/mono/netcore/sample/mbr/README.md b/src/mono/netcore/sample/mbr/README.md
new file mode 100644
index 0000000000000..c4f860327be7e
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/README.md
@@ -0,0 +1,62 @@
+
+# Mono Method Body Replacement Samples
+
+## Prerequisites
+
+To run the tests you will need to build a runtime with the Mono metadata update
+property set, and you will need the `roslynildiff` commandline tool.
+
+At this time there is only support for Mac and Linux. No Windows.
+
+## Building
+
+Build the runtime with the `/p:MonoMetadataUpdate=true` option.
+
+Both Debug and Release configurations should work, although mixing a Release
+configuration with a Debug runtime configuration (or vice versa) is not
+supported.
+
+For desktop:
+
+```console
+build.sh -s Mono+Libs /p:MonoMetadataUpdate=true
+```
+
+
+For Wasm:
+
+Make sure `EMSDK_PATH` is set (see [workflow](../../../../../docs/workflow/building/libraries/webassembly-instructions.md))
+```console
+build.sh --os browser /p:MonoMetadataUpdate=true
+```
+
+## Running
+
+Edit [browser/WasmDelta.csproj](./browser/WasmDelta.csproj) or [console/ConsoleDelta.csproj](./console/ConsoleDelta.csproj) and set the `RoslynILDiffFullPath` property to the path to the `roslynildiff` shell script:
+
+```xml
+
+
+ /home/user/tools/roslyn-ildiff/roslynildiff
+
+```
+
+(Make sure the configuration is the same as the runtime that you built)
+
+For console:
+
+```
+make MONO_CONFIG=Debug publish && make MONO_CONFIG=Debug run
+```
+
+The output from `run` should print an old string, apply an update and then print a new string
+
+For wasm:
+
+```
+make CONFIG=Debug && make CONFIG=Debug run
+```
+
+Then go to http://localhost:8000/ and click the button once or twice (the example has 2 updates prebuilt)
+
+
diff --git a/src/mono/netcore/sample/mbr/browser/Makefile b/src/mono/netcore/sample/mbr/browser/Makefile
new file mode 100644
index 0000000000000..e4c115d70a786
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/browser/Makefile
@@ -0,0 +1,27 @@
+TOP=../../../../../..
+DOTNET=$(TOP)/dotnet.sh
+
+ifeq ($(V),)
+DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary
+else
+DOTNET_Q_ARGS=--nologo
+endif
+
+CONFIG?=Release
+
+all: build
+
+build:
+ $(DOTNET) build $(DOTNET_Q_ARGS) /p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG) WasmDelta.csproj
+
+clean:
+ rm -rf bin
+
+run:
+ if ! $(DOTNET) tool list --global | grep dotnet-serve; then \
+ echo "The tool dotnet-serve could not be found. Install with: $(DOTNET) tool install --global dotnet-serve"; \
+ exit 1; \
+ else \
+ $(DOTNET) serve -d bin/$(CONFIG)/publish -p 8000; \
+ fi
+
diff --git a/src/mono/netcore/sample/mbr/browser/Program.cs b/src/mono/netcore/sample/mbr/browser/Program.cs
new file mode 100644
index 0000000000000..9174760b66f0b
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/browser/Program.cs
@@ -0,0 +1,32 @@
+// 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;
+using MonoDelta;
+
+namespace Sample
+{
+ public class Test
+ {
+ static DeltaHelper replacer = DeltaHelper.Make ();
+
+ public static void Main(string[] args)
+ {
+ Console.WriteLine ("Hello, World!");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static int TestMeaning()
+ {
+ return 42;
+ }
+
+ public static void Update()
+ {
+ Assembly assm = typeof (Test).Assembly;
+ replacer.Update (assm);
+ }
+ }
+}
diff --git a/src/mono/netcore/sample/mbr/browser/Program_v1.cs b/src/mono/netcore/sample/mbr/browser/Program_v1.cs
new file mode 100644
index 0000000000000..02974e4697bfc
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/browser/Program_v1.cs
@@ -0,0 +1,32 @@
+// 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;
+using MonoDelta;
+
+namespace Sample
+{
+ public class Test
+ {
+ static DeltaHelper replacer = DeltaHelper.Make ();
+
+ public static void Main(string[] args)
+ {
+ Console.WriteLine ("Hello, World!");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static int TestMeaning()
+ {
+ return 43;
+ }
+
+ public static void Update()
+ {
+ Assembly assm = typeof (Test).Assembly;
+ replacer.Update (assm);
+ }
+ }
+}
diff --git a/src/mono/netcore/sample/mbr/browser/Program_v2.cs b/src/mono/netcore/sample/mbr/browser/Program_v2.cs
new file mode 100644
index 0000000000000..98eec23781796
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/browser/Program_v2.cs
@@ -0,0 +1,32 @@
+// 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;
+using MonoDelta;
+
+namespace Sample
+{
+ public class Test
+ {
+ static DeltaHelper replacer = DeltaHelper.Make ();
+
+ public static void Main(string[] args)
+ {
+ Console.WriteLine ("Hello, World!");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static int TestMeaning()
+ {
+ return 128;
+ }
+
+ public static void Update()
+ {
+ Assembly assm = typeof (Test).Assembly;
+ replacer.Update (assm);
+ }
+ }
+}
diff --git a/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj b/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj
new file mode 100644
index 0000000000000..7440308f883ed
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj
@@ -0,0 +1,65 @@
+
+
+ Exe
+ bin
+ false
+ $(NetCoreAppToolCurrent)
+ wasm
+ Browser
+ $(ArtifactsBinDir)microsoft.netcore.app.runtime.browser-wasm\$(Configuration)\runtimes\browser-wasm\
+ $(MSBuildThisFileDirectory)obj\$(Configuration)\wasm
+ $(MSBuildThisFileDirectory)bin\$(Configuration)\publish
+
+
+
+
+
+
+
+
+
+ $(AppDir)
+ bin\WasmDelta.dll
+ runtime.js
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \%(_DeltaFileForPublish.Filename)%(_DeltaFileForPublish.Extension)
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
+ Program_v1.cs;Program_v2.cs
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mono/netcore/sample/mbr/browser/index.html b/src/mono/netcore/sample/mbr/browser/index.html
new file mode 100644
index 0000000000000..472cf5f29ea05
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/browser/index.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+ TESTS
+
+
+
+
+
+ Result from Sample.Test.TestMeaning:
+
+ Click here (upto 2 times):
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mono/netcore/sample/mbr/browser/runtime.js b/src/mono/netcore/sample/mbr/browser/runtime.js
new file mode 100644
index 0000000000000..7db3cb4fc70e7
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/browser/runtime.js
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+var Module = {
+ onRuntimeInitialized: function () {
+ config.loaded_cb = function () {
+ App.init ();
+ };
+ config.fetch_file_cb = function (asset) {
+ return fetch (asset, { credentials: 'same-origin' });
+ }
+
+ MONO.mono_load_runtime_and_bcl_args (config);
+ },
+};
diff --git a/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj b/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj
new file mode 100644
index 0000000000000..d807eff508bdc
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj
@@ -0,0 +1,39 @@
+
+
+ Exe
+ $(NetCoreAppToolCurrent)
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TestClass_v1.cs
+
+
+
+
+
+
+
+ $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mono/netcore/sample/mbr/console/Makefile b/src/mono/netcore/sample/mbr/console/Makefile
new file mode 100644
index 0000000000000..359522a516392
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/console/Makefile
@@ -0,0 +1,26 @@
+TOP=../../../../../../
+DOTNET:=$(TOP)./dotnet.sh
+DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary
+
+CONFIG ?=Release
+MONO_ARCH=x64
+
+OS := $(shell uname -s)
+ifeq ($(OS),Darwin)
+ TARGET_OS=osx
+else
+ TARGET_OS=linux
+endif
+
+MONO_ENV_OPTIONS ?=--interp=-inline
+
+publish:
+ $(DOTNET) publish -c $(CONFIG) -r $(TARGET_OS)-$(MONO_ARCH)
+
+run: publish
+ COMPlus_DebugWriteToStdErr=1 \
+ MONO_ENV_OPTIONS="$(MONO_ENV_OPTIONS)" \
+ $(TOP)artifacts/bin/ConsoleDelta/$(MONO_ARCH)/$(CONFIG)/$(TARGET_OS)-$(MONO_ARCH)/publish/ConsoleDelta
+
+clean:
+ rm -rf $(TOP)artifacts/bin/HelloWorld/
diff --git a/src/mono/netcore/sample/mbr/console/Program.cs b/src/mono/netcore/sample/mbr/console/Program.cs
new file mode 100644
index 0000000000000..01fba0d8e4ee1
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/console/Program.cs
@@ -0,0 +1,117 @@
+// 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;
+using System.Threading;
+using MonoDelta;
+
+namespace HelloWorld
+{
+ internal class Program
+ {
+ class State {
+ public readonly ManualResetEventSlim mreIn;
+ public readonly ManualResetEventSlim mreOut;
+ public readonly ManualResetEventSlim mreBusy;
+ public string res;
+ private volatile bool busyChanged;
+
+ public State() {
+ mreIn = new ManualResetEventSlim ();
+ mreOut = new ManualResetEventSlim ();
+ mreBusy = new ManualResetEventSlim ();
+ res = "";
+ busyChanged = false;
+ }
+
+
+ public bool BusyChanged {get => busyChanged ; set { busyChanged = value; mreBusy.Set ();} }
+
+ public void WaitForBusy () {
+ mreBusy.Wait ();
+ mreBusy.Reset ();
+ }
+
+ public string ConsumerStep () {
+ mreIn.Set ();
+ mreOut.Wait ();
+ mreOut.Reset ();
+ return res;
+ }
+
+ public void ProducerStep (Func step) {
+ mreIn.Wait ();
+ mreIn.Reset ();
+ res = step ();
+ mreOut.Set ();
+ }
+ }
+
+ private static int Main(string[] args)
+ {
+ bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null;
+ Console.WriteLine($"Hello World {(isMono ? "from Mono!" : "from CoreCLR!")}");
+ Console.WriteLine(typeof(object).Assembly.FullName);
+ Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly ());
+ Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
+
+ Assembly assm = typeof (TestClass).Assembly;
+ var replacer = DeltaHelper.Make ();
+
+ var st = new State ();
+ var t = new Thread (MutatorThread);
+ t.Start (st);
+ var t2 = new Thread (BusyThread) { IsBackground = true };
+ t2.Start (st);
+
+ string res = st.ConsumerStep ();
+ if (res != "OLD STRING")
+ return 1;
+
+ replacer.Update (assm);
+
+ res = st.ConsumerStep ();
+ if (res != "NEW STRING")
+ return 2;
+
+ st.WaitForBusy ();
+ Console.WriteLine ("BusyChanged: {0}", st.BusyChanged);
+
+ return 0;
+ }
+
+ private static void MutatorThread (object o)
+ {
+ var st = (State)o;
+ static string Step () => TestClass.TargetMethod ();
+ st.ProducerStep (Step);
+ st.ProducerStep (Step);
+ }
+
+ // This method is not affected by the update, but it calls the target
+ // method which is. Still we expect to see "BusyThread" and its
+ // callees show up in the trace output when it safepoints during an
+ // update.
+ private static void BusyThread (object o)
+ {
+ State st = (State)o;
+ string prev = TestClass.TargetMethod ();
+ while (true) {
+ Thread.Sleep (0);
+ for (int i = 0; i < 5000; ++i) {
+ if (i % 1000 == 0) {
+ string cur = TestClass.TargetMethod ();
+ if (cur != prev) {
+ st.BusyChanged = true;
+ prev = cur;
+ }
+ }
+ }
+ }
+ }
+
+ }
+}
+
diff --git a/src/mono/netcore/sample/mbr/console/TestClass.cs b/src/mono/netcore/sample/mbr/console/TestClass.cs
new file mode 100644
index 0000000000000..c18db33b47257
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/console/TestClass.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Runtime.CompilerServices;
+
+public class TestClass {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static string TargetMethod () {
+ string s = "OLD STRING";
+ Console.WriteLine (s);
+ return s;
+ }
+}
diff --git a/src/mono/netcore/sample/mbr/console/TestClass_v1.cs b/src/mono/netcore/sample/mbr/console/TestClass_v1.cs
new file mode 100644
index 0000000000000..a41793c3f7dcc
--- /dev/null
+++ b/src/mono/netcore/sample/mbr/console/TestClass_v1.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Runtime.CompilerServices;
+
+public class TestClass {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static string TargetMethod () {
+ string s = "NEW STRING";
+ Console.WriteLine (s);
+ return s;
+ }
+}
diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile
index a1f1752d1f1e1..f07aedab6d27f 100644
--- a/src/mono/wasm/Makefile
+++ b/src/mono/wasm/Makefile
@@ -21,6 +21,7 @@ ICU_LIBDIR?=
SYSTEM_NATIVE_LIBDIR?=$(TOP)/src/libraries/Native/Unix/System.Native
ENABLE_ES6?=false
_MSBUILD_WASM_BUILD_ARGS=/p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=$(CONFIG)
+ENABLE_METADATA_UPDATE?=false
all: build-native icu-files source-files header-files
@@ -63,6 +64,9 @@ EMCC_FLAGS=--profiling-funcs -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=1 -s US
ifneq ($(ENABLE_ES6),false)
EMCC_FLAGS+=-s MODULARIZE=1 -s EXPORT_ES6=1
endif
+ifeq ($(ENABLE_METADATA_UPDATE),true)
+ EMCC_FLAGS+=-DENABLE_METADATA_UPDATE=1
+endif
EMCC_DEBUG_FLAGS =-g -Os -s ASSERTIONS=1 -DENABLE_NETCORE=1 -DDEBUG=1
EMCC_RELEASE_FLAGS=-Oz --llvm-opts 2 -DENABLE_NETCORE=1
diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c
index 1e66fcd913aa3..ab7895f0eb3be 100644
--- a/src/mono/wasm/runtime/driver.c
+++ b/src/mono/wasm/runtime/driver.c
@@ -466,6 +466,9 @@ EMSCRIPTEN_KEEPALIVE void
mono_wasm_load_runtime (const char *unused, int debug_level)
{
const char *interp_opts = "";
+#ifdef ENABLE_METADATA_UPDATE
+ interp_opts = "-inline"; /* FIXME: EnC hack - make this configurable */
+#endif
#ifdef DEBUG
monoeg_g_setenv ("MONO_LOG_LEVEL", "debug", 0);
diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj
index 1993f1d252f08..092c10cbf17ec 100644
--- a/src/mono/wasm/wasm.proj
+++ b/src/mono/wasm/wasm.proj
@@ -13,6 +13,7 @@
$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native'))
false
false
+ true