diff --git a/src/mono/wasm/host/CommonConfiguration.cs b/src/mono/wasm/host/CommonConfiguration.cs index eb0a0af997d82d..7d854cc47141e2 100644 --- a/src/mono/wasm/host/CommonConfiguration.cs +++ b/src/mono/wasm/host/CommonConfiguration.cs @@ -22,6 +22,7 @@ internal sealed class CommonConfiguration public WasmHost Host { get; init; } public HostConfig HostConfig { get; init; } public WasmHostProperties HostProperties { get; init; } + public IEnumerable HostArguments { get; init; } private string? hostArg; private string? _runtimeConfigPath; @@ -30,11 +31,13 @@ internal sealed class CommonConfiguration private CommonConfiguration(string[] args) { + List hostArgsList = new(); var options = new OptionSet { { "debug|d", "Start debug server", _ => Debugging = true }, { "host|h=", "Host config name", v => hostArg = v }, - { "runtime-config|r=", "runtimeconfig.json path for the app", v => _runtimeConfigPath = v } + { "runtime-config|r=", "runtimeconfig.json path for the app", v => _runtimeConfigPath = v }, + { "extra-host-arg=", "Extra argument to be passed to the host", hostArgsList.Add }, }; RemainingArgs = options.Parse(args); @@ -95,6 +98,9 @@ private CommonConfiguration(string[] args) if (!Enum.TryParse(HostConfig.HostString, ignoreCase: true, out WasmHost wasmHost)) throw new CommandLineException($"Unknown host {HostConfig.HostString} in config named {HostConfig.Name}"); Host = wasmHost; + + hostArgsList.AddRange(HostConfig.HostArguments); + HostArguments = hostArgsList; } public ProxyOptions ToProxyOptions() diff --git a/src/mono/wasm/host/JSEngineHost.cs b/src/mono/wasm/host/JSEngineHost.cs index fc77d69818a417..cc80f068a18a5c 100644 --- a/src/mono/wasm/host/JSEngineHost.cs +++ b/src/mono/wasm/host/JSEngineHost.cs @@ -37,8 +37,6 @@ public static async Task InvokeAsync(CommonConfiguration commonArgs, private async Task RunAsync() { - string[] engineArgs = Array.Empty(); - string engineBinary = _args.Host switch { WasmHost.V8 => "v8", @@ -79,9 +77,10 @@ private async Task RunAsync() args.Add("--expose_wasm"); } + args.AddRange(_args.CommonConfig.HostArguments); + args.Add(_args.JSPath!); - args.AddRange(engineArgs); if (_args.Host is WasmHost.V8 or WasmHost.JavaScriptCore) { // v8/jsc want arguments to the script separated by "--", others don't diff --git a/src/mono/wasm/host/RuntimeConfigJson.cs b/src/mono/wasm/host/RuntimeConfigJson.cs index 3b7b0564c4d09c..ed698ed8fb3725 100644 --- a/src/mono/wasm/host/RuntimeConfigJson.cs +++ b/src/mono/wasm/host/RuntimeConfigJson.cs @@ -1,6 +1,7 @@ // 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.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; @@ -32,6 +33,7 @@ internal sealed record WasmHostProperties( internal sealed record HostConfig(string? Name, [property: JsonPropertyName("host")] string? HostString) { + [property: JsonPropertyName("host-args")] public string[] HostArguments { get; set; } = Array.Empty(); // using an explicit property because the deserializer doesn't like // extension data in the record constructor [property: JsonExtensionData] public Dictionary? Properties { get; set; } diff --git a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs index 9416c71760d0fd..2aa9132a7bc9ba 100644 --- a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs @@ -34,7 +34,7 @@ public IcallTableGenerator(Func fixupSymbolName, TaskLoggingHelp // The runtime icall table should be generated using // mono --print-icall-table // - public IEnumerable Generate(string? runtimeIcallTableFile, string[] assemblies, string? outputPath) + public IEnumerable Generate(string? runtimeIcallTableFile, IEnumerable assemblies, string? outputPath) { _icalls.Clear(); _signatures.Clear(); @@ -44,9 +44,13 @@ public IEnumerable Generate(string? runtimeIcallTableFile, string[] asse var resolver = new PathAssemblyResolver(assemblies); using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib"); - foreach (var aname in assemblies) + foreach (var asmPath in assemblies) { - var a = mlc.LoadFromAssemblyPath(aname); + if (!File.Exists(asmPath)) + throw new LogAsErrorException($"Cannot find assembly {asmPath}"); + + Log.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for icalls"); + var a = mlc.LoadFromAssemblyPath(asmPath); foreach (var type in a.GetTypes()) ProcessType(type); } diff --git a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs index 1dff74a9dcc1c1..9b04d02c670f99 100644 --- a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs @@ -1,9 +1,12 @@ // 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.Collections.Generic; 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; @@ -11,7 +14,7 @@ public class ManagedToNativeGenerator : Task { [Required] - public string[]? Assemblies { get; set; } + public string[] Assemblies { get; set; } = Array.Empty(); public string? RuntimeIcallTableFile { get; set; } @@ -62,12 +65,14 @@ public override bool Execute() private void ExecuteInternal() { + List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); + var pinvoke = new PInvokeTableGenerator(FixupSymbolName, Log); var icall = new IcallTableGenerator(FixupSymbolName, Log); IEnumerable cookies = Enumerable.Concat( - pinvoke.Generate(PInvokeModules, Assemblies!, PInvokeOutputPath!), - icall.Generate(RuntimeIcallTableFile, Assemblies!, IcallOutputPath) + pinvoke.Generate(PInvokeModules, managedAssemblies, PInvokeOutputPath!), + icall.Generate(RuntimeIcallTableFile, managedAssemblies, IcallOutputPath) ); var m2n = new InterpToNativeGenerator(Log); @@ -110,4 +115,43 @@ public string FixupSymbolName(string name) _symbolNameFixups[name] = fixedName; return fixedName; } + + private List FilterOutUnmanagedBinaries(string[] assemblies) + { + List managedAssemblies = new(assemblies.Length); + foreach (string asmPath in Assemblies) + { + if (!File.Exists(asmPath)) + throw new LogAsErrorException($"Cannot find assembly {asmPath}"); + + try + { + if (!IsManagedAssembly(asmPath)) + { + Log.LogMessage(MessageImportance.Low, $"Skipping unmanaged {asmPath}."); + continue; + } + } + catch (Exception ex) + { + Log.LogMessage(MessageImportance.Low, $"Failed to read assembly {asmPath}: {ex}"); + throw new LogAsErrorException($"Failed to read assembly {asmPath}: {ex.Message}"); + } + + managedAssemblies.Add(asmPath); + } + + 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/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index ab4eac397bc9f6..b8ecfa7f8438eb 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -24,7 +24,7 @@ public PInvokeTableGenerator(Func fixupSymbolName, TaskLoggingHe _fixupSymbolName = fixupSymbolName; } - public IEnumerable Generate(string[] pinvokeModules, string[] assemblies, string outputPath) + public IEnumerable Generate(string[] pinvokeModules, IEnumerable assemblies, string outputPath) { var modules = new Dictionary(); foreach (var module in pinvokeModules) @@ -37,9 +37,14 @@ public IEnumerable Generate(string[] pinvokeModules, string[] assemblies var resolver = new PathAssemblyResolver(assemblies); using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib"); - foreach (var aname in assemblies) + + foreach (var asmPath in assemblies) { - var a = mlc.LoadFromAssemblyPath(aname); + if (!File.Exists(asmPath)) + throw new LogAsErrorException($"Cannot find assembly {asmPath}"); + + Log.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes"); + var a = mlc.LoadFromAssemblyPath(asmPath); foreach (var type in a.GetTypes()) CollectPInvokes(pinvokes, callbacks, signatures, type); }