Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm] Build static components; include hot_reload in runtime #54568

Merged
merged 9 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
<DeltaScript>deltascript.json</DeltaScript>
<SkipTestUtilitiesReference>true</SkipTestUtilitiesReference>
</PropertyGroup>
<ItemGroup>
<Compile Include="MethodBody1.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 () =>
Expand Down
1 change: 0 additions & 1 deletion src/mono/mono.proj
Original file line number Diff line number Diff line change
Expand Up @@ -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"/>
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
$(MONO_BIN_DIR)/libmono-ilgen.a \
$(MONO_BIN_DIR)/libmono-icall-table.a \
$(MONO_BIN_DIR)/libmono-profiler-aot.a \
Expand Down
3 changes: 3 additions & 0 deletions src/mono/wasm/wasm.proj
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,16 @@
<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;
$(MonoArtifactsPath)libmono-icall-table.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>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<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>
Original file line number Diff line number Diff line change
@@ -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";
}
}
}
Original file line number Diff line number Diff line change
@@ -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";
}
}
}
Original file line number Diff line number Diff line change
@@ -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";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"changes": [
{"document": "MethodBody1.cs", "update": "MethodBody1_v1.cs"},
{"document": "MethodBody1.cs", "update": "MethodBody1_v2.cs"},
]
}

81 changes: 81 additions & 0 deletions src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs
Original file line number Diff line number Diff line change
@@ -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<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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
<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? -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

soon ;)

<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>
55 changes: 55 additions & 0 deletions src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!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>
47 changes: 47 additions & 0 deletions src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js
Original file line number Diff line number Diff line change
@@ -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);
}
},
};