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

[js-api] Integrate with the ResizableArrayBuffer proposal #1300

Merged
merged 8 commits into from
Apr 3, 2024
109 changes: 96 additions & 13 deletions document/js-api/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
text: IterableToList; url: sec-iterabletolist
text: ToBigInt64; url: #sec-tobigint64
text: BigInt; url: #sec-ecmascript-language-types-bigint-type
text: SameValue; url: sec-samevalue
type: abstract-op
text: CreateMethodProperty; url: sec-createmethodproperty
urlPrefix: https://webassembly.github.io/reference-types/core/; spec: WebAssembly; type: dfn
Expand Down Expand Up @@ -187,6 +188,13 @@ urlPrefix: https://webassembly.github.io/reference-types/core/; spec: WebAssembl
urlPrefix: https://heycam.github.io/webidl/; spec: WebIDL
type: dfn
text: create a namespace object; url: create-a-namespace-object
urlPrefix: https://tc39.es/proposal-resizablearraybuffer/; spec: ResizableArrayBuffer proposal
type: interface; for: ResizableArrayBuffer proposal
text: ResizableArrayBuffer; url: sec-resizablearraybuffer-objects
type: dfn
text: handled; url: sec-hostresizearraybuffer
text: IsResizableArrayBuffer; url: sec-isresizablearraybuffer
text: HostResizeArrayBuffer; url: sec-hostresizearraybuffer
</pre>

<pre class='link-defaults'>
Expand Down Expand Up @@ -625,7 +633,9 @@ dictionary MemoryDescriptor {
interface Memory {
constructor(MemoryDescriptor descriptor);
unsigned long grow([EnforceRange] unsigned long delta);
readonly attribute ArrayBuffer buffer;
ArrayBuffer toFixedLengthBuffer();
ResizableArrayBuffer toResizableBuffer();
readonly attribute (ArrayBuffer or ResizableArrayBuffer) buffer;
};
</pre>

Expand All @@ -634,22 +644,34 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
{{Memory}} object has the following internal slots:

* \[[Memory]] : a [=memory address=]
* \[[BufferObject]] : an {{ArrayBuffer}} whose [=Data Block=] is [=identified with=] the above memory address
* \[[BufferObject]] : an {{ArrayBuffer}} or {{ResizableArrayBuffer}} whose [=Data Block=] is [=identified with=] the above memory address

<div algorithm>
To <dfn>create a memory buffer</dfn> from a [=memory address=] |memaddr|, perform the following steps:
To <dfn>create a fixed length memory buffer</dfn> from a [=memory address=] |memaddr|, perform the following steps:

1. Let |block| be a [=Data Block=] which is [=identified with=] the underlying memory of |memaddr|.
1. Let |buffer| be a new {{ArrayBuffer}} whose \[[ArrayBufferData]] is |block| and \[[ArrayBufferByteLength]] is set to the length of |block|.
1. Set |buffer|.\[[ArrayBufferDetachKey]] to "WebAssembly.Memory".
1. Return |buffer|.
</div>

<div algorithm>
To <dfn>create a resizable memory buffer</dfn> from a [=memory address=] |memaddr| and a |maxsize|, perform the following steps:

1. Let |block| be a [=Data Block=] which is [=identified with=] the underlying memory of |memaddr|.
1. Let |length| be the length of |block|.
1. If |maxsize| &gt; (65536 &times; 65536),
1. Throw a {{RangeError}} exception.
1. Let |buffer| be a new {{ResizableArrayBuffer}} whose \[[ArrayBufferData]] is |block|, \[[ArrayBufferByteLength]] is set to |length|, and \[[ArrayBufferMaxByteLength]] is |maxsize|.
1. Set |buffer|.\[[ArrayBufferDetachKey]] to "WebAssembly.Memory".
1. Return |buffer|.
</div>

<div algorithm>
To <dfn>initialize a memory object</dfn> |memory| from a [=memory address=] |memaddr|, perform the following steps:
1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=].
1. Assert: |map|[|memaddr|] doesn't [=map/exist=].
1. Let |buffer| be the result of [=create a memory buffer|creating a memory buffer=] from |memaddr|.
1. Let |buffer| be the result of [=create a fixed length memory buffer|creating a fixed length memory buffer=] from |memaddr|.
1. Set |memory|.\[[Memory]] to |memaddr|.
1. Set |memory|.\[[BufferObject]] to |buffer|.
1. [=map/Set=] |map|[|memaddr|] to |memory|.
Expand Down Expand Up @@ -679,36 +701,97 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
</div>

<div algorithm>
To <dfn>reset the Memory buffer</dfn> of |memaddr|, perform the following steps:
To <dfn>refresh the Memory buffer</dfn> of |memaddr|, perform the following steps:

1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=].
1. Assert: |map|[|memaddr|] [=map/exists=].
1. Let |memory| be |map|[|memaddr|].
1. Perform ! [=DetachArrayBuffer=](|memory|.\[[BufferObject]], "WebAssembly.Memory").
1. Let |buffer| be the result of [=create a memory buffer|creating a memory buffer=] from |memaddr|.
1. Set |memory|.\[[BufferObject]] to |buffer|.
1. Let |buffer| be |memory|.\[[BufferObject]].
1. If [=IsResizableArrayBuffer=](|buffer|) is false,
1. Perform ! [=DetachArrayBuffer=](|buffer|, "WebAssembly.Memory").
1. Let |buffer| be the result of [=create a fixed length memory buffer|creating a fixed length memory buffer=] from |memaddr|.
1. Set |memory|.\[[BufferObject]] to |buffer|.
1. Otherwise,
1. Let |block| be a [=Data Block=] which is [=identified with=] the underlying memory of |memaddr|.
1. Set |buffer|.\[[ArrayBufferData]] to |block|.
1. Set |buffer|.\[[ArrayBufferByteLength]] to the length of |block|.
</div>

<div algorithm=dom-Memory-grow>
The <dfn method for="Memory">grow(|delta|)</dfn> method, when invoked, performs the following steps:
<div algorithm>
To <dfn>grow the memory buffer</dfn> associated with a [=memory address=] |memaddr| by |delta|, perform the following steps:

1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let |memaddr| be **this**.\[[Memory]].
1. Let |ret| be the [=mem_size=](|store|, |memaddr|).
1. Let |store| be [=mem_grow=](|store|, |memaddr|, |delta|).
1. If |store| is [=error=], throw a {{RangeError}} exception.
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
1. [=Reset the memory buffer=] of |memaddr|.
1. [=Refresh the memory buffer=] of |memaddr|.
1. Return |ret|.
</div>

<div algorithm=dom-Memory-grow>
The <dfn method for="Memory">grow(|delta|)</dfn> method, when invoked, performs the following steps:
1. Let |memaddr| be **this**.\[[Memory]].
1. Return the result of [=grow the memory buffer|growing the memory buffer=] associated with |memaddr| by |delta|.
</div>

Immediately after a WebAssembly [=memory.grow=] instruction executes, perform the following steps:

<div algorithm="memory.grow">
1. If the top of the stack is not [=i32.const=] (−1),
1. Let |frame| be the [=current frame=].
1. Assert: due to validation, |frame|.[=frame/module=].[=moduleinst/memaddrs=][0] exists.
1. Let |memaddr| be the memory address |frame|.[=frame/module=].[=moduleinst/memaddrs=][0].
1. [=Reset the memory buffer=] of |memaddr|.
1. [=Refresh the memory buffer=] of |memaddr|.
</div>

<div algorithm=dom-Memory-toFixedLengthBuffer>
The <dfn method for="Memory">toFixedLengthBuffer()</dfn> method, when invoked, performs the following steps:
1. Let |buffer| be **this**.\[[BufferObject]].
1. If [=IsResizableArrayBuffer=](|buffer|) is false, return |buffer|.
1. Let |memaddr| be **this**.\[[Memory]].
1. Let |fixedBuffer| be the result of [=create a fixed length memory buffer|creating a fixed length memory buffer=] from |memaddr|.
1. Perform ! [=DetachArrayBuffer=](|buffer|, "WebAssembly.Memory").
1. Set **this**.\[[BufferObject]] to |fixedBuffer|.
1. Return |fixedBuffer|.
</div>

<div algorithm=dom-Memory-toResizableBuffer>
The <dfn method for="Memory">toResizableBuffer()</dfn> method, when invoked, performs the following steps:
1. Let |buffer| be **this**.\[[BufferObject]].
1. If [=IsResizableArrayBuffer=](|buffer|) is true, return |buffer|.
1. Let |memaddr| be **this**.\[[Memory]].
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let |memtype| be [=mem_type=](|store|, |memaddr|).
1. If |memtype| has a max,
1. Let |maxsize| be the max value in |memtype|.
1. Otherwise,
1. Let |maxsize| be an implementation-defined value &ge; |buffer|.\[[ArrayBufferByteLength]] and &lt; (65536 &times; 65536).

Note: The implementation-defined value above is the same across all invocations of this method.
syg marked this conversation as resolved.
Show resolved Hide resolved

1. Let |resizableBuffer| be the result of [=create a resizable memory buffer|creating a resizable memory buffer=] from |memaddr| and |maxsize|.
1. Perform ! [=DetachArrayBuffer=](|buffer|, "WebAssembly.Memory").
1. Set **this**.\[[BufferObject]] to |resizableBuffer|.
1. Return |resizableBuffer|.
</div>

{{ResizableArrayBuffer}} objects returned by a {{Memory}} object must use the following definition of [=HostResizeArrayBuffer=].
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think of host hooks as being uniform across the agent. Maybe it makes more sense to use an internal slot where hosts can write a method, rather than a host hook, if this varies per instance.

Anyway, the idea that the Wasm ResizableArrayBuffers round up to the next Wasm page when you grow them (and invoke the "grow the memory buffer" algorithm) sounds perfect to me!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an interesting idea for hosts to write a method. It'll be a new editorial convention.

Do I understand the nit correctly that you see this as an implementation hook instead of a host hook, thus the suggestion?


<div algorithm>
The <dfn>abstract operation [=HostResizeArrayBuffer=]</dfn> takes arguments |buffer| and |newLength|. It performs the following steps when called.

1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=].
1. Assert: |buffer| is the \[[BufferObject]] of exactly one value in |map|.
1. [=map/iterate|For each=] |memaddr| &rarr; |mem| in |map|,
1. If [=SameValue=](mem.\[[BufferObject]], |buffer|) is true,
1. Assert: |buffer|.\[[ArrayBufferByteLength]] modulo 65536 is 0.
1. Let |lengthDelta| be |newLength| - |buffer|.\[[ArrayBufferByteLength]].
1. If |lengthDelta| &lt; 0 or |lengthDelta| modulo 65536 is not 0,
1. Throw a {{RangeError}} exception.
1. Let |delta| be |lengthDelta| &div; 65536.
1. [=grow the memory buffer|Grow the memory buffer=] associated with |memaddr| by |delta|.
1. Return handled.
</div>

<div algorithm>
Expand Down