Skip to content

Initial addition of builtin steps #39

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

Merged
merged 16 commits into from
Aug 21, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 240 additions & 15 deletions document/js-api/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
text: 𝔽; url: #𝔽
text: ℤ; url: #ℤ
text: SameValue; url: sec-samevalue
text: IsStrictlyEqual; url: sec-isstrictlyequal
text: IsLessThan; url: sec-islessthan
text: String.fromCharCode; url: sec-string.fromcharcode
text: String.fromCodePoint; url: sec-string.fromcodepoint
text: String.prototype.charCodeAt; url: sec-string.prototype.charcodeat
text: String.prototype.codePointAt; url: sec-string.prototype.codepointat
text: String.prototype.concat; url: sec-string.prototype.concat
text: String.prototype.substring; url: sec-string.prototype.substring
urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: dfn
url: valid/modules.html#valid-module
text: valid
Expand Down Expand Up @@ -358,8 +366,8 @@ Note:
1. Let |stableBytes| be a [=get a copy of the buffer source|copy of the bytes held by the buffer=] |bytes|.
1. [=Compile a WebAssembly module|Compile=] |stableBytes| as a WebAssembly module and store the results as |module|.
1. If |module| is [=error=], return false.
1. Let |builtinSetNames| be options["builtins"]
1. Let |importedStringModule| be options["importedStringConstants"]
1. Let |builtinSetNames| be |options|["builtins"]
1. Let |importedStringModule| be |options|["importedStringConstants"]
1. If [=validate builtins and imported string for a WebAssembly module|validating builtins and imported strings=] for |module| with |builtinSetNames| and |importedStringModule| returns false, return false.
1. Return true.
</div>
Expand Down Expand Up @@ -390,8 +398,8 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje
1. [=compile a WebAssembly module|Compile the WebAssembly module=] |bytes| and store the result as |module|.
1. [=Queue a task=] to perform the following steps. If |taskSource| was provided, queue the task on that task source.
1. If |module| is [=error=], reject |promise| with a {{CompileError}} exception.
1. Let |builtinSetNames| be options["builtins"]
1. Let |importedStringModule| be options["importedStringConstants"]
1. Let |builtinSetNames| be |options|["builtins"]
1. Let |importedStringModule| be |options|["importedStringConstants"]
1. If [=validate builtins and imported string for a WebAssembly module|validating builtins and imported strings=] for |module| with |builtinSetNames| and |importedStringModule| is false, reject |promise| with a {{CompileError}} exception.
1. Otherwise,
1. [=Construct a WebAssembly module object=] from |module|, |bytes|, |builtinSetNames|, |importedStringModule|, and let |moduleObject| be the result.
Expand Down Expand Up @@ -686,8 +694,8 @@ interface Module {
1. Let |stableBytes| be a [=get a copy of the buffer source|copy of the bytes held by the buffer=] |bytes|.
1. [=Compile a WebAssembly module|Compile the WebAssembly module=] |stableBytes| and store the result as |module|.
1. If |module| is [=error=], throw a {{CompileError}} exception.
1. Let |builtinSetNames| be options["builtins"]
1. Let |importedStringModule| be options["importedStringConstants"]
1. Let |builtinSetNames| be |options|["builtins"]
1. Let |importedStringModule| be |options|["importedStringConstants"]
1. If [=validate builtins and imported string for a WebAssembly module|validating builtins and imported strings=] for |module| with |builtinSetNames| and |importedStringModule| returns false, throw a {{CompileError}} exception.
1. Set **this**.\[[Module]] to |module|.
1. Set **this**.\[[Bytes]] to |stableBytes|.
Expand Down Expand Up @@ -1753,7 +1761,7 @@ To <dfn>validate builtin set names</dfn> with |builtinSetNames|, perform the fol
</div>

<div algorithm>
To <dfn>create a builtin function</dfn> from type |functype| and execution steps |steps|, perform the following steps:
To <dfn>create a builtin function</dfn> from type |funcType| and execution steps |steps|, perform the following steps:

1. Let |stored settings| be the <a spec=HTML>incumbent settings object</a>.
1. Let |hostfunc| be a [=host function=] which executes |steps| when called.
Expand All @@ -1769,8 +1777,8 @@ To <dfn>instantiate a builtin set</dfn> with name |builtinSetName|, perform the
1. Let |builtins| be the result of [=get the builtins for a builtin set=] |builtinSetName|
1. Let |exportsObject| be [=!=] [$OrdinaryObjectCreate$](null).
1. If |externtype| is of the form [=external-type/func=] <var ignore>functype</var>,
1. [=list/iterate|For each=] (|name|, |funcType|, |algorithm|) of |builtins|,
1. Let |funcaddr| be the result fo [=create a builtin function=] with |funcType| and |algorithm|
1. [=list/iterate|For each=] (|name|, |funcType|, |steps|) of |builtins|,
1. Let |funcaddr| be the result fo [=create a builtin function=] with |funcType| and |steps|
1. Let |func| be the result of creating [=a new Exported Function=] from |funcaddr|.
1. Let |value| be |func|.
1. Let |status| be [=!=] [$CreateDataProperty$](|exportsObject|, |name|, |value|).
Expand All @@ -1797,28 +1805,245 @@ To <dfn>validate an import for builtins</dfn> with |import|, enabled builtins |b

String builtins adapt the interface of the String builtin object. The import name for this set is `wasm:js-string`.

<div algorithm="js-string-unwrapString">
<h4 id="builtins-js-string-abstract-ops">Abstract operations</h4>
<div algorithm>

The <dfn>unwrapString(|v|)</dfn> method, when invoked, performs the following steps:
The <dfn abstract-op lt="UnwrapString">UnwrapString(|v|)</dfn> abstract operation, when invoked, performs the following steps:

1. If [=Type=](|v|) is not String
1. If [=Type=](|v|) is not [=String=]
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. Return |v|

</div>

<div algorithm>

The <dfn abstract-op lt="FromCharCode">FromCharCode(|v|)</dfn> abstract operation, when invoked, performs the following steps:

1. Assert: |v| is of type [=i32=].
1. Return [=!=] [$Call$]([=String.fromCharCode=], undefined, « [=ToJSValue=](|v|) »).

</div>

<div algorithm>

The <dfn abstract-op lt="CharCodeAt ">CharCodeAt(|string|, |index|)</dfn> abstract operation, when invoked, performs the following steps:

1. Assert: |index| is of type [=i32=].
1. Return [=!=] [$Call$]([=String.prototype.charCodeAt=], |string|, « [=ToJSValue=](|index|) »).

</div>

<h4 id="js-string-cast">cast</h4>

The |funcType| of this builtin is `(func (param externref) (result externref))`.

<div algorithm="js-string-cast">
When this builtin is invoked with parameter |v|, the following steps must be run:

1. Return [=?=] [$UnwrapString$](|v|)

</div>

<h4 id="js-string-test">test</h4>

The |funcType| of this builtin is `(func (param externref) (result i32))`.

<div algorithm="js-string-test">
When this builtin is invoked with parameter |v|, the following steps must be run:

1. If [=Type=](|v|) is not [=String=]
1. Return 0
1. Return 1

</div>

<h4 id="js-string-fromCharCodeArray">fromCharCodeArray</h4>

The |funcType| of this builtin is `(func (param (ref null (array (mut i16))) i32 i32) (result externref))`.

Note: This function only takes a mutable i16 array defined in its own recursion group.
If this is an issue for toolchains, we can look into how to relax the function type
while still maintaining good performance.
Comment on lines +1865 to +1867
Copy link
Collaborator

Choose a reason for hiding this comment

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

Lets drop the second line of "If this is an issue...good performance". It's not clear how we could do that at this point, and no one has seemed to complain.


<div algorithm="js-string-fromCharCodeArray">
When this builtin is invoked with parameters |array|, |start|, and |end|, the following steps must be run:

1. If |array| is null
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. Let |length| be the number of elements in |array|.
1. If |start| > |end| or |end| > |length|
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. Let |result| be the empty string.
1. Let |i| be |start|.
1. While |i| < |end|:
1. Let |charCode| be the value of the element stored at index |i| in |array|.
1. Let |charCodeString| be [$FromCharCode$](|charCode|).
1. Let |result| be the concatenation of |result| and |charCodeString|.
1. Set |i| to |i| + 1.
1. Return |result|.

</div>

<h4 id="js-string-intoCharCodeArray">intoCharCodeArray</h4>

The |funcType| of this builtin is `(func (param externref (ref null (array (mut i16))) i32) (result i32))`.

Note: This function only takes a mutable i16 array defined in its own recursion group.
If this is an issue for toolchains, we can look into how to relax the function type
Copy link
Collaborator

Choose a reason for hiding this comment

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

same as above.

while still maintaining good performance.

<div algorithm="js-string-intoCharCodeArray">
When this builtin is invoked with parameters |string|, |array|, and |start|, the following steps must be run:

1. If |array| is null
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. Let |string| be [=?=] [$UnwrapString$](|string|).
1. Let |stringLength| be the [=string/length=] of |string|.
1. Let |arrayLength| be the number of elements in |array|.
1. If |start| + |length| > |arrayLength|
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this needs to be |start| + |stringLength| here.

1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. Let |i| be 0.
1. While |i| < |stringLength|:
1. Let |charCode| be [$CharCodeAt$](|string|, |i|).
1. Set the element at index |start| + |i| in |array| to [=ToWebAssemblyValue=](|charCode|).
1. Set |i| to |i| + 1.
1. Return |stringLength|.

</div>


<h4 id="js-string-fromCharCode">fromCharCode</h4>

The |funcType| of this builtin is `(func (param i32) (result externref))`.

<div algorithm="js-string-fromCharCode">
When this builtin is invoked with parameter |v|, the following steps must be run:

1. Return [$FromCharCode$](|v|).

</div>

<h4 id="js-string-fromCodePoint">fromCodePoint</h4>

The |funcType| of this builtin is `(func (param i32) (result externref))`.

<div algorithm="js-string-fromCodePoint">
When this builtin is invoked with parameter |v|, the following steps must be run:

1. If |v| &gt; 0x10ffff
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. Return [=!=] [$Call$]([=String.fromCodePoint=], undefined, « [=ToJSValue=](|v|) »).

</div>

<h4 id="js-string-charCodeAt">charCodeAt</h4>

The type of this function is `(func (param externref i32) (result i32))`.

<div algorithm="js-string-charCodeAt">
When this builtin is invoked, the following steps must be run:
When this builtin is invoked with parameters |string| and |index|, the following steps must be run:

1. Let |string| be [=?=] [$UnwrapString$](|string|).
1. Let |length| be the [=string/length=] of |string|.
1. If |index| >= |length|
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. Return [$CharCodeAt$](|string|, |index|).

</div>

<h4 id="js-string-codePointAt">codePointAt</h4>

The type of this function is `(func (param externref i32) (result i32))`.

<div algorithm="js-string-codePointAt">
When this builtin is invoked with parameters |string| and |index|, the following steps must be run:

1. Let |string| be [=?=] [$UnwrapString$](|string|).
1. Let |length| be the [=string/length=] of |string|.
1. If |index| >= |length|
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. Return [=!=] [$Call$]([=String.prototype.codePointAt=], |string|, « [=ToJSValue=](|index|) »).

</div>

<h4 id="js-string-length">length</h4>

The |funcType| of this builtin is `(func (param externref) (result i32))`.

<div algorithm="js-string-length">
When this builtin is invoked with parameter |v|, the following steps must be run:

1. Let |string| be [=?=] [$UnwrapString$](|v|).
1. Return the [=string/length=] of |string|.

</div>

1. Let |string| be [$unwrapString$](param0)
1. TODO
<h4 id="js-string-concat">concat</h4>

The |funcType| of this builtin is `(func (param externref externref) (result externref))`.

<div algorithm="js-string-concat">
When this builtin is invoked with parameters |first| and |second|, the following steps must be run:

1. Let |first| be [=?=] [$UnwrapString$](|first|).
1. Let |second| be [=?=] [$UnwrapString$](|second|).
1. Return [=!=] [$Call$]([=String.prototype.concat=], |first|, « |second| »).

</div>

<h4 id="js-string-substring">substring</h4>

The |funcType| of this builtin is `(func (param externref i32 i32) (result externref))`.

<div algorithm="js-string-substring">
When this builtin is invoked with parameters |string|, |start|, and |end|, the following steps must be run:

1. Let |string| be [=?=] [$UnwrapString$](|string|).
1. Let |length| be the [=string/length=] of |string|.
1. If |start| > |end| or |start| > |length|
1. Return the empty string.
1. Return [=!=] [$Call$]([=String.prototype.substring=], |string|, « [=ToJSValue=](|start|), [=ToJSValue=](|end|) »).

</div>

<h4 id="js-string-equals">equals</h4>

The |funcType| of this builtin is `(func (param externref externref) (result i32))`.

Note: Explicitly allow null strings to be compared for equality as that is meaningful.

<div algorithm="js-string-equals">

When this builtin is invoked with parameters |first| and |second|, the following steps must be run:

1. If |first| is not null and [=Type=](|first|) is not [=String=]
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. If |second| is not null and [=Type=](|second|) is not [=String=]
1. Throw a {{RuntimeError}} exception. TODO: this needs to not be catchable, like a trap.
1. If [=!=] [=IsStrictlyEqual=](|first|, |second|) is true
1. Return 1.
1. Return 0.

</div>

<h4 id="js-string-compare">compare</h4>

The |funcType| of this builtin is `(func (param externref externref) (result i32))`.

<div algorithm="js-string-compare">

When this builtin is invoked with parameters |first| and |second|, the following steps must be run:

1. Let |first| be [=?=] [$UnwrapString$](|first|).
1. Let |second| be [=?=] [$UnwrapString$](|second|).
1. If [=!=] [=IsStrictlyEqual=](|first|, |second|) is true
1. Return 0.
1. If [=!=] [=IsLessThan=](|first|, |second|, true) is true
1. Return -1.
1. Return 1.

</div>

<h2 id="errors">Error Condition Mappings to JavaScript</h2>

Expand Down
Loading