Skip to content

Commit

Permalink
Update JS API to handle index types
Browse files Browse the repository at this point in the history
This introduces an IndexValue typedef, which is a union of Number and
BigInt, and two algorithms, IndexValueToU64 and U64ToIndexValue, which
can be used to convert between IndexValue and WebAssembly's u64 type
(used in the embedding spec).

It also makes several drive-by fixes and improvements.
  • Loading branch information
bvisness committed Aug 5, 2024
1 parent 5a8c4db commit d1e3c03
Showing 1 changed file with 86 additions and 46 deletions.
132 changes: 86 additions & 46 deletions document/js-api/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
text: SetFunctionLength; url: sec-setfunctionlength
text: the Number value; url: sec-ecmascript-language-types-number-type
text: is a Number; url: sec-ecmascript-language-types-number-type
text: is a BigInt; url: sec-ecmascript-language-types-bigint-type
text: NumberToRawBytes; url: sec-numbertorawbytes
text: Built-in Function Objects; url: sec-built-in-function-objects
text: NativeError Object Structure; url: sec-nativeerror-object-structure
Expand All @@ -61,6 +62,7 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
text: NativeError Object Structure; url: sec-nativeerror-object-structure
text: 𝔽; url: #𝔽
text: ℤ; url: #ℤ
text: mathematical value; url: #mathematical-value
text: SameValue; url: sec-samevalue
urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: dfn
url: valid/modules.html#valid-module
Expand Down Expand Up @@ -121,6 +123,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
text: match_valtype; url: appendix/embedding.html#embed-match-valtype
text: error; url: appendix/embedding.html#embed-error
text: store; url: exec/runtime.html#syntax-store
text: index type; url: syntax/types.html#syntax-idxtype
text: table type; url: syntax/types.html#syntax-tabletype
text: table address; url: exec/runtime.html#syntax-tableaddr
text: function address; url: exec/runtime.html#syntax-funcaddr
Expand All @@ -139,6 +142,9 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
text: exn_alloc; url: appendix/embedding.html#embed-exn-alloc
text: exn_tag; url: appendix/embedding.html#embed-exn-tag
text: exn_read; url: appendix/embedding.html#embed-exn-read
url: syntax/values.html#syntax-int
text: u32
text: u64
url: syntax/types.html#syntax-numtype
text: i32
text: i64
Expand Down Expand Up @@ -200,6 +206,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
urlPrefix: https://heycam.github.io/webidl/; spec: WebIDL
type: dfn
text: create a namespace object; url: create-a-namespace-object
text: [EnforceRange]; url: #EnforceRange
urlPrefix: https://webassembly.github.io/js-types/js-api/; spec: WebAssembly JS API (JS Type Reflection)
type: abstract-op; text: FromValueType; url: abstract-opdef-fromvaluetype
urlPrefix: https://tc39.es/proposal-resizablearraybuffer/; spec: ResizableArrayBuffer proposal
Expand Down Expand Up @@ -561,6 +568,8 @@ enum IndexType {
"i64",
};

typedef ([EnforceRange] unsigned long long or bigint) IndexValue;

dictionary ModuleExportDescriptor {
required USVString name;
required ImportExportKind kind;
Expand Down Expand Up @@ -665,15 +674,15 @@ Note: The use of this synchronous API is discouraged, as some implementations so

<pre class="idl">
dictionary MemoryDescriptor {
required [EnforceRange] unsigned long long initial;
[EnforceRange] unsigned long long maximum;
required IndexValue initial;
IndexValue maximum;
IndexType index;
};

[LegacyNamespace=WebAssembly, Exposed=*]
interface Memory {
constructor(MemoryDescriptor descriptor);
unsigned long grow([EnforceRange] unsigned long long delta);
unsigned long grow(IndexValue delta);
ArrayBuffer toFixedLengthBuffer();
ArrayBuffer toResizableBuffer();
readonly attribute ArrayBuffer buffer;
Expand Down Expand Up @@ -736,11 +745,11 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each

<div algorithm>
The <dfn constructor for="Memory">Memory(|descriptor|)</dfn> constructor, when invoked, performs the following steps:
1. Let |initial| be |descriptor|["initial"].
1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be |descriptor|["maximum"]; otherwise, let |maximum| be empty.
1. If |descriptor|["index"] [=map/exists=], let |indextype| be |descriptor|["index"]; otherwise, let |indextype| be "i32".
1. Let |initial| be [=?=] [=IndexValueToU64=](|descriptor|["initial"], |indextype|).
1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be [=?=] [=IndexValueToU64=](|descriptor|["maximum"], |indextype|); otherwise, let |maximum| be empty.
1. If |maximum| is not empty and |maximum| &lt; |initial|, throw a {{RangeError}} exception.
1. If |descriptior|["index"] [=map/exists=], let |index| be |descriptor|["index"]; otherwise, let |index| be "i32".
1. Let |memtype| be { min |initial|, max |maximum|, index |index| }.
1. Let |memtype| be (|indextype|, { **min** |initial|, **max** |maximum| }).
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let (|store|, |memaddr|) be [=mem_alloc=](|store|, |memtype|). If allocation fails, throw a {{RangeError}} exception.
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
Expand Down Expand Up @@ -779,7 +788,10 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
<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|.
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let |indextype| be the [=index type=] in [=mem_type=](|store|, |memaddr|).
1. Let |delta64| be [=?=] [=IndexValueToU64=](|delta|, |indextype|).
1. Return the result of [=grow the memory buffer|growing the memory buffer=] associated with |memaddr| by |delta64|.
</div>

Immediately after a WebAssembly [=memory.grow=] instruction executes, perform the following steps:
Expand Down Expand Up @@ -857,18 +869,18 @@ enum TableKind {

dictionary TableDescriptor {
required TableKind element;
required [EnforceRange] unsigned long long initial;
[EnforceRange] unsigned long long maximum;
required IndexValue initial;
IndexValue maximum;
IndexType index;
};

[LegacyNamespace=WebAssembly, Exposed=*]
interface Table {
constructor(TableDescriptor descriptor, optional any value);
unsigned long long grow([EnforceRange] unsigned long long delta, optional any value);
any get([EnforceRange] unsigned long long index);
undefined set([EnforceRange] unsigned long long index, optional any value);
readonly attribute unsigned long length;
IndexValue grow(IndexValue delta, optional any value);
any get(IndexValue index);
undefined set(IndexValue index, optional any value);
readonly attribute IndexValue length;
};
</pre>

Expand Down Expand Up @@ -896,19 +908,19 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address

<div algorithm>
The <dfn constructor for="Table">Table(|descriptor|, |value|)</dfn> constructor, when invoked, performs the following steps:
1. Let |elementType| be [=ToValueType=](|descriptor|["element"]).
1. If |elementType| is not a [=reftype=],
1. Throw a {{TypeError}} exception.
1. Let |initial| be |descriptor|["initial"].
1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be |descriptor|["maximum"]; otherwise, let |maximum| be empty.
1. Let |elementtype| be [=ToValueType=](|descriptor|["element"]).
1. If |elementtype| is not a [=reftype=],
1. [=Throw=] a {{TypeError}} exception.
1. If |descriptor|["index"] [=map/exists=], let |indextype| be |descriptor|["index"]; otherwise, let |indextype| be "i32".
1. Let |initial| be [=?=] [=IndexValueToU64=](|descriptor|["initial"], |indextype|).
1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be [=?=] [=IndexValueToU64=](|descriptor|["maximum"], |indextype|); otherwise, let |maximum| be empty.
1. If |maximum| is not empty and |maximum| &lt; |initial|, throw a {{RangeError}} exception.
1. If |value| is missing,
1. Let |ref| be [=DefaultValue=](|elementType|).
1. Let |ref| be [=DefaultValue=](|elementtype|).
1. Assert: |ref| is not [=error=].
1. Otherwise,
1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|).
1. If |descriptior|["index"] [=map/exists=], let |index| be |descriptor|["index"]; otherwise, let |index| be "i32".
1. Let |type| be the [=table type=] [=table type|index=] |index| {[=table type|min=] |initial|, [=table type|max=] |maximum|} [=table type|refType=] |elementType|.
1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementtype|).
1. Let |type| be the [=table type=] (|indextype|, { **min** |initial|, **max** |maximum| }, |elementtype|).
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let (|store|, |tableaddr|) be [=table_alloc=](|store|, |type|, |ref|). <!-- TODO(littledan): Report allocation failure https://github.com/WebAssembly/spec/issues/584 -->
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
Expand All @@ -920,13 +932,14 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address
1. Let |tableaddr| be **this**.\[[Table]].
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let |initialSize| be [=table_size=](|store|, |tableaddr|).
1. Let (<var ignore>limits</var>, |elementType|) be [=table_type=](|tableaddr|).
1. Let (|indextype|, <var ignore>limits</var>, |elementtype|) be [=table_type=](|store|, |tableaddr|).
1. Let |delta64| be [=?=] [=IndexValueToU64=](|delta|, |indextype|).
1. If |value| is missing,
1. Let |ref| be [=DefaultValue=](|elementType|).
1. Let |ref| be [=DefaultValue=](|elementtype|).
1. If |ref| is [=error=], throw a {{TypeError}} exception.
1. Otherwise,
1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|).
1. Let |result| be [=table_grow=](|store|, |tableaddr|, |delta|, |ref|).
1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementtype|).
1. Let |result| be [=table_grow=](|store|, |tableaddr|, |delta64|, |ref|).
1. If |result| is [=error=], throw a {{RangeError}} exception.

Note: The above exception can happen due to either insufficient memory or an invalid size parameter.
Expand All @@ -939,17 +952,20 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address
The getter of the <dfn attribute for="Table">length</dfn> attribute of {{Table}}, when invoked, performs the following steps:
1. Let |tableaddr| be **this**.\[[Table]].
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Return [=table_size=](|store|, |tableaddr|).
1. Let |indextype| be the [=index type=] in [=table_type=](|store|, |tableaddr|).
1. Let |length64| be [=table_size=](|store|, |tableaddr|).
1. Return [=U64ToIndexValue=](|length64|, |indextype|).
</div>

<div algorithm>
The <dfn method for="Table">get(|index|)</dfn> method, when invoked, performs the following steps:
1. Let |tableaddr| be **this**.\[[Table]].
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let (<var ignore>limits</var>, |elementType|) be [=table_type=](|store|, |tableaddr|).
1. If |elementType| is [=exnref=],
1. Let (|indextype|, <var ignore>limits</var>, |elementtype|) be [=table_type=](|store|, |tableaddr|).
1. If |elementtype| is [=exnref=],
1. Throw a {{TypeError}} exception.
1. Let |result| be [=table_read=](|store|, |tableaddr|, |index|).
1. Let |index64| be [=?=] [=IndexValueToU64=](|index|, |indextype|).
1. Let |result| be [=table_read=](|store|, |tableaddr|, |index64|).
1. If |result| is [=error=], throw a {{RangeError}} exception.
1. Return [=ToJSValue=](|result|).
</div>
Expand All @@ -958,16 +974,16 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address
The <dfn method for="Table">set(|index|, |value|)</dfn> method, when invoked, performs the following steps:
1. Let |tableaddr| be **this**.\[[Table]].
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let (<var ignore>limits</var>, |elementType|) be [=table_type=](|store|, |tableaddr|).
1. If |elementType| is [=exnref=],
1. Let (|indextype|, <var ignore>limits</var>, |elementtype|) be [=table_type=](|store|, |tableaddr|).
1. If |elementtype| is [=exnref=],
1. Throw a {{TypeError}} exception.
1. Let |index64| be [=?=] [=IndexValueToU64=](|index|, |indextype|).
1. If |value| is missing,
1. Let |ref| be [=DefaultValue=](|elementType|).
1. Let |ref| be [=DefaultValue=](|elementtype|).
1. If |ref| is [=error=], throw a {{TypeError}} exception.
1. Otherwise,
1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|).
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let |store| be [=table_write=](|store|, |tableaddr|, |index|, |ref|).
1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementtype|).
1. Let |store| be [=table_write=](|store|, |tableaddr|, |index64|, |ref|).
1. If |store| is [=error=], throw a {{RangeError}} exception.
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
</div>
Expand Down Expand Up @@ -1236,18 +1252,18 @@ The algorithm <dfn>ToJSValue</dfn>(|w|) coerces a [=WebAssembly value=] to a Jav
1. Assert: |w| is not of the form [=ref.exn=] <var ignore>exnaddr</var>.
1. If |w| is of the form [=i64.const=] |u64|,
1. Let |i64| be [=signed_64=](|u64|).
1. Return [=ℤ=](|i64| interpreted as a mathematical value).
1. If |w| is of the form [=i32.const=] |i32|,
1. Let |i32| be [=signed_32=](|i32|).
2. Return [=𝔽=](|i32| interpreted as a mathematical value).
1. Return [=ℤ=](|i64| interpreted as a [=mathematical value=]).
1. If |w| is of the form [=i32.const=] |u32|,
1. Let |i32| be [=signed_32=](|u32|).
2. Return [=𝔽=](|i32| interpreted as a [=mathematical value=]).
1. If |w| is of the form [=f32.const=] |f32|,
1. If |f32| is [=+∞=] or [=−∞=], return **+∞**<sub>𝔽</sub> or **-∞**<sub>𝔽</sub>, respectively.
1. If |f32| is [=nan=], return **NaN**.
1. Return [=𝔽=](|f32| interpreted as a mathematical value).
1. Return [=𝔽=](|f32| interpreted as a [=mathematical value=]).
1. If |w| is of the form [=f64.const=] |f64|,
1. If |f64| is [=+∞=] or [=−∞=], return **+∞**<sub>𝔽</sub> or **-∞**<sub>𝔽</sub>, respectively.
1. If |f64| is [=nan=], return **NaN**.
1. Return [=𝔽=](|f64| interpreted as a mathematical value).
1. Return [=𝔽=](|f64| interpreted as a [=mathematical value=]).
1. If |w| is of the form [=ref.null=] <var ignore>t</var>, return null.
1. If |w| is of the form [=ref.i31=] |u31|,
1. Let |i31| be [=signed_31=](|u31|).
Expand Down Expand Up @@ -1288,15 +1304,15 @@ The algorithm <dfn>ToWebAssemblyValue</dfn>(|v|, |type|) coerces a JavaScript va
1. If |type| is [=f32=],
1. Let |number| be [=?=] [$ToNumber$](|v|).
1. If |number| is **NaN**,
1. Let |n| be an implementation-defined integer such that [=canon=]<sub>32</sub> |n| < 2<sup>[=signif=](32)</sup>.
1. Let |n| be an implementation-defined integer such that [=canon=]<sub>32</sub> &leq; |n| &lt; 2<sup>[=signif=](32)</sup>.
1. Let |f32| be [=nan=](n).
1. Otherwise,
1. Let |f32| be |number| rounded to the nearest representable value using IEEE 754-2008 round to nearest, ties to even mode. [[IEEE-754]]
1. Return [=f32.const=] |f32|.
1. If |type| is [=f64=],
1. Let |number| be [=?=] [$ToNumber$](|v|).
1. If |number| is **NaN**,
1. Let |n| be an implementation-defined integer such that [=canon=]<sub>64</sub> |n| < 2<sup>[=signif=](64)</sup>.
1. Let |n| be an implementation-defined integer such that [=canon=]<sub>64</sub> &leq; |n| &lt; 2<sup>[=signif=](64)</sup>.
1. Let |f64| be [=nan=](n).
1. Otherwise,
1. Let |f64| be |number|.
Expand Down Expand Up @@ -1337,6 +1353,31 @@ The algorithm <dfn>ToWebAssemblyValue</dfn>(|v|, |type|) coerces a JavaScript va

</div>

<div algorithm>
The algorithm <dfn>IndexValueToU64</dfn>(|v|, |indextype|) asserts that a JavaScript value is the appropriate variant of {{IndexValue}} for an {{IndexType}}, and ensures that its value is in [=u64=] range for WebAssembly embedding operations, by performing the following steps:

1. If |indextype| is "i32",
1. If |v| [=is a Number=],
1. Assert: Due to WebIDL types and [=[EnforceRange]=], 0 ≤ [=ℝ=](|v|) < 2<sup>64</sup>.
1. Return [=ℝ=](|v|) as a WebAssembly [=u64=].
1. Otherwise, [=throw=] a {{TypeError}}.
1. Else if |indextype| is "i64",
1. If |v| [=is a BigInt=],
1. If |v| is not equal to [=!=] [$ToBigUint64$](|v|), [=throw=] a {{RangeError}}.
1. Return [=ℝ=](|v|) as a WebAssembly [=u64=].
1. Otherwise, [=throw=] a {{TypeError}}.
1. Assert: This step is not reached.

</div>

<div algorithm>
The algorithm <dfn>U64ToIndexValue</dfn>(|v|, |indextype|) converts a [=u64=] value from a WebAssembly embedding operation to the correct variant of {{IndexValue}} for an {{IndexType}}, by performing the following steps:

1. If |indextype| is "i32", return [=𝔽=](|v| interpreted as a [=mathematical value=]).
1. Else if |indextype| is "i64", return [=ℤ=](|v| interpreted as a [=mathematical value=]).
1. Assert: This step is not reached.

</div>

<h3 id="tags">Tags</h3>

Expand Down Expand Up @@ -1369,7 +1410,6 @@ To <dfn>initialize a Tag object</dfn> |tag| from a [=tag address=] |tagAddress|,
</div>

<div algorithm>

To <dfn>create a Tag object</dfn> from a [=tag address=] |tagAddress|, perform the following steps:

1. Let |map| be the [=surrounding agent=]'s associated [=Tag object cache=].
Expand Down

0 comments on commit d1e3c03

Please sign in to comment.