Skip to content
This repository was archived by the owner on Nov 3, 2021. It is now read-only.

[spec] JS API changes #8

Merged
merged 3 commits into from
Apr 12, 2018
Merged
Show file tree
Hide file tree
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
56 changes: 51 additions & 5 deletions document/js-api/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
text: 𝗂𝟥𝟤.𝖼𝗈𝗇𝗌𝗍
text: 𝖿𝟥𝟤.𝖼𝗈𝗇𝗌𝗍
text: 𝖿𝟨𝟦.𝖼𝗈𝗇𝗌𝗍
text: ref.null
text: ref.func
text: ref.host
text: function index; url: syntax/modules.html#syntax-funcidx
text: function instance; url: exec/runtime.html#function-instances
text: init_store; url: appendix/embedding.html#embed-init-store
Expand Down Expand Up @@ -132,11 +135,16 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
text: table address; url: exec/runtime.html#syntax-tableaddr
text: function address; url: exec/runtime.html#syntax-funcaddr
text: memory address; url: exec/runtime.html#syntax-memaddr
url: syntax/types.html#syntax-valtype
text: host address; url: exec/runtime.html#syntax-hostaddr
url: syntax/types.html#syntax-numtype
text: 𝗂𝟥𝟤
text: 𝗂𝟨𝟦
text: 𝖿𝟥𝟤
text: 𝖿𝟨𝟦
url: syntax/types.html#syntax-reftype
text: anyref
text: anyeqref
text: anyfunc
text: function element; url: exec/runtime.html#syntax-funcelem
text: import component; url: syntax/modules.html#imports
text: external value; url: exec/runtime.html#syntax-externval
Expand Down Expand Up @@ -223,6 +231,7 @@ Each [=agent=] is associated with the following [=ordered map=]s:
* The <dfn>Memory object cache</dfn>, mapping [=memory address=]es to {{Memory}} objects.
* The <dfn>Table object cache</dfn>, mapping [=table address=]es to {{Table}} objects.
* The <dfn>Exported Function cache</dfn>, mapping [=function address=]es to [=Exported Function=] objects.
* The <dfn>Host value cache</dfn>, mapping [=host address=]es to values.

<h2 id="webassembly-namespace">The WebAssembly Namespace</h2>

Expand Down Expand Up @@ -326,7 +335,7 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje
1. [=Append=] |externfunc| to |imports|.
1. If |externtype| is of the form [=𝗀𝗅𝗈𝖻𝖺𝗅=] |globaltype|,
1. If |globaltype| is [=𝗂𝟨𝟦=] or [=Type=](|v|) is not [=Number=], throw a {{LinkError}} exception.
1. Let |value| be [=ToWebAssemblyValue=](|v|, |globaltype|.<em>[=global type|valtype=]</em>)
1. Let |value| be [=ToWebAssemblyValue=](|v|, |globaltype|.<em>[=global type|valtype=]</em>, {{LinkError}})
1. Assert: |globaltype|.<em>[=global type|mut=]</em> is [=global type|𝖼𝗈𝗇𝗌𝗍=], as verified by WebAssembly validation.
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let (|store|, |globaladdr|) be [=alloc_global=](|store|, |globaltype|, |value|).
Expand Down Expand Up @@ -590,6 +599,8 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each

<pre class="idl">
enum TableKind {
"anyref",
"anyeqref",
"anyfunc",
// Note: More values may be added in future iterations,
// e.g., typed function references, typed GC references
Expand Down Expand Up @@ -742,7 +753,7 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [
1. For each type |t| of |parameters|,
1. If the length of |argValues| &gt; |i|, let |arg| be |argValues|[i].
1. Otherwise, let |arg| be undefined.
1. [=Append=] [=ToWebAssemblyValue=](|arg|, |t|) to |args|.
1. [=Append=] [=ToWebAssemblyValue=](|arg|, |t|, {{TypeError}}) to |args|.
1. Set |i| to |i| + 1.
1. Let (|store|, |ret|) be the result of [=invoke_func=](|store|, |funcaddr|, |args|).
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
Expand All @@ -767,7 +778,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not
1. Let |ret| be ? [=Call=](|func|, undefined, |jsArguments|). If an exception is thrown, trigger a WebAssembly trap, and propagate the exception to the enclosing JavaScript.
1. Let [|parameters|] → [|results|] be |functype|.
1. If |results| is empty, return undefined.
1. Otherwise, return [=ToWebAssemblyValue=](|ret|, |results|[0]).
1. Otherwise, return [=ToWebAssemblyValue=](|ret|, |results|[0], {{TypeError}}).
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let (|store|, |funcaddr|) be [=alloc_func=](|store|, |functype|, |hostfunc|).
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
Expand All @@ -781,14 +792,24 @@ Assert: |w| is not of the form [=𝗂𝟨𝟦.𝖼𝗈𝗇𝗌𝗍=] |i64|.
1. If |w| is of the form [=𝗂𝟥𝟤.𝖼𝗈𝗇𝗌𝗍=] |i32|, return [=the Number value=] for [=signed_32=](|i32|).
1. If |w| is of the form [=𝖿𝟥𝟤.𝖼𝗈𝗇𝗌𝗍=] |f32|, return [=the Number value=] for |f32|.
1. If |w| is of the form [=𝖿𝟨𝟦.𝖼𝗈𝗇𝗌𝗍=] |f64|, return [=the Number value=] for |f64|.
1. If |w| is of the form [=ref.null=], return null.
1. If |w| is of the form [=ref.func=] |funcaddr|, return the result of creating [=a new Exported Function=] from |funcaddr|.
Copy link
Member

Choose a reason for hiding this comment

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

Just checking my understanding here: this will not create a new function each time ToWebAssemblyValue is called, because the Exported Function creation early returns if it's been already created before? (i.e. exported function creation is a projection)

Copy link
Member

Choose a reason for hiding this comment

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

What is the functype of such an Exported Function? A new kind of signature anysig that validates every static signature check but requires a dynamic signature check instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, exported functions are cached and no funcaddr will be wrapped more than once. The type is determined by the function instance it points to.

1. If |w| is of the form [=ref.host=] |hostaddr|, return the result of [=retrieving a host value=] from |hostaddr|.

<!-- If the WebAssembly value is optional, then given `None`, return JavaScript value `undefined`. -->

Note: Implementations may optionally replace the NaN payload with any other NaN payload at this point in the f32 or f64 cases; such a change would not be observable through [=NumberToRawBytes=].
</div>

<div algorithm>
The algorithm <dfn>ToWebAssemblyValue</dfn>(|v|, |type|) coerce a JavaScript value to a [=WebAssembly value=] performs the following steps:
For <dfn>retrieving a host value</dfn> from a [=host address=] |hostaddr|, perform the following steps:
1. Let |map| be the [=surrounding agent=]'s associated [=host value cache=].
1. Assert: |map|[|hostaddr|] [=map/exists=].
1. Return |map|[|hostaddr|].
</div>

<div algorithm>
The algorithm <dfn>ToWebAssemblyValue</dfn>(|v|, |type|, |error|) coerces a JavaScript value to a [=WebAssembly value=] performs the following steps:


Assert: |type| is not [=𝗂𝟨𝟦=].
Expand All @@ -801,9 +822,34 @@ Assert: |type| is not [=𝗂𝟨𝟦=].
1. If |type| is [=𝖿𝟨𝟦=],
1. Let |f64| be ? [=ToNumber=](|v|).
1. Return [=𝖿𝟨𝟦.𝖼𝗈𝗇𝗌𝗍=] |f64|.
1. If |type| is [=anyref=],
1. If |v| is a primitive value but not a string, symbol, or null, throw |error|.
Copy link
Member

Choose a reason for hiding this comment

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

What's the criteria for allowing Strings here (which I think get implicitly boxed), while primitive number/boolean are disallowed although they could also be implicitly boxed?

Copy link
Member Author

Choose a reason for hiding this comment

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

Excellent question, which has also been asked by @gahaas. I believe it would be rather useful to allow any JS value here, but @lukewagner mentioned some reasons why that might be difficult in SpiderMonkey. I have it on my slides as an open question for this week's meeting.

Copy link
Member

Choose a reason for hiding this comment

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

Well, my expectation here is that string promitives wouldn't autobox into String objects. So, e.g., the identity function would preserve primitive-ness.

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem is the implementation of ref.eq. As far as I understand it, non-primitive JS objects have an identity, so we can implement ref.eq by comparing the references to two JS objects, instead of doing a deep comparison. Primitive values, however, don't have an identity. The same number, or the same string, may be stored multiple times on the heap, in different representations. Therefore we would have to do a deep comparison for primitive values, or we would have to canonicalize primitive values in ToWebAssemblyValue to allow a reference comparison again.

Copy link
Member Author

Choose a reason for hiding this comment

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

@gahaas, ref.eq requires anyeqref, and strings are excluded from that (for the reasons you describe). So would other primitive values be. Hence I don't think there is an issue.

@lukewagner, why would other primitive types require autoboxing either? I think you can treat any tagged pointer as a reference without problem.

Copy link
Member

Choose a reason for hiding this comment

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

@rossberg To make sure I understand: are you talking about being able to stick, e.g., a bool into an anyref (without autoboxing)? Also number? Is anyref just any then? Is your underlying interest intrefs here?

Copy link
Member Author

Choose a reason for hiding this comment

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

@lukewagner, yes, there is no technical reason AFAICS why we cannot allow that. Simply view all JS values as reference types. Some of them may be unboxed/flattened in practice, but that is an implementation detail that we can leave entirely transparent to Wasm, because it can never tell the difference.

Intrefs will certainly be in a similar category, but here the motivation is (a) simplicity, and (b) completeness. I think you want all JS value to be able to round-trip through Wasm without having to distinguish them. That makes it much easier to implement generic JS data structures in Wasm.

Copy link
Member

Choose a reason for hiding this comment

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

I certainly see the value of round-tripping any JS value (and any in general). When it comes to the question of whether to NaN-box (esp on 32-bit), I think there might be perf benefits to having a more restricted union (not including f64). Probably a good general CG discussion item.

1. Return the result of [=allocating a host address=] for |v|.
1. If |type| is [=anyeqref=],
1. If |v| is a primitive value but not a symbol or null, throw |error|.
1. Return the result of [=allocating a host address=] for |v|.
1. If |type| is [=anyfunc=],
1. If |v| is not an [=Exported function=] or null, throw |error|.
1. Return the result of [=allocating a host address=] for |v|.

</div>

<div algorithm>
For <dfn>allocating a host address</dfn> for a value |v|, perform the following steps:
1. If |v| is null,
1. Return [=ref.null=].
1. If |v| is an [=Exported Function=],
1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot.
1. Return [=ref.func=] |funcaddr|.
1. Let |map| be the [=surrounding agent=]'s associated [=host value cache=].
1. If a [=host address=] |hostaddr| exists such that |map|[|hostaddr|] is the same as |v|,
1. Return [=ref.host=] |hostaddr|.
1. Let [=host address=] |hostaddr| be the smallest address such that |map|[|hostaddr|] [=map/exists=] is false.
1. [=map/Set=] |map|[|hostaddr|] to |v|.
1. Return [=ref.host=] |hostaddr|.
</div>


<h3 id="error-objects">Error Objects</h3>

WebAssembly defines the following Error classes: {{CompileError}}, {{LinkError}}, and {{RuntimeError}}. WebAssembly errors have the following custom bindings:
Expand Down
2 changes: 1 addition & 1 deletion proposals/reference-types/Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ API extensions:
* Any JS object (non-primitive value) or string or symbol or `null` can be passed as `anyref` to a Wasm function, stored in a global, or in a table.
- It may be possible to allow all other non-primitive values as well, depending on details of existing engines.

* Any JS function object or `null` can be passed as `anyfunc` to a Wasm function, stored in a global, or in a table.
* Any Wasm exported function object or `null` can be passed as `anyfunc` to a Wasm function, stored in a global, or in a table.


## Possible Future Extensions
Expand Down