diff --git a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj
index c7a311e87358ff..11eac88f6dca6b 100644
--- a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj
+++ b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj
@@ -3,6 +3,7 @@
true
true
+ true
-s USE_CLOSURE_COMPILER=1 -s LEGACY_GL_EMULATION=1 -lGL -lSDL -lidbfs.js
diff --git a/src/mono/sample/wasm/browser-advanced/index.html b/src/mono/sample/wasm/browser-advanced/index.html
index b5c8649a6d7e45..b4cefe75f40724 100644
--- a/src/mono/sample/wasm/browser-advanced/index.html
+++ b/src/mono/sample/wasm/browser-advanced/index.html
@@ -12,7 +12,7 @@
-
+
diff --git a/src/mono/wasm/runtime/assets.ts b/src/mono/wasm/runtime/assets.ts
index 38544d0d94894c..30b43d61c84124 100644
--- a/src/mono/wasm/runtime/assets.ts
+++ b/src/mono/wasm/runtime/assets.ts
@@ -74,7 +74,7 @@ export function get_preferred_icu_asset(): string | null {
return OTHERS;
}
-export function shouldLoadIcuAsset(asset : AssetEntryInternal, preferredIcuAsset: string | null) : boolean{
+export function shouldLoadIcuAsset(asset: AssetEntryInternal, preferredIcuAsset: string | null): boolean {
return !(asset.behavior == "icu" && asset.name != preferredIcuAsset);
}
@@ -293,6 +293,14 @@ async function start_asset_download_sources(asset: AssetEntryInternal): Promise<
return response;
}
catch (err) {
+ if (!response) {
+ response = {
+ ok: false,
+ url: attemptUrl,
+ status: 0,
+ statusText: "" + err,
+ } as any;
+ }
continue; //next source
}
}
diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts
index bcfce611388fee..d6e84c710f1c41 100644
--- a/src/mono/wasm/runtime/dotnet.d.ts
+++ b/src/mono/wasm/runtime/dotnet.d.ts
@@ -132,6 +132,10 @@ type MonoConfig = {
* initial number of workers to add to the emscripten pthread pool
*/
pthreadPoolSize?: number;
+ /**
+ * hash of assets
+ */
+ assetsHash?: string;
};
interface ResourceRequest {
name: string;
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index 1de2fc6998912b..5896e9736f9242 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -119,6 +119,10 @@ export type MonoConfig = {
* initial number of workers to add to the emscripten pthread pool
*/
pthreadPoolSize?: number,
+ /**
+ * hash of assets
+ */
+ assetsHash?: string,
};
export type MonoConfigInternal = MonoConfig & {
diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj
index 3dd38caa3c8809..69c1877a575cfc 100644
--- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj
+++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj
@@ -5,7 +5,7 @@
true
false
enable
- $(NoWarn),CA1050
+ $(NoWarn),CA1050,CA1850
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj
index 28e5cc265c80aa..e76730b5aeca07 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj
@@ -5,7 +5,7 @@
true
false
enable
- $(NoWarn),CA1050
+ $(NoWarn),CA1050,CA1850
$(NoWarn),CS8604,CS8602
diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj b/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj
index 75c898c560c0e3..61f3cc89e3c61b 100644
--- a/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj
+++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.csproj
@@ -5,7 +5,7 @@
true
false
enable
- $(NoWarn),CA1050
+ $(NoWarn),CA1050,CA1850
diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs
index 91e5b77c1e198e..17b915f330bae5 100644
--- a/src/tasks/Common/Utils.cs
+++ b/src/tasks/Common/Utils.cs
@@ -3,9 +3,12 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
+using System.Reflection.PortableExecutable;
+using System.Reflection.Metadata;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Build.Framework;
@@ -237,6 +240,32 @@ public static string ComputeHash(string filepath)
return Convert.ToBase64String(hash);
}
+ public static string ComputeIntegrity(string filepath)
+ {
+ using var stream = File.OpenRead(filepath);
+ using HashAlgorithm hashAlgorithm = SHA256.Create();
+
+ byte[] hash = hashAlgorithm.ComputeHash(stream);
+ return "sha256-" + Convert.ToBase64String(hash);
+ }
+
+ public static string ComputeIntegrity(byte[] bytes)
+ {
+ using HashAlgorithm hashAlgorithm = SHA256.Create();
+
+ byte[] hash = hashAlgorithm.ComputeHash(bytes);
+ return "sha256-" + Convert.ToBase64String(hash);
+ }
+
+ public static string ComputeTextIntegrity(string str)
+ {
+ using HashAlgorithm hashAlgorithm = SHA256.Create();
+
+ var bytes = Encoding.UTF8.GetBytes(str);
+ byte[] hash = hashAlgorithm.ComputeHash(bytes);
+ return "sha256-" + Convert.ToBase64String(hash);
+ }
+
#if NETCOREAPP
public static void DirectoryCopy(string sourceDir, string destDir, Func? predicate=null)
{
@@ -258,4 +287,44 @@ public static void DirectoryCopy(string sourceDir, string destDir, Func$(TargetFrameworkForNETCoreTasks);$(TargetFrameworkForNETFrameworkTasks)
false
enable
- $(NoWarn),CA1050
+ $(NoWarn),CA1050,CA1850
diff --git a/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj b/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj
index d17dc95c41af62..a717c45f42c5ae 100644
--- a/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj
+++ b/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj
@@ -5,7 +5,7 @@
true
false
enable
- $(NoWarn),CA1050
+ $(NoWarn),CA1050,CA1850
diff --git a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs
index 081803b4b3c994..74855a41daf85d 100644
--- a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs
+++ b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs
@@ -6,7 +6,6 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
-using System.Reflection.PortableExecutable;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
@@ -128,7 +127,7 @@ private List FilterOutUnmanagedBinaries(string[] assemblies)
try
{
- if (!IsManagedAssembly(asmPath))
+ if (!Utils.IsManagedAssembly(asmPath))
{
Log.LogMessage(MessageImportance.Low, $"Skipping unmanaged {asmPath}.");
continue;
@@ -145,15 +144,4 @@ private List FilterOutUnmanagedBinaries(string[] assemblies)
return managedAssemblies;
}
-
- private static bool IsManagedAssembly(string filePath)
- {
- if (!File.Exists(filePath))
- return false;
-
- using FileStream fileStream = File.OpenRead(filePath);
- using PEReader reader = new(fileStream, PEStreamOptions.Default);
- return reader.HasMetadata;
- }
-
}
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
index 95a065dbdf177f..b7441396754fe1 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
@@ -7,6 +7,7 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
@@ -51,46 +52,49 @@ private sealed class WasmAppConfig
public List RemoteSources { get; set; } = new List();
[JsonExtensionData]
public Dictionary Extra { get; set; } = new();
+ [JsonPropertyName("assetsHash")]
+ public string AssetsHash { get; set; } = "none";
}
private class AssetEntry
{
- protected AssetEntry (string name, string behavior)
+ protected AssetEntry (string name, string hash, string behavior)
{
Name = name;
Behavior = behavior;
+ Hash = hash;
}
[JsonPropertyName("behavior")]
public string Behavior { get; init; }
[JsonPropertyName("name")]
public string Name { get; init; }
- // TODO [JsonPropertyName("hash")]
- // TODO public string? Hash { get; set; }
+ [JsonPropertyName("hash")]
+ public string? Hash { get; set; }
}
private sealed class WasmEntry : AssetEntry
{
- public WasmEntry(string name) : base(name, "dotnetwasm") { }
+ public WasmEntry(string name, string hash) : base(name, hash, "dotnetwasm") { }
}
private sealed class ThreadsWorkerEntry : AssetEntry
{
- public ThreadsWorkerEntry(string name) : base(name, "js-module-threads") { }
+ public ThreadsWorkerEntry(string name, string hash) : base(name, hash, "js-module-threads") { }
}
private sealed class AssemblyEntry : AssetEntry
{
- public AssemblyEntry(string name) : base(name, "assembly") {}
+ public AssemblyEntry(string name, string hash) : base(name, hash, "assembly") {}
}
private sealed class PdbEntry : AssetEntry
{
- public PdbEntry(string name) : base(name, "pdb") {}
+ public PdbEntry(string name, string hash) : base(name, hash, "pdb") {}
}
private sealed class SatelliteAssemblyEntry : AssetEntry
{
- public SatelliteAssemblyEntry(string name, string culture) : base(name, "resource")
+ public SatelliteAssemblyEntry(string name, string hash, string culture) : base(name, hash, "resource")
{
CultureName = culture;
}
@@ -101,14 +105,14 @@ public SatelliteAssemblyEntry(string name, string culture) : base(name, "resourc
private sealed class VfsEntry : AssetEntry
{
- public VfsEntry(string name) : base(name, "vfs") {}
+ public VfsEntry(string name, string hash) : base(name, hash, "vfs") {}
[JsonPropertyName("virtualPath")]
public string? VirtualPath { get; set; }
}
private sealed class IcuData : AssetEntry
{
- public IcuData(string name) : base(name, "icu") {}
+ public IcuData(string name, string hash) : base(name, hash, "icu") {}
[JsonPropertyName("loadRemote")]
public bool LoadRemote { get; set; }
}
@@ -183,12 +187,20 @@ protected override bool ExecuteInternal()
foreach (ITaskItem item in NativeAssets)
{
- string dest = Path.Combine(AppDir!, Path.GetFileName(item.ItemSpec));
+ var name = Path.GetFileName(item.ItemSpec);
+ var dest = Path.Combine(AppDir!, name);
if (!FileCopyChecked(item.ItemSpec, dest, "NativeAssets"))
return false;
+ if (name == "dotnet.wasm")
+ {
+ config.Assets.Add(new WasmEntry (name, Utils.ComputeIntegrity(item.ItemSpec)) );
+ }
+ else if (IncludeThreadsWorker && name == "dotnet.worker.js")
+ {
+ config.Assets.Add(new ThreadsWorkerEntry (name, Utils.ComputeIntegrity(item.ItemSpec)));
+ }
}
-
string packageJsonPath = Path.Combine(AppDir, "package.json");
if (!File.Exists(packageJsonPath))
{
@@ -199,14 +211,29 @@ protected override bool ExecuteInternal()
foreach (var assembly in _assemblies)
{
string assemblyPath = assembly;
- if (UseWebcil)
- assemblyPath = Path.ChangeExtension(assemblyPath, ".webcil");
- config.Assets.Add(new AssemblyEntry(Path.GetFileName(assemblyPath)));
- if (DebugLevel != 0) {
- var pdb = assembly;
- pdb = Path.ChangeExtension(pdb, ".pdb");
- if (File.Exists(pdb))
- config.Assets.Add(new PdbEntry(Path.GetFileName(pdb)));
+ var bytes = File.ReadAllBytes(assemblyPath);
+ // for the is IL IsAssembly check we need to read the bytes from the original DLL
+ if (!Utils.IsManagedAssembly(bytes))
+ {
+ Log.LogMessage(MessageImportance.Low, "Skipping non-assembly file: " + assemblyPath);
+ }
+ else
+ {
+ if (UseWebcil)
+ {
+ assemblyPath = Path.Combine(asmRootPath, Path.ChangeExtension(Path.GetFileName(assembly), ".webcil"));
+ // For the hash, read the bytes from the webcil file, not the dll file.
+ bytes = File.ReadAllBytes(assemblyPath);
+ }
+
+ config.Assets.Add(new AssemblyEntry(Path.GetFileName(assemblyPath), Utils.ComputeIntegrity(bytes)));
+ if (DebugLevel != 0)
+ {
+ var pdb = assembly;
+ pdb = Path.ChangeExtension(pdb, ".pdb");
+ if (File.Exists(pdb))
+ config.Assets.Add(new PdbEntry(Path.GetFileName(pdb), Utils.ComputeIntegrity(pdb)));
+ }
}
}
@@ -228,12 +255,13 @@ protected override bool ExecuteInternal()
else
Log.LogMessage(MessageImportance.Low, $"Skipped generating {finalWebcil} as the contents are unchanged.");
_fileWrites.Add(finalWebcil);
- config.Assets.Add(new SatelliteAssemblyEntry(Path.GetFileName(finalWebcil), args.culture));
+ config.Assets.Add(new SatelliteAssemblyEntry(Path.GetFileName(finalWebcil), Utils.ComputeIntegrity(finalWebcil), args.culture));
}
else
{
- FileCopyChecked(args.fullPath, Path.Combine(directory, name), "SatelliteAssemblies");
- config.Assets.Add(new SatelliteAssemblyEntry(name, args.culture));
+ var satellitePath = Path.Combine(directory, name);
+ FileCopyChecked(args.fullPath, satellitePath, "SatelliteAssemblies");
+ config.Assets.Add(new SatelliteAssemblyEntry(name, Utils.ComputeIntegrity(satellitePath), args.culture));
}
});
@@ -271,10 +299,10 @@ protected override bool ExecuteInternal()
targetPathTable[targetPath] = item.ItemSpec;
var generatedFileName = $"{i++}_{Path.GetFileName(item.ItemSpec)}";
+ var vfsPath = Path.Combine(supportFilesDir, generatedFileName);
+ FileCopyChecked(item.ItemSpec, vfsPath, "FilesToIncludeInFileSystem");
- FileCopyChecked(item.ItemSpec, Path.Combine(supportFilesDir, generatedFileName), "FilesToIncludeInFileSystem");
-
- var asset = new VfsEntry ($"supportFiles/{generatedFileName}") {
+ var asset = new VfsEntry ($"supportFiles/{generatedFileName}", Utils.ComputeIntegrity(vfsPath)) {
VirtualPath = targetPath
};
config.Assets.Add(asset);
@@ -291,15 +319,11 @@ protected override bool ExecuteInternal()
Log.LogError($"Expected the file defined as ICU resource: {idfn} to exist but it does not.");
return false;
}
- config.Assets.Add(new IcuData(Path.GetFileName(idfn)) { LoadRemote = loadRemote });
+ config.Assets.Add(new IcuData(Path.GetFileName(idfn), Utils.ComputeIntegrity(idfn)) { LoadRemote = loadRemote });
}
}
- config.Assets.Add(new WasmEntry ("dotnet.wasm") );
- if (IncludeThreadsWorker)
- config.Assets.Add(new ThreadsWorkerEntry ("dotnet.worker.js") );
-
if (RemoteSources?.Length > 0)
{
foreach (var source in RemoteSources)
@@ -328,6 +352,13 @@ protected override bool ExecuteInternal()
string tmpMonoConfigPath = Path.GetTempFileName();
using (var sw = File.CreateText(tmpMonoConfigPath))
{
+ var sb = new StringBuilder();
+ foreach(AssetEntry asset in config.Assets)
+ {
+ sb.Append(asset.Hash);
+ }
+ config.AssetsHash = Utils.ComputeTextIntegrity(sb.ToString());
+
var json = JsonSerializer.Serialize (config, new JsonSerializerOptions { WriteIndented = true });
sw.Write(json);
}
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj
index 3fd2f17c30a253..320586d8693944 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj
@@ -5,6 +5,7 @@
$(NoWarn),CA1050
$(NoWarn),CS8604,CS8602
+ $(NoWarn),CA1850
false
true
true
diff --git a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj
index 0153d9a31ae4d1..fbb13d8d9c4ecf 100644
--- a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj
+++ b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj
@@ -2,7 +2,7 @@
$(TargetFrameworkForNETCoreTasks)
enable
- $(NoWarn),CA1050
+ $(NoWarn),CA1050,CA1850