[API Proposal]: JavaScript marshalling support for Memory<>, ReadOnlyMemory<> and ReadOnlySpan<>
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Background and motivation
Currently the dotnet WebAssembly JavaScript interop (`JSImport`/`JSExport`) supports `ArraySegment` and `Span` as zero-copy views over buffers exchanged with JS. Of those two, `ArraySegment` references can be used beyond the immediate call stack. `Memory` is the newer, generally preferred API for this purpose, so this proposal adds `Memory` support as a more general replacement for what `ArraySegment` does today. The proposal also includes `ReadOnlyMemory` and `ReadOnlySpan` to let developers control whether exposed Wasm memory is writable.
The `Span` and `ArraySegment` part of this API currently supports: `byte`, `int`, `float` and `double`. `Memory`, `ReadOnlyMemory` and `ReadOnlySpan` should support the same element types.
See https://github.com/dotnet/runtime/discussions/97268 and https://github.com/dotnet/runtime/issues/97381.
### API Proposal
```diff
namespace System.Runtime.InteropServices.JavaScript;
[Versioning.SupportedOSPlatformAttribute("browser")]
[CLSCompliant(false)]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public struct JSMarshalerArgument
{
....
public void ToManaged(out Span value) { throw null; }
public void ToJS(Span value) { throw null; }
+ public void ToManaged(out ReadOnlySpan value) { throw null; }
+ public void ToJS(ReadOnlySpan value) { throw null; }
public void ToManaged(out ArraySegment value) { throw null; }
public void ToJS(ArraySegment value) { throw null; }
+ public void ToManaged(out Memory value) { throw null; }
+ public void ToJS(Memory value) { throw null; }
+ public void ToManaged(out ReadOnlyMemory value) { throw null; }
+ public void ToJS(ReadOnlyMemory value) { throw null; }
....
public void ToManaged(out Span value) { throw null; }
public void ToJS(Span value) { throw null; }
+ public void ToManaged(out ReadOnlySpan value) { throw null; }
+ public void ToJS(ReadOnlySpan value) { throw null; }
public void ToManaged(out ArraySegment value) { throw null; }
public void ToJS(ArraySegment value) { throw null; }
+ public void ToManaged(out Memory value) { throw null; }
+ public void ToJS(Memory value) { throw null; }
+ public void ToManaged(out ReadOnlyMemory value) { throw null; }
+ public void ToJS(ReadOnlyMemory value) { throw null; }
....
public void ToManaged(out Span value) { throw null; }
public void ToJS(Span value) { throw null; }
+ public void ToManaged(out ReadOnlySpan value) { throw null; }
+ public void ToJS(ReadOnlySpan value) { throw null; }
public void ToManaged(out ArraySegment value) { throw null; }
public void ToJS(ArraySegment value) { throw null; }
+ public void ToManaged(out Memory value) { throw null; }
+ public void ToJS(Memory value) { throw null; }
+ public void ToManaged(out ReadOnlyMemory value) { throw null; }
+ public void ToJS(ReadOnlyMemory value) { throw null; }
....
public void ToManaged(out Span value) { throw null; }
public void ToJS(Span value) { throw null; }
+ public void ToManaged(out ReadOnlySpan value) { throw null; }
+ public void ToJS(ReadOnlySpan value) { throw null; }
public void ToManaged(out ArraySegment value) { throw null; }
public void ToJS(ArraySegment value) { throw null; }
+ public void ToManaged(out Memory value) { throw null; }
+ public void ToJS(Memory value) { throw null; }
+ public void ToManaged(out ReadOnlyMemory value) { throw null; }
+ public void ToJS(ReadOnlyMemory value) { throw null; }
....
}
```
### API Usage
This is **API for code generated by Roslyn** analyzer when users use `[JSImport]` or `[JSExport]`.
It's not human facing API, but it needs to be public API because Roslyn generated code is consuming only visible APIs.
The generated code looks similar to this, just with `float` data type.
https://github.com/dotnet/runtime/blob/cf05e728871742f5c5483db2f3efea537ff17328/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/Compiles.cs#L210-L227
### User facing API
This doesn't change because this is all user code. The new types just become available to be used.
```csharp
[JSImport("echo", "JavaScriptTestHelper")]
static partial Memory Echo(Memory value);
[JSExport]
internal static Memory Round(Memory value)
{
return value;
}
[JSImport("echo3", "JavaScriptTestHelper")]
static partial ReadOnlyMemory Echo3(ReadOnlyMemory value);
[JSExport]
static ReadOnlySpan EchoSpan(ReadOnlySpan value)
{
return value;
}
```
### Alternative Designs
_No response_
### Risks
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.