Skip to content

Commit

Permalink
rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelsavara committed Feb 17, 2024
1 parent d06742c commit 3e4e9a5
Show file tree
Hide file tree
Showing 41 changed files with 433 additions and 217 deletions.
8 changes: 4 additions & 4 deletions docs/workflow/debugging/mono/wasm-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ $func166 @ dotnet.wasm:0xba0a
$func2810 @ dotnet.wasm:0xabacf
$func1615 @ dotnet.wasm:0x6f8eb
$func1619 @ dotnet.wasm:0x6ff58
$mono_wasm_invoke_method @ dotnet.wasm:0x96c9
Module._mono_wasm_invoke_method @ dotnet.6.0.1.hopd7ipo8x.js:1
$mono_wasm_invoke_jsexport @ dotnet.wasm:0x96c9
Module.mono_wasm_invoke_jsexport @ dotnet.6.0.1.hopd7ipo8x.js:1
managed__Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_BeginInvokeDotNet @ managed__Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_BeginInvokeDotNet:19
beginInvokeDotNetFromJS @ blazor.webassembly.js:1
b @ blazor.webassembly.js:1
Expand Down Expand Up @@ -244,8 +244,8 @@ $mono_jit_runtime_invoke @ dotnet.wasm:0x1dec32
$do_runtime_invoke @ dotnet.wasm:0x95fca
$mono_runtime_try_invoke @ dotnet.wasm:0x966fe
$mono_runtime_invoke @ dotnet.wasm:0x98982
$mono_wasm_invoke_method @ dotnet.wasm:0x227de2
Module._mono_wasm_invoke_method @ dotnet..y6ggkhlo8e.js:9927
$mono_wasm_invoke_jsexport @ dotnet.wasm:0x227de2
Module.mono_wasm_invoke_jsexport @ dotnet..y6ggkhlo8e.js:9927
managed__Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_BeginInvokeDotNet @ managed__Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_BeginInvokeDotNet:19
beginInvokeDotNetFromJS @ blazor.webassembly.js:1
b @ blazor.webassembly.js:1
Expand Down
8 changes: 4 additions & 4 deletions src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ internal static unsafe partial class Runtime
public static extern void UninstallWebWorkerInterop();

[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void InvokeJSImportSync(nint data, nint signature);
public static extern void InvokeJSImportSync(nint signature, nint args);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void InvokeJSImportSyncSend(nint targetNativeTID, nint data, nint signature);
public static extern void InvokeJSImportSyncSend(nint targetNativeTID, nint signature, nint args);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void InvokeJSImportAsyncPost(nint targetNativeTID, nint data, nint signature);
public static extern void InvokeJSImportAsyncPost(nint targetNativeTID, nint signature, nint args);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void CancelPromise(nint taskHolderGCHandle);
[MethodImpl(MethodImplOptions.InternalCall)]
Expand All @@ -60,7 +60,7 @@ internal static unsafe partial class Runtime
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern unsafe void BindJSImport(void* signature, out int is_exception, out object result);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void InvokeJSImport(int importHandle, nint data);
public static extern void InvokeJSImportST(int importHandle, nint args);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void CancelPromise(nint gcHandle);
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ ResolvedGenerator fail(string failReason)
return ResolvedGenerator.NotSupported(new(info, context));

// void
case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.OneWay }:
return ResolvedGenerator.Resolved(new VoidGenerator(MarshalerType.OneWay));
case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Discard }:
case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Void }:
case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.None }:
Expand All @@ -52,6 +54,10 @@ ResolvedGenerator fail(string failReason)
case { JSType: JSTypeFlags.Discard }:
return fail(SR.DiscardOnlyVoid);

// oneway no void
case { JSType: JSTypeFlags.OneWay }:
return fail(SR.OneWayOnlyVoid);

// primitive
case { TypeInfo: JSSimpleTypeInfo simple }:
return Create(info, isToJs, simple.KnownType, Array.Empty<KnownManagedType>(), jsMarshalingInfo.JSType, Array.Empty<JSTypeFlags>(), fail);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal enum JSTypeFlags : int
MemoryView = 0x800,
Any = 0x1000,
Discard = 0x2000,
OneWay = 0x4000,
Missing = 0x4000_0000,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@
<data name="DiscardOnlyVoid" xml:space="preserve">
<value>'JSType.Discard' could be only used with void return argument.</value>
</data>
<data name="OneWayOnlyVoid" xml:space="preserve">
<value>'JSType.OneWay' could be only used with void returning method.</value>
</data>
<data name="FuncArgumentNotSupported" xml:space="preserve">
<value>Type {0} is not supported as argument of marshaled function.</value>
<comment>{0} is a type of the argument</comment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,16 @@
<Left>ref/net9.0/System.Runtime.InteropServices.JavaScript.dll</Left>
<Right>runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Runtime.InteropServices.JavaScript.JSType.OneWay</Target>
<Left>ref/net9.0/System.Runtime.InteropServices.JavaScript.dll</Left>
<Right>runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:System.Runtime.InteropServices.JavaScript.JSMarshalerType.get_OneWay</Target>
<Left>ref/net9.0/System.Runtime.InteropServices.JavaScript.dll</Left>
<Right>runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll</Right>
</Suppression>
</Suppressions>
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -28,6 +30,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
#if FEATURE_WASM_MANAGED_THREADS
// when we arrive here, we are on the thread which owns the proxies
arg_exc.AssertCurrentThreadContext();
Debug.Assert(arg_result.slot.Type == MarshalerType.TaskPreCreated);
#endif

arg_1.ToManaged(out IntPtr assemblyNamePtr);
Expand All @@ -47,6 +50,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
}
}

// the marshaled signature is: void LoadLazyAssembly(byte[] dll, byte[] pdb)
public static void LoadLazyAssembly(JSMarshalerArgument* arguments_buffer)
{
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0];
Expand All @@ -70,6 +74,7 @@ public static void LoadLazyAssembly(JSMarshalerArgument* arguments_buffer)
}
}

// the marshaled signature is: void LoadSatelliteAssembly(byte[] dll)
public static void LoadSatelliteAssembly(JSMarshalerArgument* arguments_buffer)
{
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0];
Expand All @@ -91,10 +96,8 @@ public static void LoadSatelliteAssembly(JSMarshalerArgument* arguments_buffer)
}
}

// The JS layer invokes this method when the JS wrapper for a JS owned object
// has been collected by the JS garbage collector
// the marshaled signature is:
// void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle)
// The JS layer invokes this method when the JS wrapper for a JS owned object has been collected by the JS garbage collector
// the marshaled signature is: void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle)
public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments_buffer)
{
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ internal static unsafe partial class JavaScriptImports

#if DEBUG
[JSImport("globalThis.console.log")]
[return: JSMarshalAs<JSType.OneWay>]
public static partial void Log([JSMarshalAs<JSType.String>] string message);
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal JSFunctionBinding() { }
internal static volatile uint nextImportHandle = 1;
internal int ImportHandle;
internal bool IsAsync;
internal bool IsOneWay;
#if DEBUG
internal string? FunctionName;
#endif
Expand Down Expand Up @@ -285,6 +286,11 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span
arguments[1].slot.GCHandle = holder.GCHandle;
}

if (signature.IsOneWay)
{
arguments[1].slot.Type = MarshalerType.OneWay;
}

#if FEATURE_WASM_MANAGED_THREADS
// if we are on correct thread already or this is synchronous call, just call it
if (targetContext.IsCurrentThread())
Expand All @@ -299,15 +305,15 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span
#endif

}
else if (!signature.IsAsync)
else if (signature.IsAsync || signature.IsOneWay)
{
//sync
DispatchJSImportSyncSend(signature, targetContext, arguments);
//async
DispatchJSImportAsyncPost(signature, targetContext, arguments);
}
else
{
//async
DispatchJSImportAsyncPost(signature, targetContext, arguments);
//sync
DispatchJSImportSyncSend(signature, targetContext, arguments);
}
#else
InvokeJSImportCurrent(signature, arguments);
Expand All @@ -332,9 +338,9 @@ internal static unsafe void InvokeJSImportCurrent(JSFunctionBinding signature, S
fixed (JSMarshalerArgument* args = arguments)
{
#if FEATURE_WASM_MANAGED_THREADS
Interop.Runtime.InvokeJSImportSync((nint)args, (nint)signature.Header);
Interop.Runtime.InvokeJSImportSync((nint)signature.Header, (nint)args);
#else
Interop.Runtime.InvokeJSImport(signature.ImportHandle, (nint)args);
Interop.Runtime.InvokeJSImportST(signature.ImportHandle, (nint)args);
#endif
}

Expand All @@ -361,7 +367,7 @@ internal static unsafe void DispatchJSImportSyncSend(JSFunctionBinding signature
// we also don't throw PNSE here, because we know that the target has JS interop installed and that it could not block
// so it could take some time, while target is CPU busy, but not forever
// see also https://github.com/dotnet/runtime/issues/76958#issuecomment-1921418290
Interop.Runtime.InvokeJSImportSyncSend(targetContext.JSNativeTID, args, sig);
Interop.Runtime.InvokeJSImportSyncSend(targetContext.JSNativeTID, sig, args);

ref JSMarshalerArgument exceptionArg = ref arguments[0];
if (exceptionArg.slot.Type != MarshalerType.None)
Expand All @@ -375,7 +381,10 @@ internal static unsafe void DispatchJSImportSyncSend(JSFunctionBinding signature
#endif
internal static unsafe void DispatchJSImportAsyncPost(JSFunctionBinding signature, JSProxyContext targetContext, Span<JSMarshalerArgument> arguments)
{
// this copy is freed in mono_wasm_invoke_import_async
// meaning JS side needs to dispose it
ref JSMarshalerArgument exc = ref arguments[0];
exc.slot.ReceiverShouldFree = true;

var bytes = sizeof(JSMarshalerArgument) * arguments.Length;
void* cpy = (void*)Marshal.AllocHGlobal(bytes);
void* src = Unsafe.AsPointer(ref arguments[0]);
Expand All @@ -385,7 +394,7 @@ internal static unsafe void DispatchJSImportAsyncPost(JSFunctionBinding signatur
// we already know that we are not on the right thread
// this will return quickly after sending the message
// async
Interop.Runtime.InvokeJSImportAsyncPost(targetContext.JSNativeTID, (nint)cpy, sig);
Interop.Runtime.InvokeJSImportAsyncPost(targetContext.JSNativeTID, sig, (nint)cpy);

}

Expand Down Expand Up @@ -431,8 +440,8 @@ internal static unsafe void ResolveOrRejectPromise(JSProxyContext targetContext,
else
{
// meaning JS side needs to dispose it
ref JSMarshalerArgument res = ref arguments[1];
res.slot.BooleanValue = true;
ref JSMarshalerArgument exc = ref arguments[0];
exc.slot.ReceiverShouldFree = true;

// this copy is freed in mono_wasm_resolve_or_reject_promise
var bytes = sizeof(JSMarshalerArgument) * arguments.Length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ public static unsafe JSFunctionBinding GetMethodSignature(ReadOnlySpan<JSMarshal
var type = signature.Sigs[i] = types[i + 1]._signatureType;
}
signature.IsAsync = types[0]._signatureType.Type == MarshalerType.Task;
signature.IsOneWay = types[0]._signatureType.Type == MarshalerType.OneWay;

signature.Header[0].ImportHandle = signature.ImportHandle;
signature.Header[0].FunctionNameLength = functionNameBytes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ internal struct JSMarshalerArgumentImpl

[FieldOffset(16)]
internal IntPtr ContextHandle;

[FieldOffset(20)]
internal bool ReceiverShouldFree;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ private JSMarshalerType(JSFunctionBinding.JSBindingType signatureType)
Type = MarshalerType.Discard
});

/// <summary>
/// Dispatches the call asynchronously and doesn't wait for result.
/// </summary>
/// <returns>The marshaler metadata.</returns>
public static JSMarshalerType OneWay { get; } = new JSMarshalerType(new JSFunctionBinding.JSBindingType
{
Type = MarshalerType.OneWay
});

/// <summary>
/// Marshal as JavaScript <see href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean</see> type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ private JSProxyContext()
#else
public nint ContextHandle;
public nint JSNativeTID; // target thread where JavaScript is running
public int ManagedTID;
public nint NativeTID; // current pthread id
public int ManagedTID; // current managed thread id
public bool IsMainThread;
public JSSynchronizationContext SynchronizationContext;

Expand All @@ -58,7 +59,7 @@ public static IntPtr GetNativeThreadId()
public JSProxyContext(bool isMainThread, JSSynchronizationContext synchronizationContext)
{
SynchronizationContext = synchronizationContext;
JSNativeTID = GetNativeThreadId();
NativeTID = JSNativeTID = GetNativeThreadId();
ManagedTID = Environment.CurrentManagedThreadId;
IsMainThread = isMainThread;
ContextHandle = (nint)GCHandle.Alloc(this, GCHandleType.Normal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ private unsafe void ScheduleJSPump()
{
// While we COULD pump here, we don't want to. We want the pump to happen on the next event loop turn.
// Otherwise we could get a chain where a pump generates a new work item and that makes us pump again, forever.
TargetThreadScheduleBackgroundJob(ProxyContext.JSNativeTID, (delegate* unmanaged[Cdecl]<void>)&BackgroundJobHandler);
TargetThreadScheduleBackgroundJob(ProxyContext.NativeTID, (delegate* unmanaged[Cdecl]<void>)&BackgroundJobHandler);
}

public override void Post(SendOrPostCallback d, object? state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ public sealed class Discard : JSType
internal Discard() { }
}

/// <summary>
/// Could return immediately without waiting for the execution to finish, when dispatching the call to another thread.
/// Suppresses marshaling of the JavaScript function's return value.
/// </summary>
public sealed class OneWay : JSType
{
internal OneWay() { }
}

/// <summary>
/// Marshal as JavaScript <see href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean</see> type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ internal enum MarshalerType : byte
Span,
Action,
Function,
OneWay,

#if !JSIMPORTGENERATOR
// only on runtime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ await JsExportTestAsync(value,
"boolean");
}

[Theory]
[MemberData(nameof(MarshalInt32Cases))]
public async Task JsExportInt32OneWay(int value)
{
JavaScriptTestHelper.optimizedReached=0;

JavaScriptTestHelper.invoke1O(value);
await Task.Yield();
Assert.Equal(value, JavaScriptTestHelper.optimizedReached);
}

private async Task JsExportTestAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value
, Func<T, string, Task<T>> invoke, string echoName, string jsType, string? jsClass = null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,15 @@ public void JsImportInt16(short value)
#endregion Int16

#region Int32
[Theory]
[MemberData(nameof(MarshalInt32Cases))]
public async Task JsImportInt32OneWay(int value)
{
JavaScriptTestHelper.store1OneWay_Int32(value);
await Task.Yield();
Assert.Equal(value, JavaScriptTestHelper.retrieve1_Int32());
}

[Theory]
[MemberData(nameof(MarshalInt32Cases))]
public void JsImportInt32(int value)
Expand Down
Loading

0 comments on commit 3e4e9a5

Please sign in to comment.