-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Wasm marshaler API for user-defined types; migrate Uri and DateTime to new API #47640
Conversation
Tagging subscribers to 'arch-wasm': @lewing Issue DetailsThis PR introduces a system for defining custom JS➔Managed and Managed➔JS marshaling primitives so that any* user-defined type can cross between environments seamlessly. The ideal end goal is to use this new system for marshaling types like First, some sample code: public struct CustomDate {
public DateTime Date;
private static string JSToManaged_PreFilter () => "value.toISOString()";
private static string ManagedToJS_PostFilter () => "new Date(value)";
private static CustomDate JSToManaged (string s) {
return new CustomDate {
Date = DateTime.Parse(s).ToUniversalTime()
};
}
private static string ManagedToJS (ref CustomDate cd) {
return cd.Date.ToString("o");
}
} General overview:
More detailed notes:
Also in this PR:
|
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
To help with understanding some of what this machinery does, here's an example of some of the code generated by the bindings to facilitate calling C# from JS: |
Failure is relevant |
This breaks debugger tests with It fails at The method in question: .. is in a nested class |
Thanks for digging in. I bet it's due to generics. |
Not sure if it is generics or not but I had to add some code for handling generic signatures in the PR here: #47519 This allows for generic signatures. |
2a4b472
to
c8554ec
Compare
Just running through the DRAFT PR:
|
d4fef0c
to
7d09a5d
Compare
a439856
to
52d3615
Compare
8897689
to
990a19f
Compare
Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes. |
4f05f98
to
fd96dc3
Compare
5eca689
to
86b404b
Compare
@kg are you still missing some changes? |
I'm still working through the msbuild integration in a way that won't break tests |
b69bcc0
to
7a1c854
Compare
....Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs
Outdated
Show resolved
Hide resolved
Here are a set of generated marshaler functions, @pavelsavara //# sourceURL=https://mono-wasm.invalid/System_DateTime$FromJavaScript
"use strict";
const Module = __closure__.Module;
const mono_wasm_new_root = __closure__.mono_wasm_new_root;
const _create_temp_frame = __closure__._create_temp_frame;
const _get_args_root_buffer_for_method_call = __closure__._get_args_root_buffer_for_method_call;
const _get_buffer_for_method_call = __closure__._get_buffer_for_method_call;
const _handle_exception_for_call = __closure__._handle_exception_for_call;
const _teardown_after_call = __closure__._teardown_after_call;
const mono_wasm_try_unbox_primitive_and_get_type = __closure__.mono_wasm_try_unbox_primitive_and_get_type;
const _unbox_mono_obj_root_with_known_nonprimitive_type = __closure__._unbox_mono_obj_root_with_known_nonprimitive_type;
const invoke_method = __closure__.invoke_method;
const method = __closure__.method;
const this_arg = __closure__.this_arg;
const token = __closure__.token;
const unbox_buffer = __closure__.unbox_buffer;
const unbox_buffer_size = __closure__.unbox_buffer_size;
const getI32 = __closure__.getI32;
const getU32 = __closure__.getU32;
const getF32 = __closure__.getF32;
const getF64 = __closure__.getF64;
const converter_d_result_unmarshaled = __closure__.converter_d_result_unmarshaled;
function System_DateTime$FromJavaScript(arg0) {
_create_temp_frame();
let resultRoot = token.scratchResultRoot, exceptionRoot = token.scratchExceptionRoot;
token.scratchResultRoot = null;
token.scratchExceptionRoot = null;
if (resultRoot === null)
resultRoot = mono_wasm_new_root();
if (exceptionRoot === null)
exceptionRoot = mono_wasm_new_root();
let argsRootBuffer = _get_args_root_buffer_for_method_call(converter_d_result_unmarshaled, token);
let scratchBuffer = _get_buffer_for_method_call(converter_d_result_unmarshaled, token);
let buffer = converter_d_result_unmarshaled.compiled_function(
scratchBuffer, argsRootBuffer, method,
arg0
);
let is_result_marshaled = false;
resultRoot.value = invoke_method(method, this_arg, buffer, exceptionRoot.get_address());
_handle_exception_for_call(converter_d_result_unmarshaled, token, buffer, resultRoot, exceptionRoot, argsRootBuffer);
let resultPtr = resultRoot.value, result = undefined;
result = resultPtr;
_teardown_after_call(converter_d_result_unmarshaled, token, buffer, resultRoot, exceptionRoot, argsRootBuffer);
return result;
};
return System_DateTime$FromJavaScript;
//# sourceURL=https://mono-wasm.invalid/interchange_filter_for_type62173044
"use strict";
const Module = __closure__.Module;
const MONO = __closure__.MONO;
const BINDING = __closure__.BINDING;
const typePtr = __closure__.typePtr;
const alloca = __closure__.alloca;
const getI8 = __closure__.getI8;
const getI16 = __closure__.getI16;
const getI32 = __closure__.getI32;
const getI64 = __closure__.getI64;
const getU8 = __closure__.getU8;
const getU16 = __closure__.getU16;
const getU32 = __closure__.getU32;
const getF32 = __closure__.getF32;
const getF64 = __closure__.getF64;
const setI8 = __closure__.setI8;
const setI16 = __closure__.setI16;
const setI32 = __closure__.setI32;
const setI64 = __closure__.setI64;
const setU8 = __closure__.setU8;
const setU16 = __closure__.setU16;
const setU32 = __closure__.setU32;
const setF32 = __closure__.setF32;
const setF64 = __closure__.setF64;
const System_DateTime$FromJavaScript = __closure__.System_DateTime$FromJavaScript;
function interchange_filter_for_type62173044(value) {
switch (typeof (value)) {
case 'number':
return value;
default:
if (value instanceof Date) {
return value.valueOf();
} else
throw new Error('Value must be a number (msecs since unix epoch), or a Date');
}
};
return interchange_filter_for_type62173044;
//# sourceURL=https://mono-wasm.invalid/js_to_interchange_for_type62173044
"use strict";
const Module = __closure__.Module;
const MONO = __closure__.MONO;
const BINDING = __closure__.BINDING;
const typePtr = __closure__.typePtr;
const alloca = __closure__.alloca;
const getI8 = __closure__.getI8;
const getI16 = __closure__.getI16;
const getI32 = __closure__.getI32;
const getI64 = __closure__.getI64;
const getU8 = __closure__.getU8;
const getU16 = __closure__.getU16;
const getU32 = __closure__.getU32;
const getF32 = __closure__.getF32;
const getF64 = __closure__.getF64;
const setI8 = __closure__.setI8;
const setI16 = __closure__.setI16;
const setI32 = __closure__.setI32;
const setI64 = __closure__.setI64;
const setU8 = __closure__.setU8;
const setU16 = __closure__.setU16;
const setU32 = __closure__.setU32;
const setF32 = __closure__.setF32;
const setF64 = __closure__.setF64;
const System_DateTime$FromJavaScript = __closure__.System_DateTime$FromJavaScript;
const filter = __closure__.filter;
function js_to_interchange_for_type62173044(value, method, parmIdx) {
let filteredValue = filter(value);
let convertedResult = System_DateTime$FromJavaScript(filteredValue, method, parmIdx);
return convertedResult;
};
return js_to_interchange_for_type62173044; |
Vs code with default settings, not sure why
On November 10, 2021 10:49:13 PM PST, Pavel Savara ***@***.***> wrote:
***@***.*** commented on this pull request.
> @@ -188,8 +193,8 @@ export function _handle_exception_for_call(
function _handle_exception_and_produce_result_for_call(
converter: Converter | undefined, token: BoundMethodToken | null,
- buffer: VoidPtr, resultRoot: WasmRoot<MonoString>,
- exceptionRoot: WasmRoot<MonoObject>, argsRootBuffer: WasmRootBuffer | undefined,
+ buffer: VoidPtr, resultRoot: WasmRoot<MonoString>,
The whitespace at the end is something your editor does ?
--
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#47640 (review)
- kg (mobile)
|
PR updated with support for passing |
Pull request contains merge conflicts. |
ceba251
to
def3119
Compare
Azure Pipelines successfully started running 1 pipeline(s). |
/azp run runtime-manual |
Azure Pipelines successfully started running 1 pipeline(s). |
221f4c4
to
b23df27
Compare
/azp run runtime-manual |
Azure Pipelines successfully started running 1 pipeline(s). |
/azp run runtime-manual |
Azure Pipelines successfully started running 1 pipeline(s). |
8afdc4a
to
0deb1c2
Compare
Add test coverage for date marshaling Add uri test Disable some log statements Returning structs works Returning custom classes works Add null checks to describe_value Remove invalid cwrap Add trimming annotations Restore old benchmark html Reuse result and exception roots Cleaner codegen Disambiguate converters in devtools Transition back to static methods, add signature checks and more error handling Implement a simple bound method cache so that automated tests don't exhaust the scratch root buffer; make the scratch root buffer smaller Fix a LMF leak Fix C warnings Hard-coded table of marshalers is no longer needed Shorter wrapper function names for better stacks Add linker exclusions for custom marshalers Hack around bug in linker/runtime that causes System.Uri forwarder to break Rework class lookup Support passing numeric values as DateTime Transition some null returns to asserts Clean up conditionals Don't use ISO strings Change pre/post filter syntax to require an explicit "return" so it can contain multiple statements Allow ToJavaScript to accept a pointer instead of an 'in' reference Support managed pointer return/parameter types in more places Add marshal_type for pointers Add tests verifying that you can marshal structs by address and then unpack them Initial gc safety work Fix formatting rule Rename pre/post filters Fix type error in driver.c remove _pick_result_chara_for_marshal_type Add library build descriptor to ensure marshalers are not stripped when the BCL is trimmed Attempt to fix the linker stripping test code Better version of no-configured-marshaler warning Annotate SetupJSContinuation Annotate safehandle APIs and add null checks Update targets file to pass custom marshaler msbuild items through to the helix proxy project (requires another change to work) Add tests for Task and ValueTask marshaling Fix unboxing for generic structs Rebase cleanups Correct datetime test to use UTC for comparison Eliminate use of MONO. and BINDING. in closures Optimize out a js->c call Normalize some APIs to take MonoType instead of MonoClass Repair merge damage Address PR feedback Move some types around Repair merge damage Type system fixes Rework create_named_function so that it can handle larger numbers of closure keys more efficiently Remove unnecessary test instrumentation Fix closure variables being generated in the wrong place Use a single memory slab for temp_malloc Checkpoint span support Fix unbuffered filters, support ReadOnlySpan Fix auto signatures for primitives and add test Use 4-chara unicode escapes since the x escapes are not officially permitted in JSON Checkpoint C# implementation of converter generator Align everything by 8 when constructing argument buffers because if you don't do that, the runtime passes corrupt data to C# functions Don't shove raw mono pointers into root buffers since we weren't doing it before (it might be nice to do it though) Fully transition over to having C# generate signature converters C# implementation of bind_method codegen Clean up the bindings named closure table management Add some comments Don't allocate a root for the this-ref when binding methods if the this-ref is null Don't generate dead code for void signatures Remove some dead code and refactor bound method generator Generate more specialized result handling code for bound methods. Align C# and C's idea of marshal types better. Fix GC safety issue Fix tsc wasting 70 seconds on every build Add basic build profiling information Fix root index being incorrect Rename MemOffset and MemValue Remove bind_method this_arg support Move some stuff around Change nested type descriptor syntax
…m hiding other errors, and avoid double teardown when managed code throws
fd1dc81
to
85eb349
Compare
This PR introduces a system for defining custom JS➔Managed and Managed➔JS marshaling primitives so that any* user-defined type can cross between environments seamlessly, and migrates our existing Uri and DateTime marshaling logic to use the new system.
First, some sample code:
General overview:
Date
or C#DateTime
can cross the bindings boundary directly, you can marshal it as a string and then use a filter to map it to/from the JSDate
type.const string
instead of function getters, but right now the only straightforward way to get at managed values is through function calls.'a'
type specifier for method signatures (the strings you pass tocall_static_method
,bind_static_method
etc) that basically means 'figure it out'. When you use this specifier, the bindings layer will examine the target method and identify the best fit for that parameter. This is meant for the purpose of passing user-defined types so you don't need to think about whether they're classes or structs, but it also can be useful in other cases.More detailed notes:
JSON.parse(value)
) to simplify the former, but the latter really demands integrated support for passingSpan
s around and I have no idea how we'd do this in the existing setup.BinaryFormatter
or something like that, but it seems out of scope. This system is at least relatively easy to extend to do that as a fallback.Also in this PR:
box_js_obj_with_converter
API that allows you to request a specific managed type when boxing a JS value (this is how you create a type like Uri explicitly)Date
s. I think that was actually broken, but Blazor works so whatever...