1 of 32

Web IDL Bindings Update

June 12-13, 2019

Google, Mozilla

2 of 32

Agenda

  • Progress since last time
  • Short summary of the current proposal
  • Prototype work
    • C++
    • Rust
    • WebGL
  • Additional uses identified
    • WASI
    • "Shared-Nothing" Linking
  • Proposed spec refactoring
  • FAQ
  • Poll about proposed spec refactoring

3 of 32

Progress Since Last Time

  • TPAC 2018:
    • Proposed new scope: optimizing calls to Web APIs via Web IDL
    • Thus rename Host Bindings --> Web IDL Bindings
    • Note quite sure, so postponed any decision
  • CG-04-02 meeting:
    • Agreed to rescoping/renaming based on PR with rewritten explainer
  • After that:
    • Iteration on design
    • Iteration on prototypes (discussed later)
    • Emergence of more use cases for this feature (discussed later)

4 of 32

Summary of Current Proposal

5 of 32

wasm module

function import

function export

function import

function export

Web API function

params

results

JS values

wasm JS API spec

Web IDL values

Web IDL spec: ECMAScript Binding

JS glue code

Web API callback

params

results

JS values

wasm JS API spec

JS glue code

Web IDL values

Web IDL spec: ECMAScript Binding

wasm module

function import

params

results

wasm module

function export

params

results

In the MVP, ...

... when calling wasm

... when calling a Web API

6 of 32

Web IDL Bindings "wrap" the core module

  • Defines new custom section
  • Can 100% polyfill by generating JS glue from custom section

Web IDL Bindings

wasm module

function import

function export

Web API function

Web API callback

params

results

Web IDL values

params

results

Web IDL values

Incoming binding exprs

  • can place values into linear memory

Outgoing binding exprs

  • can extract values from linear memory

With the proposal...

... when calling a Web API

function import

function export

wasm module

function import

params

results

wasm module

function export

params

results

... when calling wasm

7 of 32

dictionary TextEncoderEncodeIntoResult {

unsigned long long read;

unsigned long long written;

};

interface TextEncoder {

TextEncoderEncodeIntoResult� encodeInto(USVString source,� Uint8Array destination);

};

https://github.com/WebAssembly/webidl-bindings/blob/master/proposals/webidl-bindings/Explainer.md

8 of 32

Note on values and copying

  • "Web IDL values" are immutable values
  • Binding Expressions construct Web IDL values from wasm values / linear memory (and vice versa)
  • Thus creating a DOMString from linear memory = eager copy
    • Why? Linear memory can be mutated; values are immutable.
  • However, the browser doesn't have to actually copy if:
    • Memory is unshared and copy isn't used after Web API returns
    • Memory is shared and linear memory is read in a single pass
  • Web IDL Interface/Promise/... values are reference values
    • The value is the abstract address of the object, not the object itself
  • Dictionary/Sequence/Union values are not reference values
    • The value is their contents, there is no address/reference

9 of 32

Prototype Work

10 of 32

Standalone Polyfill Prototype

https://github.com/jgravelle-google/wasm-webidl-polyfill

Building a webIDL.js module, reads a custom section and fixes up import + export dicts at runtime

Goals:

  • Prototype the design, prove feasibility of polyfilling
  • Polyfillability in general is a useful property because developers can ship the real bytes early, and the browser can support that natively at a later time
  • Having a prepackaged chunk of JS makes this easier to include in arbitrary toolchains

11 of 32

Integration in the Rust+Wasm Toolchain

  • https://github.com/rustwasm/wasm-webidl-bindings
    • Parser
    • Encoder
    • AST for working with bindings
  • Ongoing work to integrate into wasm-bindgen
    • Clear separation between features supported by webidl bindings and features not supported

12 of 32

Right Now

Sources

rustc/llvm

wasm-bindgen

wasm-bindgen

✓ Optional anyref

✗ No webidl bindings

✗ No anyref

✗ No webidl bindings

* Non-standard bindings

section

  • Optional “anyref” table in JS
  • obj/slice/string/etc glue

13 of 32

Vision for the Future

Sources

wasm-bindgen as optional AOT polyfill

  • “Anyref” table in JS
  • obj/slice/string glue
  • Uses anyref
  • Has webidl bindings section

rustc/llvm

wasm-bindgen

wasm-bindgen

  • No anyref
  • No webidl bindings

14 of 32

Incremental Goal

Sources

rustc/llvm

wasm-bindgen

optional AOT polyfill

  • “Anyref” table in JS
  • obj/slice/string glue

wasm-bindgen

wasm-bindgen

  • Uses anyref
  • Has webidl bindings section

✗ No anyref

✗ No webidl bindings

* Non-standard bindings section

  • No anyref
  • No webidl bindings

15 of 32

WebGL Experiment

  • Animometer Benchmark uses ~20 OpenGL functions.
  • 7 function calls in a hot loop.

// repeated for 20,000 primitives

glUniform1f(uScale, uniformData[i].scale);

glUniform1f(uTime, uniformData[i].time);

glUniform1f(uOffsetX, uniformData[i].offsetX);

glUniform1f(uOffsetY, uniformData[i].offsetY);

glUniform1f(uScalar, uniformData[i].scalar);

glUniform1f(uScalarOffset, uniformData[i].scalarOffset);

glDrawArrays(GL_TRIANGLES, 0, 3);

16 of 32

Methods

--- WebAssembly code ---

kind: wasm function

compiler: TurboFan

Body (size = 96 = 72 + 24 padding)

Instructions (size = 64)

0 55 push rbp

1 4889e5 REX.W movq rbp,rsp

4 6a0a push 0xa

6 56 push rsi

7 4883ec10 REX.W subq rsp,0x10

b 488d7de0 REX.W leaq rdi,[rbp-0x20]

f 8907 movl [rdi],rax

11 c5fb114f04 vmovsd [rdi+0x4],xmm1

16 488b7617 REX.W movq rsi,[rsi+0x17]

1a 4989e2 REX.W movq r10,rsp

1d 4883ec08 REX.W subq rsp,0x8

21 4883e4f0 REX.W andq rsp,0xf0

25 4c891424 REX.W movq [rsp],r10

29 48b8d06c171301000000 REX.W movq rax,0x113176cd0

33 ffd0 call rax

35 488b2424 REX.W movq rsp,[rsp]

39 488be5 REX.W movq rsp,rbp

3c 5d pop rbp

3d c3 retl

3e 90 nop

3f 90 nop

--- WebAssembly code ---

kind: wasm-to-js

compiler: TurboFan

Body (size = 608 = 588 + 20 padding)

Instructions (size = 516)

0 55 push rbp

1 4889e5 REX.W movq rbp,rsp

4 6a0c push 0xc

6 488b7e0f REX.W movq rdi,[rsi+0xf]

a 488b7607 REX.W movq rsi,[rsi+0x7]

e 56 push rsi

f 4883ec48 REX.W subq rsp,0x48

13 488b9e9f000000 REX.W movq rbx,[rsi+0x9f]

1a 488b4e77 REX.W movq rcx,[rsi+0x77]

1e 4c8b467f REX.W movq r8,[rsi+0x7f]

22 4d8b80d8300000 REX.W movq r8,[r8+0x30d8]

29 41c70000000000 movl [r8],0x0

30 4c8b471f REX.W movq r8,[rdi+0x1f]

34 4c8b4b2f REX.W movq r9,[rbx+0x2f]

38 c57b2cd9 vcvttsd2si r11,xmm1

3c c5f957c0 vxorpd xmm0,xmm0,xmm0

40 c4c17b2ac3 vcvtlsi2sd xmm0,xmm0,r11

45 48897c2430 REX.W movq [rsp+0x30],rdi

4a 4889742440 REX.W movq [rsp+0x40],rsi

4f c5fb114c2420 vmovsd [rsp+0x20],xmm1

55 c5fb11542408 vmovsd [rsp+0x8],xmm2

5b 48895c2438 REX.W movq [rsp+0x38],rbx

60 48894c2418 REX.W movq [rsp+0x18],rcx

65 4c89442410 REX.W movq [rsp+0x10],r8

6a 4c894c2428 REX.W movq [rsp+0x28],r9

6f c5f92ec1 vucomisd xmm0,xmm1

73 0f8a19000000 jpe 0x251a417d5412 <+0x92>

79 0f8513000000 jnz 0x251a417d5412 <+0x92>

7f 4183fb00 cmpl r11,0x0

83 0f84cf000000 jz 0x251a417d54d8 <+0x158>

89 49c1e320 REX.W shlq r11, 32

8d e913000000 jmp 0x251a417d5425 <+0xa5>

92 e813fdffff call 0x251a417d512a ;; wasm stub: WasmAllocateHeapNumber

97 c5fb10442420 vmovsd xmm0,[rsp+0x20]

9d c5fb114007 vmovsd [rax+0x7],xmm0

a2 4c8bd8 REX.W movq r11,rax

a5 c5fb2c5c2408 vcvttsd2si rbx,[rsp+0x8]

ab c5f957c0 vxorpd xmm0,xmm0,xmm0

af c5fb2ac3 vcvtlsi2sd xmm0,xmm0,rbx

b3 4c895c2420 REX.W movq [rsp+0x20],r11

b8 c5f92e442408 vucomisd xmm0,[rsp+0x8]

be 0f8a18000000 jpe 0x251a417d545c <+0xdc>

c4 0f8512000000 jnz 0x251a417d545c <+0xdc>

ca 83fb00 cmpl rbx,0x0

cd 0f84e4000000 jz 0x251a417d5537 <+0x1b7>

d3 48c1e320 REX.W shlq rbx, 32

d7 e913000000 jmp 0x251a417d546f <+0xef>

dc e8c9fcffff call 0x251a417d512a ;; wasm stub: WasmAllocateHeapNumber

e1 c5fb10442408 vmovsd xmm0,[rsp+0x8]

e7 c5fb114007 vmovsd [rax+0x7],xmm0

ec 488bd8 REX.W movq rbx,rax

ef ff742428 push [rsp+0x28]

f3 ff742428 push [rsp+0x28]

f7 53 push rbx

f8 b802000000 movl rax,0x2

fd 488b7c2448 REX.W movq rdi,[rsp+0x48]

102 488b542430 REX.W movq rdx,[rsp+0x30]

107 488b742428 REX.W movq rsi,[rsp+0x28]

10c 488b4f2f REX.W movq rcx,[rdi+0x2f]

110 4883c13f REX.W addq rcx,0x3f

114 ffd1 call rcx

116 488b742438 REX.W movq rsi,[rsp+0x38]

11b e850fdffff call 0x251a417d51f0 ;; wasm stub: WasmToNumber

120 488bd8 REX.W movq rbx,rax

123 4883e301 REX.W andq rbx,0x1

127 0f85a9000000 jnz 0x251a417d5556 <+0x1d6>

12d 48c1e820 REX.W shrq rax, 32

131 c5f957c0 vxorpd xmm0,xmm0,xmm0

135 c5fb2ac0 vcvtlsi2sd xmm0,xmm0,rax

139 488b5c2440 REX.W movq rbx,[rsp+0x40]

13e 488b5b7f REX.W movq rbx,[rbx+0x7f]

142 488b9bd8300000 REX.W movq rbx,[rbx+0x30d8]

149 c70301000000 movl [rbx],0x1

14f c5f928c8 vmovapd xmm1,xmm0

153 488be5 REX.W movq rsp,rbp

156 5d pop rbp

157 c3 retl

158 660f3a16cb01 pextrd rbx,xmm1,1

15e 4c891c24 REX.W movq [rsp],r11

162 83fb00 cmpl rbx,0x0

165 0f8c28000000 jl 0x251a417d5513 <+0x193>

16b 488b5c2438 REX.W movq rbx,[rsp+0x38]

170 488b7c2430 REX.W movq rdi,[rsp+0x30]

175 4c8b4c2428 REX.W movq r9,[rsp+0x28]

17a 488b4c2418 REX.W movq rcx,[rsp+0x18]

17f 4c8b442410 REX.W movq r8,[rsp+0x10]

184 c5fb10542408 vmovsd xmm2,[rsp+0x8]

18a 4c8b1c24 REX.W movq r11,[rsp]

18e e9f6feffff jmp 0x251a417d5409 <+0x89>

193 488b5c2438 REX.W movq rbx,[rsp+0x38]

198 488b7c2430 REX.W movq rdi,[rsp+0x30]

19d 4c8b4c2428 REX.W movq r9,[rsp+0x28]

1a2 488b4c2418 REX.W movq rcx,[rsp+0x18]

1a7 4c8b442410 REX.W movq r8,[rsp+0x10]

1ac c5fb10542408 vmovsd xmm2,[rsp+0x8]

1b2 e9dbfeffff jmp 0x251a417d5412 <+0x92>

1b7 48891c24 REX.W movq [rsp],rbx

1bb 8b5c240c movl rbx,[rsp+0xc]

1bf 488b742440 REX.W movq rsi,[rsp+0x40]

1c4 83fb00 cmpl rbx,0x0

1c7 0f8c0fffffff jl 0x251a417d545c <+0xdc>

1cd 488b1c24 REX.W movq rbx,[rsp]

1d1 e9fdfeffff jmp 0x251a417d5453 <+0xd3>

1d6 488b742440 REX.W movq rsi,[rsp+0x40]

1db 48394677 REX.W cmpq [rsi+0x77],rax

1df 0f840a000000 jz 0x251a417d556f <+0x1ef>

1e5 c5fb104007 vmovsd xmm0,[rax+0x7]

1ea e94affffff jmp 0x251a417d54b9 <+0x139>

1ef c5f976c0 vpcmpeqd xmm0,xmm0,xmm0

1f3 c5f973f034 vpsllq xmm0,xmm0,52

1f8 c5f973d001 vpsrlq xmm0,xmm0,1

1fd e937ffffff jmp 0x251a417d54b9 <+0x139>

202 90 nop

203 90 nop

Safepoints (size = 72)

97 0011011110000 (sp -> fp)

e1 0011111110000 (sp -> fp)

116 0000000110000 (sp -> fp)

120 0000000010000 (sp -> fp)

RelocInfo (size = 7)

wasm stub call

wasm stub call

wasm stub call

--- End code ---

  • Hand-coded ~20 host bindings for WebGL in Chrome.
  • Modified V8 to generate native import wrappers.

17 of 32

Results

18 of 32

Additional Uses Identified

19 of 32

WASI

  • Let's say WASI wanted to define puts() (it doesn't, but say it did)
  • This C signature is: int puts(const char *str)
  • What should WASI specify?
  • Obvs, right? (func $puts (param i32) (result i32))
  • So many problems:
    • How does the callee find the caller's memory in a polyfill? (Consider multi-memory)
    • What if I have my string in a wasm-GC (ref (array u8))?
    • Which encoding? http://utf8everywhere.org?
    • What if I have my string in an opaque (but compatible) host string? (e.g., JS / DOMString)
    • What if I want to run wasm content in a no-GC wasm host environment? (general wasm goal)�
  • No great solution

🤯

... until Web IDL Bindings: int puts(DOMString str)

20 of 32

Kinds of WebAssembly Linking (of compiled code)

bar/src

foo/src

Static Linking

compile

compile

.a

.a

link

.wasm

Instance

Memory + Table

load

bar/src

foo/src

Dynamic Linking

load+link

Instance

Memory + Table

compile

compile

.wasm

.wasm

Instance

bar/src

foo/src

Shared-Nothing Linking

load+link

Instance

Memory + Table

compile

compile

.wasm

.wasm

Instance

Memory + Table

21 of 32

With ESM-integration in Node, what should packages contain?

bar/src

foo/src

Static Linking

compile

compile

.a

.a

link

.wasm

Instance

Memory + Table

load

bar/src

foo/src

Dynamic Linking

load+link

Instance

Memory + Table

compile

compile

.wasm

.wasm

Instance

bar/src

foo/src

"Shared-Nothing" Linking

load+link

Instance

Memory + Table

compile

compile

.wasm

.wasm

Instance

Memory + Table

?

at least

as a default

22 of 32

How do non-scalars get copied between memories?

  • First-class "slice"/"view" references (dynamic operands to load/store)
    • Pros:
      • Optimal zero-copy performance if caller/callee are linear-memory and optimize for this
    • Cons:
      • Hard to optimize for this (in C++/Rust); likely have at least 1 copy
      • Either slices are revocable (🤮) or we have lifetime/encapsulation issues (use-after-free)
      • Do we add a "table slice" to pass a collection of references?
      • Interfaces forced to bias toward GC- or linear-memory
  • Shared "scratch space" Memory/Table (with multi-memory extension)
    • Pros:
      • Optimal zero-copy performance if caller/callee are linear-memory and optimize for this
    • Cons:
      • Really hard to achieve zero-copy; hard to achieve 1 copy; likely have 2 copy
      • Scratch memory space requires coordinated management; most complicated "ABI"
      • Still have some encapsulation/lifetime issues, depending on protocol
      • Interface forced to bias toward GC- or linear-memory

23 of 32

How do non-scalars get copied between memories?

  • First-class GC references
    • Pros:
      • Optimal zero-copy performance if caller/callee are GC-memory and the types line up
      • Encapsulation if only immutable GC types are shared
    • Cons:
      • 2 copies + garbage creation for linear memory caller/callee
      • Can't work in no-GC host environments
  • JS-glue-based calling convention
    • Pros:
      • MVP-compatible! Could work today in Node.js.
    • Cons:
      • Performance of calling between JS and wasm
      • May be hard to establish firm, interoperable standard
      • Can't work in no-JS host environments

24 of 32

How do non-scalars get copied between memories?

  • Web IDL Bindings!
    • How?
      • Module A binds its export, module B imports from A and binds its import.
      • E.g., copy array of bytes via ArrayBuffer
      • This wasm-to-wasm behavior simply falls out from the existing Web IDL Bindings proposal.
    • Pros:
      • Polyfillable (via JS API), so it could work today in Node.js.
        • Ignoring native support, could be thought of as establishing a JS-glue-based calling convention
      • Full encapsulation of Memory/Table possible
      • Compatible with either GC- or linear-memory
      • Simplest ABI (just use Web IDL)
    • Cons:
      • At least one copy when linear memory is on either side; none with suitably immutable GC references)
    • This seems like a reasonable default
      • The other solutions could be available as advanced options (like pipes vs. shmem)

25 of 32

A star (topology) is born

Web�IDL�values

Web

APIs

JS

wasm�module�A

WASI

wasm�module�B

Web IDL ECMAScript Binding �(Section 3 of Web IDL spec)

Binding Expressions (specified by A)

Binding Expressions (specified by B)

Identity function

Note: We're not attempting to solve the general "inter-language interoperability" problem here. We're sending immutable Web IDL values between parties, so languages are interoperable only insofar as they already are via Unix pipes (where reference values = file descriptors).

Note: Nowhere are we specifying a binary serialization format. (A host API could define one at a spoke, though.)

26 of 32

Is the center node really "Web IDL"?

  • We started with Web IDL, then:
    • removed:
      • Web IDL Interface/Promise/... types (using type+function imports instead)
      • ECMAScript Bindings and all accidental JS-isms only needed for the JS "spoke"
      • Types/Annotations that add runtime checks only needed for the Web API "spoke"
    • added:
      • core-wasm reference types
      • A binary encoding of all this in the custom section�
  • Therefore, just saying "Web IDL" is not entirely accurate and confusing
    • Especially if you don't have a Web background and go look at https://heycam.github.io/webidl

27 of 32

Proposed spec factoring

wasm spec

unchanged

wasm ☃Bindings spec

new spec document defining:

☃Binding values + ☃Binding exprs + ☃Binding types

(symmetric to current Web IDL Bindings Explainer)

new cases in `read the imports`, `ToJSValue`, `ToWebAssemblyValue`

wasm JS API spec

Web IDL spec

new "WebAssembly Bindings" section, sibling to "ECMAScript Bindings".

WASI spec

Name not finalized

=

JS spec

unchanged

28 of 32

☃Binding Types rough sketch

reftype ::= ... all the core wasm reference types, including references to type imports

numtype ::= s8 | u8 | s16 | u16 | s32 | u32 | s64 | u64 | f32 | f64 // because signedness matters

bindingtype ::= // Web IDL type� reftype | // ↔ any, Interface, Promise, ...� numtype | // ↔ byte, octet, short, ...� string | // ↔ DOMString (need to think about USVString...)� bytes | // ↔ ArrayBuffer� numtype view | // ↔ Int8Array, Uint8Array, ...� bindingtype list | // ↔ Sequence� record{ (lbl: bindingtype)* } | // ↔ Dictionary� variant{ (lbl: bindingtype)* } | // ↔ Union, Enumeration func(bindingtype* → bindingtype*) // ↔ Callback function

These look a lot like

wasm GC typedefs,

it might be elegant to unify... but labels (because dictionaries)

29 of 32

☃Binding Expressions sampler

s16

i32

i32

as

as

ref T

ref T

ref T

as

as

string

utf8-mem-str

alloc-utf8-mem-str $alloc $free

i32

i32

i32

i32

ref array u8

utf8-gc-str

ref array u8

alloc-utf8-gc-str

ref T

T=string

as

ref T

T=string

as

record {� x:string,

y:string�}

string

string

record

...

...

string

string

...

...

field "x"

field "y"

ref struct ...

as

ref struct ...

as

outgoing / lifting expression

incoming / lowering expression

wasm type

wasm type

binding�type

30 of 32

Refactored star(-ish) topology

Web

APIs

JS

wasm�module�A

WASI

wasm�module�B

Web IDL spec: ECMAScript Binding

☃Binding Expressions (specified by A)

☃Binding Expressions (specified by B)

Identity function

☃Binding�types

Web IDL

Web IDL spec: WebAssembly Binding

31 of 32

Frequently Asked Questions

  • It’s a bird, no it’s a plane; no it’s an IDL. What gives?�This is not your father’s IDL - in part because of binding expressions
  • Binding expressions bind what?�Binding expressions are declarative expressions defining how to lift to/lower from an API interface
  • How many binding operators?�We currently have approximately 20 different operators. Easy to add more.
  • Immutable, schmutable. Just give me the data already.�In perfect circumstances it will be possible to avoid the overhead of copying.
  • Will it work with threads?�Completely orthogonal
  • Will it be faaast?�That’s the idea
  • Will I be able to use this to have my own API?�Definitely. But, it’s intended to allow exposure/use of coherent functionality; not really for library construction
  • Why not Cap’n Proto / protobuf / FlatBuffers / etc�These are primarily serialization-based approaches; which is not relevant for bindings. However, we are definitely inspired by them.
  • ...

32 of 32

Poll

Guidance from CG to do more work to refactor the Web IDL Bindings spec in the manner proposed (with future poll to actually accept refactoring).