-
Notifications
You must be signed in to change notification settings - Fork 695
Clarify structured clone behavior for WebAssembly objects. #1074
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -169,7 +169,7 @@ with the resulting `WebAssembly.Instance` object. On failure, the `Promise` is | |
A `WebAssembly.Module` object represents the stateless result of compiling a | ||
WebAssembly binary-format module and contains one internal slot: | ||
|
||
* [[Module]] : an [`Ast.module`](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L176) | ||
* [[WebAssembly.Module]] : an [`Ast.module`](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L176) | ||
which is the spec definition of a module | ||
|
||
### `WebAssembly.Module` Constructor | ||
|
@@ -195,7 +195,7 @@ Otherwise, this function performs synchronous compilation of the `BufferSource`: | |
according to the rules in [spec/valid.ml](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/valid.ml#L415). | ||
1. The spec `string` values inside `Ast.module` are decoded as UTF8 as described in | ||
[Web.md](Web.md#names). | ||
1. On success, a new `WebAssembly.Module` object is returned with [[Module]] set to | ||
1. On success, a new `WebAssembly.Module` object is returned with [[WebAssembly.Module]] set to | ||
the validated `Ast.module`. | ||
1. On failure, a new `WebAssembly.CompileError` is thrown. | ||
|
||
|
@@ -219,7 +219,7 @@ is thrown. | |
|
||
This function returns a new `Array` every time it is called. Each such `Array` is produced by mapping each | ||
[`Ast.export`](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L152) | ||
`e` of [moduleObject.[[Module]].exports](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L187) | ||
`e` of [moduleObject.[[WebAssemby.Module]].exports](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L187) | ||
to the Object `{ name: String(e.name), kind: e.ekind }` where `e.name` is [decoded as UTF8](Web.md#names) | ||
and `e.ekind` is mapped to one of the String values `"function"`, `"table"`, `"memory"`, `"global"`. | ||
|
||
|
@@ -240,7 +240,7 @@ is thrown. | |
|
||
This function returns a new `Array` every time it is called. Each such `Array` is produced by mapping each | ||
[`Ast.import`](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L167) | ||
`i` of [moduleObject.[[Module]].imports](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L203) | ||
`i` of [moduleObject.[[WebAssembly.Module]].imports](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L203) | ||
to the Object `{ module: String(i.module_name), name: String(i.item_name), kind: i.ikind }` where | ||
`i.module_name` and `i.item_name` are [decoded as UTF8](Web.md#names) and | ||
`i.ikind` is mapped to one of the String values `"function"`, `"table"`, `"memory"`, `"global"`. | ||
|
@@ -270,22 +270,6 @@ This function returns a new `Array` every time it is called. Each such `Array` i | |
|
||
The `Array` is populated in the same order custom sections appear in the WebAssembly binary. | ||
|
||
### Structured Clone of a `WebAssembly.Module` | ||
|
||
A `WebAssembly.Module` is a | ||
[cloneable object](https://html.spec.whatwg.org/multipage/infrastructure.html#cloneable-objects) | ||
which means it can be cloned between windows/workers and also | ||
stored/retrieved into/from an [IDBObjectStore](https://w3c.github.io/IndexedDB/#object-store). | ||
The semantics of a structured clone is as-if the binary source, from which the | ||
`WebAssembly.Module` was compiled, were cloned and recompiled into the target realm. | ||
Engines should attempt to share/reuse internal compiled code when performing | ||
a structured clone although, in corner cases like CPU upgrade or browser | ||
update, this may not be possible and full recompilation may be necessary. | ||
|
||
Given the above engine optimizations, structured cloning provides developers | ||
explicit control over both compiled-code caching and cross-window/worker code | ||
sharing. | ||
|
||
## `WebAssembly.Instance` Objects | ||
|
||
A `WebAssembly.Instance` object represents the instantiation of a | ||
|
@@ -313,7 +297,7 @@ If `moduleObject` is not a `WebAssembly.Module`, a [`TypeError`](https://tc39.gi | |
is thrown. | ||
|
||
Let `module` be the [`Ast.module`](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/ast.ml#L176) | ||
`moduleObject.[[Module]]`. | ||
`moduleObject.[[WebAssembly.Module]]`. | ||
|
||
If the `importObject` parameter is not `undefined` and `Type(importObject)` is | ||
not Object, a [`TypeError`](https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-typeerror) | ||
|
@@ -371,7 +355,7 @@ For each [`import`](https://github.com/WebAssembly/spec/blob/master/interpreter/ | |
[`memory_type`](https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#memory_type) | ||
by `Eval.init` below.) | ||
1. Append `v` to `memories`. | ||
1. Append `v.[[Memory]]` to `imports`. | ||
1. Append `v.[[WebAssembly.Memory]]` to `imports`. | ||
1. Otherwise (`i` is a table import): | ||
1. If `v` is not a [`WebAssembly.Table` object](#webassemblytable-objects), | ||
throw a `WebAssembly.LinkError`. | ||
|
@@ -446,7 +430,7 @@ each [external](https://github.com/WebAssembly/spec/blob/master/interpreter/spec | |
1. If `v` is an `i64`, throw a `WebAssembly.LinkError`. | ||
1. Return [`ToJSValue`](#tojsvalue)`(v)`. | ||
1. If `e` is a [memory](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/instance.ml#L14) `m`: | ||
1. If there is an element `memory` in `memories` whose `memory.[[Memory]]` is `m`, then return `memory`. | ||
1. If there is an element `memory` in `memories` whose `memory.[[WebAssembly.Memory]]` is `m`, then return `memory`. | ||
1. (Note: At most one `WebAssembly.Memory` object is created for any memory, so the above `memory` is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import, the original object will be found.) | ||
1. Otherwise: | ||
1. Let `memory` be a new `WebAssembly.Memory` object created via [`CreateMemoryObject`](#creatememoryobject) from `m`. | ||
|
@@ -537,9 +521,9 @@ A `WebAssembly.Memory` object contains a single [linear memory](Semantics.md#lin | |
which can be simultaneously referenced by multiple `Instance` objects. Each | ||
`Memory` object has two internal slots: | ||
|
||
* [[Memory]] : a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/memory.mli) | ||
* [[BufferObject]] : the current `ArrayBuffer` whose [[ArrayBufferByteLength]] | ||
matches the current byte length of [[Memory]] | ||
* [[WebAssembly.Memory]] : a [memory instance](https://webassembly.github.io/spec/exec/runtime.html#memory-instances) | ||
* [[BufferObject]] : the current `ArrayBuffer` or `SharedArrayBuffer` whose [[ArrayBufferByteLength]] | ||
matches the byte length of [[WebAssembly.Memory]]. | ||
|
||
### `WebAssembly.Memory` Constructor | ||
|
||
|
@@ -571,7 +555,7 @@ Return the result of [`CreateMemoryObject`](#creatememoryobject)(`memory`). | |
|
||
### CreateMemoryObject | ||
|
||
Given a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/memory.mli#L1) | ||
Given a [memory instance](https://webassembly.github.io/spec/exec/runtime.html#memory-instances) | ||
`m`, to create a `WebAssembly.Memory`: | ||
|
||
Let `buffer` be a new `ArrayBuffer` whose | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be changed to create a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I probably should just update it in the threads repo, I think. |
||
|
@@ -584,7 +568,7 @@ Any attempts to [`detach`](http://tc39.github.io/ecma262/#sec-detacharraybuffer) | |
the detachment performed by [`m.grow`](#webassemblymemoryprototypegrow) shall throw a | ||
[`TypeError`](https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-typeerror) | ||
|
||
Return a new `WebAssembly.Memory` instance with [[Memory]] set to `m` and | ||
Return a new `WebAssembly.Memory` instance with [[WebAssembly.Memory]] set to `m` and | ||
[[BufferObject]] set to `buffer`. | ||
|
||
### `WebAssembly.Memory.prototype [ @@toStringTag ]` Property | ||
|
@@ -615,13 +599,22 @@ with delta `d`. On failure, a | |
[`RangeError`](https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror) | ||
is thrown. | ||
|
||
Perform [`DetachArrayBuffer`](http://tc39.github.io/ecma262/#sec-detacharraybuffer)(`M.[[BufferObject]]`). | ||
|
||
Assign to `M.[[BufferObject]]` a new `ArrayBuffer` whose | ||
[[[ArrayBufferData]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) | ||
aliases `M.[[Memory]]` and whose | ||
[[[ArrayBufferByteLength]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) | ||
is set to the new byte length of `M.[[Memory]]`. | ||
If `M.[[BufferObject]]` is a not Shared Data Block: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be M.[[BufferObject]].[[ArrayBufferData]] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I think you need to drop the "not" |
||
1. Assign to `M.[[BufferObject]]` a new `ArrayBuffer` whose | ||
[[[ArrayBufferData]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) | ||
aliases `M.[[WebAssembly.Memory]]` and whose | ||
[[[ArrayBufferByteLength]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) | ||
is set to the new byte length of `M.[[WebAssembly.Memory]]`. | ||
2. Do the same in all Shared Data Block which mirror `M.[[BufferObject]]`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Blocks", not "Block", I guess There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was expecting this to look more like: "Perform step 1 to all |
||
doing so atomically in relation to other updates of such blocks. | ||
Otherwise: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: markdown puts this at the end of item 2, I think you need a \n before or something to render correctly |
||
1. perform [`DetachArrayBuffer`](http://tc39.github.io/ecma262/#sec-detacharraybuffer)(`M.[[BufferObject]]`). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uppercase "P" |
||
|
||
2. Assign to `M.[[BufferObject]]` a new `ArrayBuffer` whose | ||
[[[ArrayBufferData]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) | ||
aliases `M.[[WebAssembly.Memory]]` and whose | ||
[[[ArrayBufferByteLength]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) | ||
is set to the new byte length of `M.[[WebAssembly.Memory]]`. | ||
|
||
Return `ret` as a Number value. | ||
|
||
|
@@ -639,7 +632,7 @@ A `WebAssembly.Table` object contains a single [table](Semantics.md#table) | |
which can be simultaneously referenced by multiple `Instance` objects. Each | ||
`Table` object has two internal slots: | ||
|
||
* [[Table]] : a [`Table.table`](https://github.com/WebAssembly/spec/blob/master/interpreter/spec/table.mli#L1) | ||
* [[Table]] : a [`table instance`](https://webassembly.github.io/spec/exec/runtime.html#table-instances) | ||
* [[Values]] : an array whose elements are either `null` or [Exported Function Exotic Object](#exported-function-exotic-objects) | ||
|
||
### `WebAssembly.Table` Constructor | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -202,6 +202,95 @@ Given these values, to process a potential WebAssembly response: | |
_reason_. | ||
1. Return _returnValue_. | ||
|
||
### Structured serialization of a `WebAssembly.Memory` | ||
|
||
A `WebAssembly.Memory` is a | ||
[serializable object](https://html.spec.whatwg.org/multipage/infrastructure.html#serializable-objects), and will require changes to the | ||
[HTML Standard](https://html.spec.whatwg.org/multipage/infrastructure.html#serialization-steps) as follows: | ||
|
||
[StructuredSerializeInternal](https://html.spec.whatwg.org/multipage/infrastructure.html#structuredserializeinternal) adds a new step after step 12: | ||
|
||
* Otherwise, if *value* has a [[WebAssembly.Memory]] internal slot, then: | ||
1. Let *memory* be *value*.[[WebAssembly.Memory]], which is a | ||
[`memory instance`](https://webassembly.github.io/spec/exec/runtime.html#memory-instances). | ||
2. Let *data* be *memory*'s `data` field, which is a sizable vector of bytes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is "sizable" standard terminology here? If not, "resizable" is probably more clear, as I usually think of "sizable" as a synonym for "large" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused. Is data a Shared Data Block, or a sizable vector of bytes? Are the two concepts the same somehow? |
||
3. Let *maximum* be the value of *memory*'s `max` field, which is an optional u32. | ||
4. If *data* is a Shared Data Block, then: | ||
1. If *forStorage* is true, then throw a | ||
"[`DataCloneError`](https://heycam.github.io/webidl/#datacloneerror)" | ||
[`DOMException`](https://heycam.github.io/webidl/#dfn-DOMException). | ||
2. Otherwise, | ||
set *serialized* to { [[Type]]: "WebAssembly.Memory", | ||
[[MemoryData]]: *data*, | ||
[[MemoryMaximum]]: *maximum*, | ||
[[AgentCluster]]: the [current Realm Record](https://tc39.github.io/ecma262/#current-realm)'s corresponding [agent cluster](https://tc39.github.io/ecma262/#sec-agent-clusters) }. | ||
5. Otherwise throw a | ||
"[`DataCloneError`](https://heycam.github.io/webidl/#datacloneerror)" | ||
[`DOMException`](https://heycam.github.io/webidl/#dfn-DOMException). | ||
|
||
[StructuredDeserialize](https://html.spec.whatwg.org/multipage/infrastructure.html#structureddeserialize) adds a new step after step 12: | ||
|
||
* Otherwise, if *serialized*.[[Type]] is "WebAssembly.Memory", then: | ||
1. Assert *serialized*.[[MemoryData]] is a Shared Data Block. | ||
2. If *targetRealm*'s corresponding [agent cluster](https://tc39.github.io/ecma262/#sec-agent-clusters) is not | ||
*serialized*.[[AgentCluster]], then throw a | ||
"[`DataCloneError`](https://heycam.github.io/webidl/#datacloneerror)" | ||
[`DOMException`](https://heycam.github.io/webidl/#dfn-DOMException). | ||
3. Set *value* to a new WebAssembly.Memory object in *targetRealm*, where: | ||
1. *value*'s internal slot [[WebAssembly.Memory]] is set to a new | ||
[`memory instance`](https://webassembly.github.io/spec/exec/runtime.html#memory-instances) *mem*, where: | ||
1. *mem*'s `data` field is set to *serialized*.[[MemoryData]]. | ||
2. *mem*'s `max` size is set to *serialized*.[[MemoryMaximum]]. | ||
2. *value*'s internal slot [[BufferObject]] is set to a new `SharedArrayBuffer` *buf*, where: | ||
1. *buf*'s internal slot [[ArrayBufferData]] | ||
aliases *mem*'s `data` field, which is a sizable vector of bytes. | ||
2. *buf*'s internal slot [[ArrayBufferByteLength]] | ||
is set to the byte length of *mem*'s linear memory. | ||
3. The above is done atomically in relation to other updates to all | ||
aliases of [[BufferObject]]. | ||
|
||
|
||
### Structured serialization of a `WebAssembly.Module` | ||
|
||
A `WebAssembly.Module` is a | ||
[serializable object](https://html.spec.whatwg.org/multipage/infrastructure.html#serializable-objects), and will require changes to the | ||
[HTML Standard](https://html.spec.whatwg.org/multipage/infrastructure.html#serialization-steps) as follows: | ||
|
||
[StructuredSerializeInternal](https://html.spec.whatwg.org/multipage/infrastructure.html#structuredserializeinternal) adds a new step after step 12: | ||
|
||
* Otherwise, if *value* has a [[WebAssembly.Module]] internal slot, then: | ||
1. If *forStorage* is true and the relevant settings | ||
object of *value* is not "Secure" per the | ||
["Is the environment settings object a secure context?"](https://w3c.github.io/webappsec-secure-contexts/#settings-object) | ||
algorithm, throw a | ||
"[`DataCloneError`](https://heycam.github.io/webidl/#datacloneerror)" | ||
[`DOMException`](https://heycam.github.io/webidl/#dfn-DOMException). | ||
2. Otherwise, | ||
set *serialized* to { [[Type]]: "WebAssembly.Module", | ||
[[WebAssembly.Module]]: *value*.[[WebAssembly.Module]], | ||
[[AgentCluster]]: the [current Realm Record](https://tc39.github.io/ecma262/#current-realm)'s corresponding [agent cluster](https://tc39.github.io/ecma262/#sec-agent-clusters) }. | ||
|
||
[StructuredDeserialize](https://html.spec.whatwg.org/multipage/infrastructure.html#structureddeserialize) adds a new step after step 12: | ||
|
||
* Otherwise, if *serialized*.[[Type]] is "WebAssembly.Module", then: | ||
1. If *targetRealm*'s corresponding [agent cluster](https://tc39.github.io/ecma262/#sec-agent-clusters) is not | ||
*serialized*.[[AgentCluster]], then throw a | ||
"[`DataCloneError`](https://heycam.github.io/webidl/#datacloneerror)" | ||
[`DOMException`](https://heycam.github.io/webidl/#dfn-DOMException). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically windows that can document.domain each other are in the same agent (similar-origin window agent). And therefore also in the same agent cluster. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, sorry for not getting back to you earlier, but @annevk covered it. Also note that "same origin" and "same origin-domain" are distinct concepts; whether a Window is same origin with another is immutable, but whether it's same origin-domain can change (by changing document.domain). Thus the "could be". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha, thanks. So my root question was whether this definition was permissive enough to allow cross-origin code caching (e.g., in the local IDB of a CDN origin), but it looks like 'no', that'll have to wait for foreign-fetch. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, that matches my understanding too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, it looks like foreign fetch is no longer being proposed. So what's the recommended mechanism for explicit sharing of compiled code that doesn't rely on pure HTTP cache magic? I think this is a really important capability for frameworks/middleware in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What was the reasoning for restricting Modules from being postMessage'd across agent clusters? It's hard for me to understand how this squares with serializing Modules in IndexedDB (which this patch seems to allow AFAICT, though I don't see how it specifies the sub-serialization of the agent cluster). |
||
2. Otherwise, set *value* to a new WebAssembly.Module object in *targetRealm* | ||
whose [[WebAssembly.Module]] internal slot *value* is *serialized*.[[WebAssembly.Module]]. | ||
|
||
The semantics of a structured serialization is as-if the binary source, from which the | ||
`WebAssembly.Module` was compiled, is serialized, then deserialized, and recompiled into the target realm. | ||
Engines should attempt to share/reuse internal compiled code when performing | ||
a structured serialization, although in corner cases like CPU upgrade or browser | ||
update, this might not be possible and full recompilation may be necessary. | ||
|
||
Given the above engine optimizations, structured serialization provides developers | ||
explicit control over both compiled-code caching and cross-window/worker code | ||
sharing. | ||
|
||
|
||
## Developer-facing display conventions | ||
|
||
Browsers, JavaScript engines, and offline tools have common ways of referring to | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WebAssembly
is spelt incorrectly here.