Skip to content

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

Closed
wants to merge 1 commit into from
Closed
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
65 changes: 29 additions & 36 deletions JS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

Expand All @@ -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)

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.

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"`.

Expand All @@ -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"`.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

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

Should this be changed to create a new SharedArrayBuffer depending on whether m is shared? Or is that a separate proposal?

Copy link
Member

Choose a reason for hiding this comment

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

I probably should just update it in the threads repo, I think.

Expand All @@ -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
Expand Down Expand Up @@ -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:
Copy link
Member

Choose a reason for hiding this comment

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

Should be M.[[BufferObject]].[[ArrayBufferData]]

Copy link
Member

@lukewagner lukewagner Aug 14, 2017

Choose a reason for hiding this comment

The 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]]`,
Copy link
Member

Choose a reason for hiding this comment

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

"Blocks", not "Block", I guess

Copy link
Member

Choose a reason for hiding this comment

The 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 WebAssembly.Memory objects N in the same Agent Cluster as M where N.[[BufferObject]].[[ArrayBufferData]] == M.[[BufferObject]].[[ArrayBufferData]]..."

doing so atomically in relation to other updates of such blocks.
Otherwise:
Copy link
Member

@lukewagner lukewagner Aug 14, 2017

Choose a reason for hiding this comment

The 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]]`).
Copy link
Member

Choose a reason for hiding this comment

The 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.

Expand All @@ -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
Expand Down
89 changes: 89 additions & 0 deletions Web.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The 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"

Copy link
Member

Choose a reason for hiding this comment

The 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).
Copy link
Member

Choose a reason for hiding this comment

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

@domenic Just to be clear, this means a Module can be post-messaged between some windows (linked by owner and same-origin, same-origin and iframe, and maybe even cross-origin; I'm not sure how to read the could in "could be same-origin" in 8.1.3.10). Is that right?

Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The 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".

Copy link
Member

@lukewagner lukewagner Jul 28, 2017

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, that matches my understanding too.

Copy link
Member

@lukewagner lukewagner Aug 24, 2017

Choose a reason for hiding this comment

The 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.

Choose a reason for hiding this comment

The 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
Expand Down