<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
<DeltaScript>deltascript.json</DeltaScript>
+ <SkipTestUtilitiesReference>true</SkipTestUtilitiesReference>
</PropertyGroup>
<ItemGroup>
<Compile Include="MethodBody1.cs" />
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 () =>
<_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"/>
@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 \
<ItemGroup>
<ICULibNativeFiles Include="$(ICULibDir)/libicuuc.a;
$(ICULibDir)/libicui18n.a" />
+ <MonoComponentLibs Include="$(MonoArtifactsPath)libmono-component-hot_reload-static.a;
+ $(MonoArtifactsPath)libmono-component-diagnostics_tracing-stub-static.a" />
<MonoLibFiles Include="$(MonoArtifactsPath)libmono-ee-interp.a;
$(MonoArtifactsPath)libmonosgen-2.0.a;
$(MonoArtifactsPath)libmono-ilgen.a;
$(NativeBinDir)libSystem.Native.a;
$(NativeBinDir)libSystem.IO.Compression.Native.a" />
<MonoLibFiles Include="@(ICULibNativeFiles)" />
+ <MonoLibFiles Include="@(MonoComponentLibs)" />
<PInvokeTableFile Include="$(WasmObjDir)\pinvoke-table.h" />
<ICULibFiles Include="$(ICULibDir)/*.dat" />
</ItemGroup>
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk" TreatAsLocalProperty="EnableAggressiveTrimming;PublishTrimmed">
+ <PropertyGroup>
+ <TestRuntime>true</TestRuntime>
+ <DeltaScript>deltascript.json</DeltaScript>
+ <OutputType>library</OutputType>
+ <IsTestProject>false</IsTestProject>
+ <IsTestSupportProject>true</IsTestSupportProject>
+ <!-- to call AsssemblyExtensions.ApplyUpdate we need Optimize=false, EmitDebugInformation=true in all configurations -->
+ <Optimize>false</Optimize>
+ <EmitDebugInformation>true</EmitDebugInformation>
+ <!-- hot reload is not compatible with trimming -->
+ <EnableAggressiveTrimming>false</EnableAggressiveTrimming>
+ <PublishTrimmed>false</PublishTrimmed>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="MethodBody1.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <!-- This package from https://github.com/dotnet/hotreload-utils provides
+ targets that read the json delta script and generates deltas based on the baseline assembly and the modified sources.
+
+ Projects must define the DeltaScript property that specifies the (relative) path to the json script.
+ Deltas will be emitted next to the output assembly. Deltas will be copied when the current
+ project is referenced from other other projects.
+ -->
+ <PackageReference Include="Microsoft.DotNet.HotReload.Utils.Generator.BuildTool" Version="$(MicrosoftDotNetHotReloadUtilsGeneratorBuildToolVersion)" />
+ </ItemGroup>
+</Project>
--- /dev/null
+// 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";
+ }
+ }
+}
--- /dev/null
+// 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";
+ }
+ }
+}
--- /dev/null
+// 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";
+ }
+ }
+}
--- /dev/null
+{
+ "changes": [
+ {"document": "MethodBody1.cs", "update": "MethodBody1_v1.cs"},
+ {"document": "MethodBody1.cs", "update": "MethodBody1_v2.cs"},
+ ]
+}
+
--- /dev/null
+// 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<Type>());
+
+ 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, int> 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);
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <MonoForceInterpreter>true</MonoForceInterpreter>
+ <RunAOTCompilation>false</RunAOTCompilation>
+ <PublishTrimmed>false</PublishTrimmed>
+ <TestRuntime>true</TestRuntime>
+ <Scenario>WasmTestOnBrowser</Scenario>
+ <ExpectedExitCode>42</ExpectedExitCode>
+ <WasmMainJSPath>runtime.js</WasmMainJSPath>
+ <EnableDefaultItems>false</EnableDefaultItems>
+ <!-- setting WasmXHarnessMonoArgs doesn't work here, but see runtime.js -->
+ <!-- <WasmXHarnessMonoArgs>- -setenv=DOTNET_MODIFIABLE_ASSEMBLIES=debug</WasmXHarnessMonoArgs> -->
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Content Include="index.html">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ <ProjectReference Include="ApplyUpdateReferencedAssembly\ApplyUpdateReferencedAssembly.csproj" />
+ </ItemGroup>
+
+ <Target Name="AfterWasmBuildApp" AfterTargets="WasmBuildApp">
+ <Copy SourceFiles="$(OutDir)\index.html" DestinationFolder="$(WasmAppDir)" />
+ </Target>
+
+ <Target Name="PreserveEnCAssembliesFromLinking"
+ Condition="'$(TargetOS)' == 'Browser' and '$(EnableAggressiveTrimming)' == 'true'"
+ BeforeTargets="ConfigureTrimming">
+ <ItemGroup>
+ <!-- Don't modify EnC test assemblies -->
+ <TrimmerRootAssembly
+ Condition="$([System.String]::Copy('%(ResolvedFileToPublish.FileName)%(ResolvedFileToPublish.Extension)').EndsWith('ApplyUpdateReferencedAssembly.dll'))"
+ Include="%(ResolvedFileToPublish.FullPath)" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="IncludeDeltasInWasmBundle"
+ BeforeTargets="PrepareForWasmBuildApp"
+ Condition="'$(TargetOS)' == 'Browser'">
+ <ItemGroup>
+ <!-- FIXME: this belongs in eng/testing/tests.wasm.targets -->
+ <!-- FIXME: Can we do something on the Content items in the referenced projects themselves to get this for free? -->
+ <WasmFilesToIncludeInFileSystem Include="@(PublishItemsOutputGroupOutputs)"
+ Condition="$([System.String]::new('%(PublishItemsOutputGroupOutputs.Identity)').EndsWith('.dmeta'))" />
+ <WasmFilesToIncludeInFileSystem Include="@(PublishItemsOutputGroupOutputs)"
+ Condition="$([System.String]::new('%(PublishItemsOutputGroupOutputs.Identity)').EndsWith('.dil'))" />
+ <WasmFilesToIncludeInFileSystem Include="@(PublishItemsOutputGroupOutputs)"
+ Condition="$([System.String]::new('%(PublishItemsOutputGroupOutputs.Identity)').EndsWith('.dpdb'))" />
+ </ItemGroup>
+ </Target>
+
+</Project>
--- /dev/null
+<!DOCTYPE html>
+<!-- Licensed to the .NET Foundation under one or more agreements. -->
+<!-- The .NET Foundation licenses this file to you under the MIT license. -->
+<html>
+ <head>
+ <title>TESTS</title>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ </head>
+ <body onload="onLoad()">
+ <h3 id="header">Wasm Browser Sample</h3>
+ Result from Sample.Test.TestMeaning: <span id="out"></span>
+ <script type='text/javascript'>
+ var is_testing = false;
+ var onLoad = function() {
+ var url = new URL(decodeURI(window.location));
+ let args = url.searchParams.getAll('arg');
+ is_testing = args !== undefined && (args.find(arg => arg == '--testing') !== undefined);
+ };
+
+ var test_exit = function(exit_code)
+ {
+ if (!is_testing) {
+ console.log(`test_exit: ${exit_code}`);
+ return;
+ }
+
+ /* Set result in a tests_done element, to be read by xharness */
+ var tests_done_elem = document.createElement("label");
+ tests_done_elem.id = "tests_done";
+ tests_done_elem.innerHTML = exit_code.toString();
+ document.body.appendChild(tests_done_elem);
+
+ console.log(`WASM EXIT ${exit_code}`);
+ };
+
+ var App = {
+ init: function () {
+ var exit_code = BINDING.call_static_method("[WebAssembly.Browser.HotReload.Test] Sample.Test:TestMeaning", []);
+ document.getElementById("out").innerHTML = exit_code;
+
+ if (is_testing)
+ {
+ console.debug(`exit_code: ${exit_code}`);
+ test_exit(exit_code);
+ }
+ },
+ };
+ </script>
+ <script type="text/javascript" src="runtime.js"></script>
+
+ <script defer src="dotnet.js"></script>
+
+ </body>
+</html>
--- /dev/null
+// 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);
+ }
+ },
+};