Skip to content

Commit 68764dc

Browse files
authored
[js-api] Add support for resizable ArrayBuffers and growable SharedArrayBuffers (#208)
* Merge remote-tracking branch 'syg/spec/rab-js-api-integration' into add-RAB-support This adds support for ResizableArrayBuffers and GrowableSharedArrayBuffers, by adding and extending the changes in rab-js-api-integration [1] to include cases for SharedArrayBuffer. Also adds a TODO for the steps of the new HostGrowSharedArrayBuffer algorithm. All this could potentially be refactored to be more concise. [1] WebAssembly/spec#1300 * Add WIP algorithm for HostGrowSharedArrayBuffer and stylistic changes to match the style in the spec PR adding resizable buffers. * Fixed HostGrowSharedArrayBuffer algorithm notation, added TODOs to not forget to check these cases. * Moved TODO item to the correct location.
1 parent b15f313 commit 68764dc

1 file changed

Lines changed: 147 additions & 14 deletions

File tree

document/js-api/index.bs

Lines changed: 147 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
4444
text: NativeError Object Structure; url: sec-nativeerror-object-structure
4545
text: 𝔽; url: #𝔽
4646
text: ℤ; url: #ℤ
47+
text: SameValue; url: sec-samevalue
4748
urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: dfn
4849
url: valid/modules.html#valid-module
4950
text: valid
@@ -101,6 +102,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
101102
text: memory address; url: exec/runtime.html#syntax-memaddr
102103
text: global address; url: exec/runtime.html#syntax-globaladdr
103104
text: extern address; url: exec/runtime.html#syntax-externaddr
105+
text: page size; url: exec/runtime.html#page-size
104106
url: syntax/types.html#syntax-numtype
105107
text: i32
106108
text: i64
@@ -147,6 +149,11 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
147149
urlPrefix: https://heycam.github.io/webidl/; spec: WebIDL
148150
type: dfn
149151
text: create a namespace object; url: create-a-namespace-object
152+
urlPrefix: https://tc39.es/proposal-resizablearraybuffer/; spec: ResizableArrayBuffer proposal
153+
type: dfn
154+
text: IsFixedLengthArrayBuffer; url: sec-isfixedarraybuffer
155+
text: HostResizeArrayBuffer; url: sec-hostresizearraybuffer
156+
text: HostGrowSharedArrayBuffer; url: sec-hostgrowsharedarraybuffer
150157
</pre>
151158

152159
<pre class='link-defaults'>
@@ -595,6 +602,8 @@ dictionary MemoryDescriptor {
595602
interface Memory {
596603
constructor(MemoryDescriptor descriptor);
597604
unsigned long grow([EnforceRange] unsigned long delta);
605+
ArrayBuffer toFixedLengthBuffer();
606+
ArrayBuffer toResizableBuffer();
598607
readonly attribute (ArrayBuffer or SharedArrayBuffer) buffer;
599608
};
600609
</pre>
@@ -607,14 +616,44 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
607616
* \[[BufferObject]] : an {{ArrayBuffer}} or {{SharedArrayBuffer}} whose [=Data Block=] is [=identified with=] the above memory address
608617

609618
<div algorithm>
610-
To <dfn>create a memory buffer</dfn> from a [=memory address=] |memaddr|, perform the following steps:
619+
To <dfn>create a fixed length memory buffer</dfn> from a [=memory address=] |memaddr|, perform the following steps:
620+
621+
1. Let |block| be a [=Data Block=] which is [=identified with=] the underlying memory of |memaddr|.
622+
1. If |block| is a [=Shared Data Block=],
623+
1. Let |buffer| be a new {{SharedArrayBuffer}} with the internal slots \[[ArrayBufferData]] and \[[ArrayBufferByteLength]].
624+
1. Set |buffer|.\[[ArrayBufferData]] to |block|.
625+
1. Set |buffer|.\[[ArrayBufferByteLength]] to the length of |block|.
626+
1. Perform ! [$SetIntegrityLevel$](|buffer|, `"frozen"`).
627+
1. Otherwise,
628+
1. Let |buffer| be a new {{ArrayBuffer}} with the internal slots \[[ArrayBufferData]], \[[ArrayBufferByteLength]], and \[[ArrayBufferDetachKey]].
629+
1. Set |buffer|.\[[ArrayBufferData]] to |block|.
630+
1. Set |buffer|.\[[ArrayBufferByteLength]] to the length of |block|.
631+
1. Set |buffer|.\[[ArrayBufferDetachKey]] to "WebAssembly.Memory".
632+
1. Return |buffer|.
633+
</div>
634+
635+
Note: (TODO) Check whether only SharedArrayBuffers need integrity level "frozen" (see the Threads Proposal Overview).
636+
637+
To create a resizable memory buffer we perform the same steps as above, but also setting the buffer's maximum size.
638+
639+
<div algorithm>
640+
To <dfn>create a resizable memory buffer</dfn> from a [=memory address=] |memaddr| and a |maxsize|, perform the following steps:
611641

612642
1. Let |block| be a [=Data Block=] which is [=identified with=] the underlying memory of |memaddr|.
643+
1. Let |length| be the length of |block|.
644+
1. If |maxsize| &gt; (65536 &times; 65536),
645+
1. Throw a {{RangeError}} exception.
613646
1. If |block| is a [=Shared Data Block=],
614-
1. Let |buffer| be a new {{SharedArrayBuffer}} whose \[[ArrayBufferData]] is |block| and \[[ArrayBufferByteLength]] is set to the length of |block|.
647+
1. Let |buffer| be a new {{SharedArrayBuffer}} with the internal slots \[[ArrayBufferData]], \[[ArrayBufferByteLength]], and \[[ArrayBufferMaxByteLength]].
648+
1. Set |buffer|.\[[ArrayBufferData]] to |block|.
649+
1. Set |buffer|.\[[ArrayBufferByteLength]] to the length of |block|.
650+
1. Set |buffer|.\[[ArrayBufferMaxByteLength]] to |maxsize|.
615651
1. Perform ! [$SetIntegrityLevel$](|buffer|, `"frozen"`).
616652
1. Otherwise,
617-
1. Let |buffer| be a new {{ArrayBuffer}} whose \[[ArrayBufferData]] is |block| and \[[ArrayBufferByteLength]] is set to the length of |block|.
653+
1. Let |buffer| be a new {{ArrayBuffer}} with the internal slots \[[ArrayBufferData]], \[[ArrayBufferByteLength]], \[[ArrayBufferMaxByteLength]], and \[[ArrayBufferDetachKey]].
654+
1. Set |buffer|.\[[ArrayBufferData]] to |block|.
655+
1. Set |buffer|.\[[ArrayBufferByteLength]] to |length|.
656+
1. Set |buffer|.\[[ArrayBufferMaxByteLength]] to |maxsize|.
618657
1. Set |buffer|.\[[ArrayBufferDetachKey]] to "WebAssembly.Memory".
619658
1. Return |buffer|.
620659
</div>
@@ -623,7 +662,7 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
623662
To <dfn>initialize a memory object</dfn> |memory| from a [=memory address=] |memaddr|, perform the following steps:
624663
1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=].
625664
1. Assert: |map|[|memaddr|] doesn't [=map/exist=].
626-
1. Let |buffer| be the result of [=create a memory buffer|creating a memory buffer=] from |memaddr|.
665+
1. Let |buffer| be the result of [=create a fixed length memory buffer|creating a fixed length memory buffer=] from |memaddr|.
627666
1. Set |memory|.\[[Memory]] to |memaddr|.
628667
1. Set |memory|.\[[BufferObject]] to |buffer|.
629668
1. [=map/Set=] |map|[|memaddr|] to |memory|.
@@ -655,39 +694,133 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
655694
</div>
656695

657696
<div algorithm>
658-
To <dfn>reset the Memory buffer</dfn> of |memaddr|, perform the following steps:
697+
To <dfn>refresh the Memory buffer</dfn> of |memaddr|, perform the following steps:
659698

660699
1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=].
661700
1. Assert: |map|[|memaddr|] [=map/exists=].
662701
1. Let |memory| be |map|[|memaddr|].
663-
1. If |memory|.\[[BufferObject]] is an {{ArrayBuffer}},
664-
1. Perform [=!=] [$DetachArrayBuffer$](|memory|.\[[BufferObject]], "WebAssembly.Memory").
665-
1. Let |buffer| be the result of [=create a memory buffer|creating a memory buffer=] from |memaddr|.
666-
1. Set |memory|.\[[BufferObject]] to |buffer|.
702+
1. Let |buffer| be |memory|.\[[BufferObject]].
703+
1. If [=IsFixedLengthArrayBuffer=](|buffer|) is true,
704+
1. If |memory|.\[[BufferObject]] is an {{ArrayBuffer}},
705+
1. Perform [=!=] [$DetachArrayBuffer$](|buffer|, "WebAssembly.Memory").
706+
1. Let |buffer| be the result of [=create a fixed length memory buffer|creating a fixed length memory buffer=] from |memaddr|.
707+
1. Set |memory|.\[[BufferObject]] to |buffer|.
708+
1. Otherwise,
709+
1. Let |block| be a [=Data Block=] which is [=identified with=] the underlying memory of |memaddr|.
710+
1. Set |buffer|.\[[ArrayBufferData]] to |block|.
711+
1. Set |buffer|.\[[ArrayBufferByteLength]] to the length of |block|.
667712
</div>
668713

669-
<div algorithm=dom-Memory-grow>
670-
The <dfn method for="Memory">grow(|delta|)</dfn> method, when invoked, performs the following steps:
714+
<div algorithm>
715+
To <dfn>grow the memory buffer</dfn> associated with a [=memory address=] |memaddr| by |delta|, perform the following steps:
716+
671717
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
672-
1. Let |memaddr| be **this**.\[[Memory]].
673718
1. Let |ret| be the [=mem_size=](|store|, |memaddr|).
674719
1. Let |store| be [=mem_grow=](|store|, |memaddr|, |delta|).
675720
1. If |store| is [=error=], throw a {{RangeError}} exception.
676721
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
677-
1. [=Reset the memory buffer=] of |memaddr|.
722+
1. [=Refresh the memory buffer=] of |memaddr|.
678723
1. Return |ret|.
679724
</div>
680725

726+
<div algorithm=dom-Memory-grow>
727+
The <dfn method for="Memory">grow(|delta|)</dfn> method, when invoked, performs the following steps:
728+
1. Let |memaddr| be **this**.\[[Memory]].
729+
1. Return the result of [=grow the memory buffer|growing the memory buffer=] associated with |memaddr| by |delta|.
730+
</div>
731+
681732
Immediately after a WebAssembly [=memory.grow=] instruction executes, perform the following steps:
682733

683734
<div algorithm="memory.grow">
684735
1. If the top of the stack is not [=i32.const=] (−1),
685736
1. Let |frame| be the [=current frame=].
686737
1. Assert: due to validation, |frame|.[=frame/module=].[=moduleinst/memaddrs=][0] exists.
687738
1. Let |memaddr| be the memory address |frame|.[=frame/module=].[=moduleinst/memaddrs=][0].
688-
1. [=Reset the memory buffer=] of |memaddr|.
739+
1. [=Refresh the memory buffer=] of |memaddr|.
740+
</div>
741+
742+
<div algorithm=dom-Memory-toFixedLengthBuffer>
743+
The <dfn method for="Memory">toFixedLengthBuffer()</dfn> method, when invoked, performs the following steps:
744+
1. Let |buffer| be **this**.\[[BufferObject]].
745+
1. Let |memaddr| be **this**.\[[Memory]].
746+
1. If |buffer| is an {{ArrayBuffer}},
747+
1. If [=IsFixedLengthArrayBuffer=](|buffer|) is true, return |buffer|.
748+
1. Otherwise,
749+
1. Let |fixedBuffer| be the result of [=create a fixed length memory buffer|creating a fixed length memory buffer=] from |memaddr|.
750+
1. Perform [=!=] [$DetachArrayBuffer$](|buffer|, "WebAssembly.Memory").
751+
1. Set **this**.\[[BufferObject]] to |fixedBuffer|.
752+
1. Return |fixedBuffer|.
753+
1. Otherwise,
754+
1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=].
755+
1. Assert: |map|[|memaddr|] [=map/exists=].
756+
1. Let |newMemory| be |map|[|memaddr|].
757+
1. Let |newBufferObject| be |newMemory|.\[[BufferObject]].
758+
1. Set **this**.\[[BufferObject]] to |newBufferObject|.
759+
1. Return |newBufferObject|.
760+
</div>
761+
762+
<div algorithm=dom-Memory-toResizableBuffer>
763+
The <dfn method for="Memory">toResizableBuffer()</dfn> method, when invoked, performs the following steps:
764+
1. Let |buffer| be **this**.\[[BufferObject]].
765+
1. If [=IsFixedLengthArrayBuffer=](|buffer|) is false, return |buffer|.
766+
1. Let |memaddr| be **this**.\[[Memory]].
767+
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
768+
1. Let |memtype| be [=mem_type=](|store|, |memaddr|).
769+
1. If |memtype| has a max,
770+
1. Let |maxsize| be the max value in |memtype|.
771+
1. Otherwise,
772+
1. Let |maxsize| be 65536 &times; 65536.
773+
1. Let |resizableBuffer| be the result of [=create a resizable memory buffer|creating a resizable memory buffer=] from |memaddr| and |maxsize|.
774+
1. If |buffer| is an {{ArrayBuffer}},
775+
1. Perform [=!=] [$DetachArrayBuffer$](|buffer|, "WebAssembly.Memory").
776+
1. Set **this**.\[[BufferObject]] to |resizableBuffer|.
777+
1. Return |resizableBuffer|.
778+
</div>
779+
780+
{{ArrayBuffer}} and {{SharedArrayBuffer}} objects returned by a {{Memory}} object must have size that is a multiple of a WebAssembly [=page size=] (the constant 65536). For this reason [=HostResizeArrayBuffer=] and [=HostGrowSharedArrayBuffer=] are redefined as follows.
781+
782+
<div algorithm>
783+
The <dfn>abstract operation [=HostResizeArrayBuffer=]</dfn> takes arguments an {{ArrayBuffer}} |buffer| and |newLength|. It performs the following steps when called.
784+
785+
1. If |buffer|.\[[ArrayBufferDetachKey]] is "WebAssembly.Memory",
786+
1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=].
787+
1. Assert: |buffer| is the \[[BufferObject]] of exactly one value in |map|.
788+
1. [=map/iterate|For each=] |memaddr| &rarr; |mem| in |map|,
789+
1. If [=SameValue=](mem.\[[BufferObject]], |buffer|) is true,
790+
1. Assert: |buffer|.\[[ArrayBufferByteLength]] modulo 65536 is 0.
791+
1. Let |lengthDelta| be |newLength| - |buffer|.\[[ArrayBufferByteLength]].
792+
1. If |lengthDelta| &lt; 0 or |lengthDelta| modulo 65536 is not 0,
793+
1. Throw a {{RangeError}} exception.
794+
1. Let |delta| be |lengthDelta| &div; 65536.
795+
1. [=grow the memory buffer|Grow the memory buffer=] associated with |memaddr| by |delta|.
796+
1. Return handled.
797+
1. Otherwise, return unhandled.
689798
</div>
690799

800+
<div algorithm>
801+
The <dfn>abstract operation [=HostGrowSharedArrayBuffer=]</dfn> takes arguments a {{SharedArrayBuffer}} |buffer| and |newLength|. It performs the following steps when called.
802+
803+
1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=].
804+
1. Assert: |buffer| is the \[[BufferObject]] of exactly one value in |map|.
805+
1. [=map/iterate|For each=] |memaddr| &rarr; |mem| in |map|,
806+
1. If [=SameValue=](mem.\[[BufferObject]], |buffer|) is true,
807+
1. Assert: |buffer|.\[[ArrayBufferByteLength]] modulo 65536 is 0.
808+
1. Let |lengthDelta| be |newLength| - |buffer|.\[[ArrayBufferByteLength]].
809+
1. If |lengthDelta| &lt; 0 or |lengthDelta| modulo 65536 is not 0,
810+
1. Throw a {{RangeError}} exception.
811+
1. Let |delta| be |lengthDelta| &div; 65536.
812+
1. [=grow the memory buffer|Grow the memory buffer=] associated with |memaddr| by |delta|.
813+
1. If |newLength| &lt; |buffer|.\[[ArrayBufferByteLength]] or |newLength| &gt; |buffer|.\[[ArrayBufferMaxByteLength]], throw a {{RangeError}} exception.
814+
1. Let |agentRecord| be the surrounding agent's [=Agent Record=].
815+
1. Let |isLittleEndian| be |agentRecord|.\[[LittleEndian]] .
816+
1. Let |event| be an implementation defined [=ReadModifyWriteSharedMemory=] event, whose \[[Order]] is "SeqCst", \[[Payload]] is [§NumericToRawBytes$](Bigint, |newLength|, |isLittleEndian|), \[[Block]] is |buffer|.\[[ArrayBufferByteLengthData]], \[[ByteIndex]] is 0, and \[[ElementSize]] is 8.
817+
1. Let |eventsRecord| be the [=Agent Events Record|EventsRecord=] in |agentRecord|.\[[CandidateExecution]].\[[EventsRecords]] that corresponds to the surrounding agent's signifier |agentRecord|.\[[Signifier]].
818+
1. Append |event| to |eventsRecord|.\[[EventsList]].
819+
1. Return handled.
820+
</div>
821+
822+
Note: (TODO) Check the error case of memory.grow on the Wasm side.
823+
691824
<div algorithm>
692825
The getter of the <dfn attribute for="Memory">buffer</dfn> attribute of {{Memory}} perfoms the following steps:
693826

0 commit comments

Comments
 (0)