Skip to content
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
110 changes: 72 additions & 38 deletions src/cs/Bootsharp.Publish.Test/Pack/BindingTest.cs

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions src/cs/Bootsharp.Publish.Test/Pack/PackTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ protected override void AddAssembly (string assemblyName, params MockSource[] so

private BootsharpPack CreateTask () => new() {
BuildDirectory = Project.Root,
DebugDirectory = Project.Root,
InspectedDirectory = Project.Root,
EntryAssemblyName = "System.Runtime.dll",
BuildEngine = Engine,
TrimmingEnabled = false,
EmbedBinaries = false,
Threading = false,
LLVM = false
LLVM = false,
Debug = false
};
}
24 changes: 24 additions & 0 deletions src/cs/Bootsharp.Publish.Test/Pack/ResourceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,28 @@ public void BinariesNotEmbeddedWhenDisabled ()
Contains("""wasm: { name: "dotnet.native.wasm", content: undefined },""");
Contains("""{ name: "Foo.wasm", content: undefined""");
}

[Fact]
public void WhenDebugEnabledDebugArtifactsIncluded ()
{
Task.Debug = true;
AddAssembly("Foo.dll");
Project.WriteFile("Foo.pdb", "MockPdbContent");
Project.WriteFile("dotnet.native.js.symbols", "MockSymbolsContent");
Execute();
Contains("""{ name: "Foo.pdb", content: undefined }""");
Contains("""{ name: "dotnet.native.js.symbols", content: undefined }""");
}

[Fact]
public void WhenDebugDisabledDebugArtifactsNotIncluded ()
{
Task.Debug = false;
AddAssembly("Foo.dll");
Project.WriteFile("Foo.pdb", "MockPdbContent");
Project.WriteFile("dotnet.native.js.symbols", "MockSymbolsContent");
Execute();
DoesNotContain("""{ name: "Foo.pdb", content: undefined }""");
DoesNotContain("""{ name: "dotnet.native.js.symbols", content: undefined }""");
}
}
44 changes: 36 additions & 8 deletions src/cs/Bootsharp.Publish/Pack/BindingGenerator/BindingGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Bootsharp.Publish;

internal sealed class BindingGenerator (Preferences prefs)
internal sealed class BindingGenerator (Preferences prefs, bool debug)
{
private record Binding (MethodMeta? Method, Type? Enum, string Namespace);

Expand Down Expand Up @@ -35,6 +35,12 @@ public string Generate (SolutionInspection inspection)
EmitImports();
builder.Append("\n\n");

if (debug)
{
EmitDebugHelpers();
builder.Append("\n\n");
}

builder.Append(serdeGenerator.Generate(inspection.Serialized));
builder.Append('\n');

Expand All @@ -54,9 +60,30 @@ private void EmitImports ()
import { Event } from "./event";
import { registerInstance, getInstance, disposeOnFinalize } from "./instances";
import { serialize, deserialize, binary, types } from "./serialization";
"""
);
}

function getExports() { if (exports == null) throw Error("Boot the runtime before invoking C# APIs."); return exports; }
function getImport(handler, serializedHandler, name) { if (typeof handler !== "function") throw Error(`Failed to invoke '${name}' from C#. Make sure to assign the function in JavaScript.`); return serializedHandler; }
private void EmitDebugHelpers ()
{
builder.Append(
"""
function getExport(name) {
return (...args) => {
if (exports == null) throw Error("Boot the runtime before invoking C# APIs.");
let result;
try { result = exports[name](...args); }
catch (error) { throw Error(`${error.message}\n${error.stack}`); }
if (typeof result?.then === "function")
return result.catch(error => { throw Error(`${error.message}\n${error.stack}`); });
return result;
};
}

function getImport(handler, serializedHandler, name) {
if (typeof handler !== "function") throw Error(`Failed to invoke '${name}' from C#. Make sure to assign the function in JavaScript.`);
return serializedHandler;
}
"""
);
}
Expand Down Expand Up @@ -123,7 +150,8 @@ private void EmitInvokable (MethodMeta method)
{
var instanced = IsInstanced(method);
var wait = ShouldWait(method);
var endpoint = $"getExports().{method.Space.Replace('.', '_')}_{method.Name}";
var fn = $"{method.Space.Replace('.', '_')}_{method.Name}";
var endpoint = debug ? $"""getExport("{fn}")""" : $"exports.{fn}";
var funcArgs = string.Join(", ", method.Arguments.Select(a => a.JSName));
if (instanced) funcArgs = PrependInstanceIdArgName(funcArgs);
var invArgs = string.Join(", ", method.Arguments.Select(BuildInvArg));
Expand Down Expand Up @@ -158,11 +186,11 @@ private void EmitFunction (MethodMeta method)
if (instanced) builder.Append($"{Break()}{name}Serialized: {serdeHandler}");
else
{
var set = $"{handler} = handler; this.{name}SerializedHandler = {serdeHandler};";
var serde = $"return getImport({handler}, this.{name}SerializedHandler, \"{binding.Namespace}.{name}\");";
var serde = $"this.{name}SerializedHandler";
var serdeExp = debug ? $"getImport({handler}, {serde}, \"{binding.Namespace}.{name}\")" : serde;
builder.Append($"{Break()}get {name}() {{ return {handler}; }}");
builder.Append($"{Break()}set {name}(handler) {{ {set} }}");
builder.Append($"{Break()}get {name}Serialized() {{ {serde} }}");
builder.Append($"{Break()}set {name}(handler) {{ {handler} = handler; {serde} = {serdeHandler}; }}");
builder.Append($"{Break()}get {name}Serialized() {{ return {serdeExp}; }}");
}

string BuildInvArg (ArgumentMeta arg)
Expand Down
9 changes: 5 additions & 4 deletions src/cs/Bootsharp.Publish/Pack/BootsharpPack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ namespace Bootsharp.Publish;
public sealed class BootsharpPack : Microsoft.Build.Utilities.Task
{
public required string BuildDirectory { get; set; }
public required string DebugDirectory { get; set; }
public required string InspectedDirectory { get; set; }
public required string EntryAssemblyName { get; set; }
public required bool TrimmingEnabled { get; set; }
public required bool EmbedBinaries { get; set; }
public required bool Threading { get; set; }
public required bool LLVM { get; set; }
public required bool Debug { get; set; }

public override bool Execute ()
{
Expand Down Expand Up @@ -49,7 +50,7 @@ IEnumerable<string> GetFiles ()

private void GenerateBindings (Preferences prefs, SolutionInspection inspection)
{
var generator = new BindingGenerator(prefs);
var generator = new BindingGenerator(prefs, Debug);
var content = generator.Generate(inspection);
File.WriteAllText(Path.Combine(BuildDirectory, "bindings.g.js"), content);
}
Expand All @@ -63,8 +64,8 @@ private void GenerateDeclarations (Preferences prefs, SolutionInspection inspect

private void GenerateResources (SolutionInspection inspection)
{
var generator = new ResourceGenerator(EntryAssemblyName, EmbedBinaries);
var content = generator.Generate(BuildDirectory);
var generator = new ResourceGenerator(EntryAssemblyName, EmbedBinaries, Debug);
var content = generator.Generate(BuildDirectory, DebugDirectory);
File.WriteAllText(Path.Combine(BuildDirectory, "resources.g.js"), content);
}

Expand Down
21 changes: 18 additions & 3 deletions src/cs/Bootsharp.Publish/Pack/ResourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
namespace Bootsharp.Publish;

internal sealed class ResourceGenerator (string entryAssemblyName, bool embed)
internal sealed class ResourceGenerator (string entryAssemblyName, bool embed, bool debug)
{
private readonly List<string> assemblies = [];
private readonly List<string> symbols = [];
private readonly List<string> pdb = [];
private string wasm = null!;

public string Generate (string buildDir)
public string Generate (string buildDir, string debugDir)
{
foreach (var path in Directory.GetFiles(buildDir, "*.wasm"))
foreach (var path in Directory.GetFiles(buildDir, "*.wasm").Order())
if (path.EndsWith("dotnet.native.wasm")) wasm = BuildBin(path);
else assemblies.Add(BuildBin(path));
if (debug)
{
foreach (var path in Directory.GetFiles(debugDir, "*.symbols").Order())
symbols.Add(BuildBin(path));
foreach (var path in Directory.GetFiles(debugDir, "*.pdb").Order())
pdb.Add(BuildBin(path));
}
return
$$"""
export default {
wasm: {{wasm}},
assemblies: [
{{JoinLines(assemblies, 2, ",\n")}}
],
symbols: [
{{JoinLines(symbols, 2, ",\n")}}
],
pdb: [
{{JoinLines(pdb, 2, ",\n")}}
],
entryAssemblyName: "{{entryAssemblyName}}"
};
""";
Expand Down
10 changes: 6 additions & 4 deletions src/cs/Bootsharp/Build/Bootsharp.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
<BootsharpPackageDirectory/>

<!-- Internal Properties -->
<!-- Whether to publish in the debug configuration. -->
<BsDebug>$([MSBuild]::ValueOrDefault('$(Configuration)', '').Equals('Debug'))</BsDebug>
<!-- Whether to use NativeAOT-LLVM compiler (enabled by default for release builds). -->
<BootsharpLlvm>$([MSBuild]::ValueOrDefault('$(Configuration)', '').Equals('Release'))</BootsharpLlvm>
<BsLlvm>!$(BsDebug)</BsLlvm>
</PropertyGroup>

<PropertyGroup Condition="$(BootsharpLlvm)">
<PropertyGroup Condition="$(BsLlvm)">
<!-- NativeAOT-LLVM specific knobs. -->
<DotNetJsApi>true</DotNetJsApi>
<EmccFlags>$(EmccFlags) -O3</EmccFlags>
Expand Down Expand Up @@ -77,7 +79,7 @@
<XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>
</PropertyGroup>

<PropertyGroup Condition="!$(BootsharpLlvm)">
<PropertyGroup Condition="!$(BsLlvm)">
<!-- Various knobs that improve debugging and compile times. -->
<WasmRunWasmOpt>false</WasmRunWasmOpt>
<WasmNativeStrip>false</WasmNativeStrip>
Expand All @@ -94,6 +96,6 @@

<!-- Include NativeAOT-LLVM properties when enabled. -->
<Import Project="$(MSBuildThisFileDirectory)../llvm/microsoft.dotnet.ilcompiler.llvm/build/Microsoft.DotNet.ILCompiler.LLVM.props"
Condition="$(BootsharpLlvm)"/>
Condition="$(BsLlvm)"/>

</Project>
Loading
Loading