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

Introduce write barriers in wasm bindings, migrate to ref/out params, add gc safe regions #65994

Merged
merged 7 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -236,6 +236,7 @@
<PlatformManifestFileEntry Include="driver.c" IsNative="true" />
<PlatformManifestFileEntry Include="pinvoke.c" IsNative="true" />
<PlatformManifestFileEntry Include="pinvoke.h" IsNative="true" />
<PlatformManifestFileEntry Include="gc-common.h" IsNative="true" />
<PlatformManifestFileEntry Include="emcc-default.rsp" IsNative="true" />
<PlatformManifestFileEntry Include="emcc-link.rsp" IsNative="true" />
<PlatformManifestFileEntry Include="emcc-props.json" IsNative="true" />
Expand Down
37 changes: 20 additions & 17 deletions src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,43 @@ internal static partial class Runtime
internal static extern object CompileFunction(string str, out int exceptionalResult);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object InvokeJSWithArgs(IntPtr jsHandle, string method, object?[] parms, out int exceptionalResult);
// FIXME: All of these signatures need to be object? in various places and not object, but the nullability
// warnings will take me hours and hours to fix so I'm not doing that right now since they're already broken
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object GetObjectProperty(IntPtr jsHandle, string propertyName, out int exceptionalResult);
internal static extern void GetObjectPropertyRef(IntPtr jsHandle, in string propertyName, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object SetObjectProperty(IntPtr jsHandle, string propertyName, object value, bool createIfNotExists, bool hasOwnProperty, out int exceptionalResult);
internal static extern void SetObjectPropertyRef(IntPtr jsHandle, in string propertyName, in object? value, bool createIfNotExists, bool hasOwnProperty, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object GetByIndex(IntPtr jsHandle, int index, out int exceptionalResult);
internal static extern void GetByIndexRef(IntPtr jsHandle, int index, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object SetByIndex(IntPtr jsHandle, int index, object? value, out int exceptionalResult);
internal static extern void SetByIndexRef(IntPtr jsHandle, int index, in object? value, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object GetGlobalObject(string? globalName, out int exceptionalResult);
internal static extern void GetGlobalObjectRef(in string? globalName, out int exceptionalResult, out object result);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object TypedArrayToArray(IntPtr jsHandle, out int exceptionalResult);
internal static extern void TypedArrayToArrayRef(IntPtr jsHandle, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object ReleaseCSOwnedObject(IntPtr jsHandle);
internal static extern void ReleaseCSOwnedObject(IntPtr jsHandle);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object CreateCSOwnedObject(string className, object[] parms, out int exceptionalResult);
internal static extern void CreateCSOwnedObjectRef(in string className, in object[] parms, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object TypedArrayCopyTo(IntPtr jsHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult);
internal static extern void TypedArrayCopyToRef(IntPtr jsHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object TypedArrayFrom(int arrayPtr, int begin, int end, int bytesPerElement, int type, out int exceptionalResult);
internal static extern void TypedArrayFromRef(int arrayPtr, int begin, int end, int bytesPerElement, int type, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object TypedArrayCopyFrom(IntPtr jsHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult);
internal static extern void TypedArrayCopyFromRef(IntPtr jsHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult, out object result);

// FIXME: Why are these IntPtr?
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object WebSocketSend(IntPtr webSocketJSHandle, IntPtr messagePtr, int offset, int length, int messageType, bool endOfMessage, out IntPtr promiseJSHandle, out int exceptionalResult);
internal static extern void WebSocketSend(IntPtr webSocketJSHandle, IntPtr messagePtr, int offset, int length, int messageType, bool endOfMessage, out IntPtr promiseJSHandle, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object WebSocketReceive(IntPtr webSocketJSHandle, IntPtr bufferPtr, int offset, int length, IntPtr responsePtr, out IntPtr promiseJSHandle, out int exceptionalResult);
internal static extern void WebSocketReceive(IntPtr webSocketJSHandle, IntPtr bufferPtr, int offset, int length, IntPtr responsePtr, out IntPtr promiseJSHandle, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object WebSocketOpen(string uri, object[]? subProtocols, Delegate onClosed, out IntPtr webSocketJSHandle, out IntPtr promiseJSHandle, out int exceptionalResult);
internal static extern void WebSocketOpenRef(in string uri, in object[]? subProtocols, in Delegate onClosed, out IntPtr webSocketJSHandle, out IntPtr promiseJSHandle, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern string WebSocketAbort(IntPtr webSocketJSHandle, out int exceptionalResult);
internal static extern void WebSocketAbort(IntPtr webSocketJSHandle, out int exceptionalResult, out string result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern object WebSocketClose(IntPtr webSocketJSHandle, int code, string? reason, bool waitForCloseReceived, out IntPtr promiseJSHandle, out int exceptionalResult);
internal static extern void WebSocketCloseRef(IntPtr webSocketJSHandle, int code, in string? reason, bool waitForCloseReceived, out IntPtr promiseJSHandle, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern string CancelPromise(IntPtr promiseJSHandle, out int exceptionalResult);

Expand Down Expand Up @@ -80,7 +83,7 @@ public static string InvokeJS(string str)
public static object GetGlobalObject(string? str = null)
{
int exception;
object jsObj = GetGlobalObject(str, out exception);
GetGlobalObjectRef(str, out exception, out object jsObj);

if (exception != 0)
throw new JSException($"Error obtaining a handle to global {str}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public object this[int i]
{
AssertNotDisposed();

object indexValue = Interop.Runtime.GetByIndex(JSHandle, i, out int exception);
Interop.Runtime.GetByIndexRef(JSHandle, i, out int exception, out object indexValue);

if (exception != 0)
throw new JSException((string)indexValue);
Expand All @@ -92,7 +92,7 @@ public object this[int i]
{
AssertNotDisposed();

object res = Interop.Runtime.SetByIndex(JSHandle, i, value, out int exception);
Interop.Runtime.SetByIndexRef(JSHandle, i, value, out int exception, out object res);

if (exception != 0)
throw new JSException((string)res);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public object GetObjectProperty(string name)
{
AssertNotDisposed();

object propertyValue = Interop.Runtime.GetObjectProperty(JSHandle, name, out int exception);
Interop.Runtime.GetObjectPropertyRef(JSHandle, name, out int exception, out object propertyValue);
if (exception != 0)
throw new JSException((string)propertyValue);
Interop.Runtime.ReleaseInFlight(propertyValue);
Expand All @@ -87,9 +87,9 @@ public void SetObjectProperty(string name, object value, bool createIfNotExists
{
AssertNotDisposed();

Interop.Runtime.SetObjectProperty(JSHandle, name, value, createIfNotExists, hasOwnProperty, out int exception);
Interop.Runtime.SetObjectPropertyRef(JSHandle, name, in value, createIfNotExists, hasOwnProperty, out int exception, out object res);
if (exception != 0)
throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}')");
throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}'): {res}");
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public static partial class Runtime
{
private static readonly Dictionary<int, WeakReference<JSObject>> _csOwnedObjects = new Dictionary<int, WeakReference<JSObject>>();

public static JSObject? GetCSOwnedObjectByJSHandle(IntPtr jsHandle, int shouldAddInflight)
public static void GetCSOwnedObjectByJSHandleRef(IntPtr jsHandle, int shouldAddInflight, out JSObject? result)
{
lock (_csOwnedObjects)
{
Expand All @@ -20,14 +20,14 @@ public static partial class Runtime
{
jsObject.AddInFlight();
}
return jsObject;
result = jsObject;
return;
}
}
return null;

result = null;
}

public static IntPtr TryGetCSOwnedObjectJSHandle(object rawObj, int shouldAddInflight)
public static IntPtr TryGetCSOwnedObjectJSHandleRef(in object rawObj, int shouldAddInflight)
{
JSObject? jsObject = rawObj as JSObject;
if (jsObject != null && shouldAddInflight != 0)
Expand All @@ -37,7 +37,7 @@ public static IntPtr TryGetCSOwnedObjectJSHandle(object rawObj, int shouldAddInf
return jsObject?.JSHandle ?? IntPtr.Zero;
}

public static IntPtr GetCSOwnedObjectJSHandle(JSObject jsObject, int shouldAddInflight)
public static IntPtr GetCSOwnedObjectJSHandleRef(in JSObject jsObject, int shouldAddInflight)
{
jsObject.AssertNotDisposed();

Expand All @@ -48,9 +48,9 @@ public static IntPtr GetCSOwnedObjectJSHandle(JSObject jsObject, int shouldAddIn
return jsObject.JSHandle;
}

public static JSObject CreateCSOwnedProxy(IntPtr jsHandle, MappedType mappedType, int shouldAddInflight)
public static void CreateCSOwnedProxyRef(IntPtr jsHandle, MappedType mappedType, int shouldAddInflight, out JSObject? jsObject)
{
JSObject? jsObject = null;
jsObject = null;

lock (_csOwnedObjects)
{
Expand All @@ -75,8 +75,6 @@ public static JSObject CreateCSOwnedProxy(IntPtr jsHandle, MappedType mappedType
{
jsObject.AddInFlight();
}

return jsObject;
}

#region used from C# side
Expand All @@ -95,8 +93,7 @@ internal static bool ReleaseCSOwnedObject(JSObject objToRelease)

internal static IntPtr CreateCSOwnedObject(JSObject proxy, string typeName, params object[] parms)
{
object res = Interop.Runtime.CreateCSOwnedObject(typeName, parms, out int exception);

Interop.Runtime.CreateCSOwnedObjectRef(typeName, parms, out int exception, out object res);
if (exception != 0)
throw new JSException((string)res);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ public static partial class Runtime
private static Dictionary<object, IntPtr> GCHandleFromJSOwnedObject = new Dictionary<object, IntPtr>(ReferenceEqualityComparer.Instance);


public static object GetJSOwnedObjectByGCHandle(int gcHandle)
public static void GetJSOwnedObjectByGCHandleRef(int gcHandle, out object result)
{
GCHandle h = (GCHandle)(IntPtr)gcHandle;
return h.Target!;
result = h.Target!;
}

// A JSOwnedObject is a managed object with its lifetime controlled by javascript.
Expand All @@ -26,7 +26,7 @@ public static object GetJSOwnedObjectByGCHandle(int gcHandle)
// strong references, allowing the managed object to be collected.
// This ensures that things like delegates and promises will never 'go away' while JS
// is expecting to be able to invoke or await them.
public static IntPtr GetJSOwnedObjectGCHandle(object obj)
public static IntPtr GetJSOwnedObjectGCHandleRef(in object obj)
{
if (obj == null)
return IntPtr.Zero;
Expand Down Expand Up @@ -58,10 +58,10 @@ public static void ReleaseJSOwnedObjectByGCHandle(int gcHandle)
public static IntPtr CreateTaskSource()
{
var tcs = new TaskCompletionSource<object>();
return GetJSOwnedObjectGCHandle(tcs);
return GetJSOwnedObjectGCHandleRef(tcs);
}

public static void SetTaskSourceResult(int tcsGCHandle, object result)
public static void SetTaskSourceResultRef(int tcsGCHandle, in object result)
{
GCHandle handle = (GCHandle)(IntPtr)tcsGCHandle;
// this is JS owned Normal handle. We always have a Target
Expand All @@ -77,21 +77,25 @@ public static void SetTaskSourceFailure(int tcsGCHandle, string reason)
tcs.SetException(new JSException(reason));
}

public static object GetTaskSourceTask(int tcsGCHandle)
public static void GetTaskSourceTaskRef(int tcsGCHandle, out object result)
{
GCHandle handle = (GCHandle)(IntPtr)tcsGCHandle;
// this is JS owned Normal handle. We always have a Target
TaskCompletionSource<object> tcs = (TaskCompletionSource<object>)handle.Target!;
return tcs.Task;
result = tcs.Task;
}

public static object TaskFromResult(object? obj)
public static void TaskFromResultRef(in object? obj, out object result)
{
return Task.FromResult(obj);
result = Task.FromResult(obj);
}

public static void SetupJSContinuation(Task task, JSObject continuationObj)
public static void SetupJSContinuationRef(in Task _task, JSObject continuationObj)
{
// HACK: Attempting to use the in-param will produce CS1628, so we make a temporary copy
// on the stack that can be captured by our local functions below
var task = _task;

if (task.IsCompleted)
Complete();
else
Expand Down
Loading