1 of 82

WebIDL

in ECMAScript

Language Specification

2 of 82

Introduction

3 of 82

WebIDL in ECMAScript Specification

  • Represent ECMAScript built-ins with WebIDL
    • Auto-generated from ECMAScript Specification HTML
  • Allow automation with WebIDL definition
    • Auto-generate binding code
    • Extracting annotations

Introduction

ECMAScript

Spec HTML

ECMAScript

Spec WebIDL

C++ Binding

Annotations

Types

...

...

...

4 of 82

Motivation

5 of 82

Auto-generating bindings for Web API (1)

Motivation

Web API is defined as WebIDL file

C++

Implementation

C++ Binding

Web API

WebIDL

WebIDL Parser

Binding

Codegen

dom/webidl/HTMLElement.webidl

[Exposed=Window,

InstrumentedProps=(attributeStyleMap,hidePopover,popover,showPopover,togglePopover)]

interface HTMLElement : Element {

[HTMLConstructor] constructor();

// metadata attributes

[CEReactions] attribute DOMString title;

...

}

6 of 82

Auto-generating bindings for Web API (1)

Motivation

WebIDL is converted into C++ binding code

C++

Implementation

C++ Binding

__GENERATED__/dom/bindings/HTMLElementBinding.cpp

MOZ_CAN_RUN_SCRIPT static bool

get_title(JSContext* cx, JS::Handle<JSObject*> obj,

void* void_self, JSJitGetterCallArgs args) {

AUTO_PROFILER_LABEL_DYNAMIC_FAST(

"HTMLElement", "title", DOM, cx,

uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |

uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));

auto* self = static_cast<nsGenericHTMLElement*>(void_self);

...

};

Web API

WebIDL

WebIDL Parser

Binding

Codegen

7 of 82

ECMAScript built-ins - current

Motivation

Hand-written

C++/JS

Implementation

ECMAScript

Spec

manually

8 of 82

ECMAScript built-ins - with WebIDL

If we have WebIDL definition for ECMAScript

Motivation

C++/JS

Implementation

C++ Binding

WebIDL Parser

Binding

Codegen

ECMAScript

Spec WebIDL

Use the same code as other WebAPI

9 of 82

Why WebIDL

  • Infrastructure reuse
    • parser, binding generator, annotations, build system
  • Maintenance
    • Shared costs across teams, cross-platform collaboration
  • Inter-specification Automation and Optimization
    • structured data source of the spec
  • Improving the developer experience of third party embeddings
    • Web API for non-browsers

Motivation

10 of 82

Infrastructure reuse (1)

Motivation

Share WebIDL parser, binding generator

C++/JS

Implementation

C++ Binding

ECMAScript

Spec WebIDL

WebIDL Parser

Binding

Codegen

Web API

WebIDL

Embedder’s

API WebIDL

Same infrastructure for all API

11 of 82

Infrastructure reuse (2)

Motivation

WebAPI, ECMAScript built-ins, embedder’s API as data source

C++/JS

Implementation

C++ Binding

ECMAScript

Spec WebIDL

WebIDL Parser

Binding

Codegen

Web API

WebIDL

Embedder’s

API WebIDL

Well-known Atoms

DevTools

Side-Effect-free

Function list

12 of 82

WebIDL from the spec

Generate WebIDL from the spec

Motivation

ECMAScript

Spec HTML

Converter

C++

Implementation

C++ Binding

WebIDL Parser

Binding

Codegen

ECMAScript

Spec WebIDL

13 of 82

Previous Works

14 of 82

IDL for ECMAScript

https://github.com/tc39/proposal-idl

  • Investigation for the use of IDL in ECMAScript
    • Motivations
      • ease of following conventions
      • auto-generation of bindings
      • “type” for toolings
    • Requirements
      • laziness
      • intuitive syntax
      • ...
    • Comparison between IDLs
      • WebIDL
      • JSIDL

Previous Works

15 of 82

JSIDL

https://w3ctag.github.io/jsidl/jsidl.html

  • ECMAScript-focused IDL
    • type coercion
      • any
      • ToBoolean. ToInteger, ...
      • options
      • enum
      • ...
    • function arguments
      • default

Previous Works

16 of 82

Syntactic changes to WebIDL

https://github.com/whatwg/webidl/issues/485

  • Discussion to make WebIDL more “JavaScript-y”
    • class
    • extends
    • : Type
    • get/set
    • ...

Previous Works

17 of 82

Proposed Method

18 of 82

Fundamental policy (1)

  • No normative changes
    • Purely-editorial changes
    • Either just add the WebIDL definition, or move some definition to WebIDL
  • No change to existing abstract operations
    • Doesn’t affect the consumers of abstract operations, across specifications
  • Small changes to existing operations
    • Parameter coercion can be moved to the types

Proposed Method

19 of 82

Fundamental policy (2)

  • ECMAScript built-ins directly representable with WebIDL constructs�→ Use the WebIDL constructs as-is
  • ECMAScript built-ins which has slight difference than WebIDL constructs�→ Introduce annotation to modify the behavior
  • ECMAScript built-ins which has large difference than WebIDL constructs�→Introduce new syntax, or annotation to replace the behavior

Proposed Method

20 of 82

Fundamental policy (3)

2 syntactic options

  • New syntax
    • Directly represent the new concept
    • Improved readability
    • Provided when applicable
  • Extended attributes
    • Modify/replace the existing syntax
    • Minimize the modification to existing parser
    • Always provided

Proposed Method

21 of 82

Applicable constructs in WebIDL

Proposed Method

ECMAScript Built-ins

WebIDL constructs

Constructor + Prototype

Interface

Singleton

Namespace

Method

Operation

Accessor

Attribute

Property on constructor and prototype

Constant

[[Prototype]] slot

Inheritance

@@unscopables

[[Unscopable]]

22 of 82

Proposed Method

Required modifications

23 of 82

(1) Plain data property

Writable, non-enumerable, configurable data property

interface Error {

property any message = "";

};

interface Error {

[DataProperty, StringValue=""]

attribute any message;

};

Proposed Method - Required modifications

New syntax

Extended attribute

24 of 82

(2) Writability, Enumerability, Configurability

ECMAScript built-ins methods are non-enumerable

namespace Math {

non_enumerable any abs(any x);

};

namespace Math {

[NonEnumerable]

any abs(any x);

};

Proposed Method - Required modifications

New syntax

Extended attribute

Apply similar modification for writability and configurability.

25 of 82

(3-1) Symbol keys

WebIDL doesn’t have symbol tokens

interface Iterator {

attribute any @@toStringTag;

};

interface Iterator {

[Name="Symbol.toStringTag"]

attribute any _symbol_toStringTag;

};

Proposed Method - Required modifications

New syntax

Extended attribute

26 of 82

(3-2) Symbol keys

Another option: computed property

Doesn’t conflict with extended attribute, but confusing

interface Iterator {

attribute any [Symbol.toStringTag];

};

Proposed Method - Required modifications

Not-chosen

new syntax

27 of 82

(4-1) Prototypes without consturctors

Some ECMAScript built-in prototypes don’t have constructor

prototype ForInIteratorPrototype {

any next();

};

[PrototypeObject]

namespace ForInIteratorPrototype {

any next();

};

Proposed Method - Required modifications

New syntax

Extended attribute

28 of 82

(4-2) Prototype inheritance

Represent the prototype object’s [[Prototype]] slot

prototype ForInIteratorPrototype : Iterator.prototype {

...

};

[PrototypeSlot="Iterator.prototype"]

namespace ForInIteratorPrototype {

...

};

Proposed Method - Required modifications

New syntax

Extended attribute

29 of 82

(5) Exotic/custom prototypes

Some ECMAScript built-in prototypes has custom internal slots

[CustomPrototype]

interface Number {

...

};

The Number prototype object:

  • is %Number.prototype%.
  • is an ordinary object.
  • is itself a Number object; it has a [[NumberData]]�internal slot with the value +0𝔽.
  • has a [[Prototype]] internal slot whose value�is %Object.prototype%.

Proposed Method - Required modifications

Extended attribute

The create custom prototype object steps for Number

is the following:

  1. Let P be ? MakeBasicObject(« [[NumberData]] »).
  2. Set P.[[Prototype]] be %Object.prototype%.
  3. Set P.[[NumberData]] to +0𝔽.
  4. Return P.

30 of 82

(6) Instance properties

Having instance properties on the WebIDL allows more automation usage

interface RegExp {

instance property any lastIndex;

};

interface RegExp {

[Instance, DataProperty]

attribute any lastIndex;

};

Proposed Method - Required modifications

New syntax

Extended attribute

31 of 82

(7) Function length

Some ECMAScript built-in method has non-default function length

interface Array {

[Length="1"]

any unshift(any... items);

};

Proposed Method - Required modifications

Extended attribute

23.1.3.37 Array.prototype.unshift ( ...items )

...

The "length" property of this method is 1𝔽.

32 of 82

(8) Brand check

WebIDL operations have brand check

Some ECMAScript built-ins don’t need.

Some other ECMAScript built-ins performs on its own.

[NoBrandCheck]

interface Array {

any at(any index);

};

Proposed Method - Required modifications

Extended attribute

2. Let steps be the following series of steps,

given function argument values args:

1. Try running the following steps:

1. Let idlObject be null.

2. If target is an interface, and

op is not a static operation:

1. Let jsValue be the this value, if it is not null

or undefined, or realm’s global object otherwise.

(...)

2. If jsValue is a platform object, then perform

a security check, passing jsValue, id, and "method".

3. If jsValue does not implement the interface target,

throw a TypeError.

4. Set idlObject to the IDL interface type value that

represents a reference to jsValue.

3. Let n be the size of args.

...

33 of 82

Some more

There are some more modifications explained in the document

https://docs.google.com/document/d/1kj7VQ-LOfg-rq59vPEaJFmL6EJsKBwJ52AZzIGfdc_0/edit?tab=t.0

(also explained in Appendix)

Proposed Method - Required modifications

34 of 82

Example

35 of 82

With the extended attributes

[Exposed=*, NoBrandCheck, NoNewTargetCheck, NoObjectCreation, NoUnforgeablesSlot, CustomPrototype]

interface Function {

[Length="1"]

constructor(any... parameterArgs /* , any bodyArg */);

[NonEnumerable] any apply(any thisArg, any argArray);

[NonEnumerable] any bind(any thisArg, any... args);

[NonEnumerable] any call(any thisArg, any... args);

[Name="toString", NonEnumerable] any _toString();

[Name="Symbol.hasInstance", ReadOnly, NonEnumerable, NonConfigurable]

any _symbol_hasInstance(any V);

[DataProperty, NonEnumerable, Configurable, NumberValue="0"]

attribute any length;

[DataProperty, NonEnumerable, Configurable, StringValue=""]

attribute any name;

[Instance, DataProperty, NonEnumerable, Configurable]

attribute any length;

[Instance, DataProperty, NonEnumerable, Configurable]

attribute any name;

[Instance, Name="prototype", DataProperty, NonEnumerable, NonConfigurable]

attribute any _prototype;

};

Example

36 of 82

With the new syntax

[Exposed=*, NoBrandCheck, NoNewTargetCheck, NoObjectCreation, NoUnforgeablesSlot, CustomPrototype]

interface Function {

[Length="1"]

constructor(any... parameterArgs /* , any bodyArg */);

non_enumerable any apply(any thisArg, any argArray);

non_enumerable any bind(any thisArg, any... args);

non_enumerable any call(any thisArg, any... args);

non_enumerable any toString();

readonly non_enumerable non_configurable any @@hasInstance(any V);

property any length = 0;

property any name = "";

instance readonly property any length;

instance readonly property any name;

instance non_configurable property any prototype;

};

Example

37 of 82

Applying Types

38 of 82

Coercion with abstract operations (1)

Applying Types

BigInt.asIntN ( bits, bigint )

...

  1. Set bits to ? ToIndex(bits).
  2. Set bigint to ? ToBigInt(bigint).
  3. Let mod be ℝ(bigint) modulo 2**bits.
  4. If mod ≥ 2**(bits - 1), return ℤ(mod - 2**bits); otherwise, return ℤ(mod).

39 of 82

Coercion with abstract operations (2)

Use abstract operation names as type names

namespace BigInt {

non_enumerable any asIntN(type<ToIndex> bits,

type<ToBigInt> bigint);

};

namespace BigInt {

non_enumerable any asIntN(ToIndex bits,

ToBigInt bigint);

};

Applying Types

New syntax

Existing syntax

40 of 82

Coercion with abstract operations (3)

Applying Types

BigInt.asIntN ( bits, bigint )

...

  • Set bits to ? ToIndex(bits).
  • Set bigint to ? ToBigInt(bigint).
  • Let mod be ℝ(bigint) modulo 2**bits.
  • If mod ≥ 2**(bits - 1), return ℤ(mod - 2**bits); otherwise, return ℤ(mod).

BigInt.asIntN (

type<ToIndex> bits,

type<ToBigInt> bigint )

...

  • Let mod be ℝ(bigint) modulo 2**bits.
  • If mod ≥ 2**(bits - 1), return ℤ(mod - 2**bits); otherwise, return ℤ(mod).

41 of 82

Coercion with abstract operations (4)

Applying Types

BigInt.asIntN ( bits, bigint )

...

  • Set bits to ? ToIndex(bits).
  • Set bigint to ? ToBigInt(bigint).
  • Let mod be ℝ(bigint) modulo 2**bits.
  • If mod ≥ 2**(bits - 1), return ℤ(mod - 2**bits); otherwise, return ℤ(mod).

BigInt.asIntN (

type<ToIndex> bits,

bigint bigint )

...

  • Let mod be ℝ(bigint) modulo 2**bits.
  • If mod ≥ 2**(bits - 1), return ℤ(mod - 2**bits); otherwise, return ℤ(mod).

42 of 82

Execution order (1)

Applying Types

String.prototype.slice (

start, end )

...

It performs the following steps when called:

  1. Let O be ? RequireObjectCoercible(this value).
  2. Let S be ? ToString(O).
  3. Let len be the length of S.
  4. Let intStart be ? ToIntegerOrInfinity(start).
  5. ...

Extra operations before

parameter type coercion

43 of 82

Execution order (2)

Lazily perform the coercion, to keep the execution order

interface String {

non_enumerable any slice(

lazy<ToIntegerOrInfinity> start,

lazy<ToIntegerOrInfinity> end);

};

interface String {

non_enumerable any slice(

[Lazy] ToIntegerOrInfinity start,

[Lazy] ToIntegerOrInfinity end);

};

Applying Types

New syntax

Extended attribute

44 of 82

Execution order (3)

Applying Types

String.prototype.slice (

start, end )

...

It performs the following steps when called:

  • Let O be ? RequireObjectCoercible(this value).
  • Let S be ? ToString(O).
  • Let len be the length of S.
  • Let intStart be ? ToIntegerOrInfinity(start).
  • ...

String.prototype.slice (

lazy<ToIntegerOrInfinity> start,

lazy<ToIntegerOrInfinity> end )

...

It performs the following steps when called:

  • Let O be ? RequireObjectCoercible(this value).
  • Let S be ? ToString(O).
  • Let len be the length of S.
  • Let intStart be ? delazify(start).
  • ...

These steps must be performed before the parameter coercion

45 of 82

Enums with RangeError (1)

Applying Types

String.prototype.normalize ( [ form ] )

...

4. Else, let f be ? ToString(form).

5. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError exception.

46 of 82

Enums with RangeError (2)

Use RangeError for erroneous value in enums

[UseRangeError]

enum NormalizationForm { "NFC", "NFD", "NFKC", “NFKD" };

interface String {

String normalize(optional NormalizationForm form = “NFC”);

};

Applying Types

Extended attribute

47 of 82

Integration

48 of 82

Possible integration plans

  1. Auto-generate the WebIDL definition from the HTML file, and provide it as an appendix
  2. Provide an alternative view with the WebIDL definition integrated into the spec HTML file
  3. Fully integrate the WebIDL definition into the spec

(stand-alone, or incremental)

Integration

49 of 82

Integration Examples

50 of 82

Array interface

Integration Examples

51 of 82

Steps for [[CustomPrototype]]

Integration Examples

52 of 82

Array Iterator Prototype

Integration Examples

53 of 82

Thank You

54 of 82

Appendix

55 of 82

Appendix (1)

Converting the spec HTML to WebIDL

56 of 82

ECMAScript spec to WebIDL (1)

Appendix - Converting the spec HTML to WebIDL

This is about Array constructor

Global property name

Constructor’s parameters

length property

57 of 82

ECMAScript spec to WebIDL (2)

Appendix - Converting the spec HTML to WebIDL

Generate IDL from collected information

ECMAScript

Spec HTML

Converter

ECMAScript

Spec WebIDL

WebIDL Parser

...

interface Array {

[Length="1"]

constructor(any... values);

static any from(any items, optional any mapfn,

optional any thisArg);

...

};

58 of 82

ECMAScript spec to WebIDL (3)

Appendix - Converting the spec HTML to WebIDL

interface Array {

[Length="1"]

constructor(any... values);

static any from(any items, optional any mapfn,

optional any thisArg);

...

};

Object → interface

Non-default length is represented as extended attribute

variadic parameter → any...

optional parameter → optional any

constructor property → static

59 of 82

Appendix (2)

Applications

60 of 82

Proposals with partial interface

Represent the API change in WebIDL

Example: Upsert Proposal

partial interface Map {

any getOrInsert(any key, any value);

any getOrInsertComputed(any key, any callbackfn);

};

partial interface WeakMap {

any getOrInsert(any key, any value);

any getOrInsertComputed(any key, any callbackfn);

};

Appendix - Applications

61 of 82

User-defined ECMAScript modules with WebIDL

module “std::foo” {

interface Bar {

non_enumerable double sum(double x, double y);

};

};

[LegacyNamespace=”std”]

namespace foo {};

[LegacyNamespace=”foo”]

interface Bar { ... };

Appendix - Applications

New syntax

Extended attribute

62 of 82

Appendix (3)

Proposed Method

Required modifications

63 of 82

"constructor" accessor

Iterator.prototype.constructor is an accessor

interface Iterator {

constructor();

non_enumerable attribute any constructor;

};

interface Iterator {

constructor();

[Name="constructor", NonEnumerable]

attribute any _constructor;

};

Appendix - Proposed Method - Required modifications

Modified syntax

Extended attribute

Override the

Iterator.prototype.constructor

property definition

64 of 82

Symbol.toStringTag

Some ECMAScript built-ins’ @@toStringTag needs special handling

prototype GeneratorPrototype {

readonly property any @@toStringTag = "Generator";

};

namespace GeneratorPrototype {

[Name="Symbol.toStringTag", DataProperty,

NonEnumerable, Configurable, StringValue="Generator"]

attribute any _symbol_toStringTag;

};

Appendix - Proposed Method - Required modifications

New syntax

Extended attribute

65 of 82

Object.prototype [[Prototype]]

Object.prototype’s [[Prototype]] is null

interface Object : null {

...

};

[PrototypeSlot=null]

interface Object {

...

};

Appendix - Proposed Method - Required modifications

New syntax

Extended attribute

66 of 82

Aliases (1)

Some built-in members share single function value

interface Date {

[Alias="toGMTString"]

any toUTCString();

};

Appendix - Proposed Method - Required modifications

Extended attribute

67 of 82

Aliases (2)

Alias across interfaces

interface Array {

[Alias="TypedArray.prototype.toString"]

non_enumerable any toString();

};

Appendix - Proposed Method - Required modifications

Extended attribute

68 of 82

Identifiers with underscore

Identifiers with leading underscore needs special handling

interface Object {

non_enumerable attribute any ___proto__;

};

interface Object {

[Name="__proto__", NonEnumerable]

attribute any _proto;

};

Appendix - Proposed Method - Required modifications

Modified syntax

Extended attribute

Remove only one leading underscore, not all

Override with the actual name

69 of 82

Object reference

Property with other object as an initial value

prototype GeneratorPrototype {

readonly property any constructor =

GeneratorFunction.prototype;

};

namespace GeneratorPrototype {

[Name="constructor", DataProperty,

ObjectValue="GeneratorFunction.prototype"]

attribute any _constructor;

};

Appendix - Proposed Method - Required modifications

New syntax

Extended attribute

70 of 82

Special values

Properties with some special numeric values

namespace Math {

static readonly non_configurable

property any EPSILON = EPSILON;

};

namespace Math {

[DataProperty, NonEnumerable, NonConfigurable,

NumberValue="EPSILON"]

static attribute any EPSILON;

};

Appendix - Proposed Method - Required modifications

New syntax

Extended attribute

71 of 82

NewTarget check

Some ECMAScript built-in constructors don’t require new

[NoNewTargetCheck]

interface Array {

[Length="1"]

constructor(any... values);

};

Appendix - Proposed Method - Required modifications

Extended attribute

72 of 82

Object creation

ECMAScript built-in constructors need to create the object by its own

[NoObjectCreation]

interface Array {

[Length="1"]

constructor(any... values);

};

Appendix - Proposed Method - Required modifications

Extended attribute

73 of 82

[[Unforgeables]] slot

ECMAScript built-in constructors doesn’t use [[Unforgeables]] slot

[NoUnforgeablesSlot]

interface Array {

[Length="1"]

constructor(any... values);

};

Appendix - Proposed Method - Required modifications

Extended attribute

74 of 82

Appendix (4)

WebAPI vs ECMAScript built-ins

75 of 82

WebIDL and modifications

Appendix - WebAPI vs ECMAScript built-ins

Proposed

modifications

Current WebIDL

Used by WebAPI

property

prototype

@@symbol

instance

...

76 of 82

Things used by ECMAScript built-ins

Appendix - WebAPI vs ECMAScript built-ins

Used by ECMAScript spec

Current WebIDL

interface

attribute

namespace

any

...

property

prototype

@@symbol

instance

...

Proposed

modifications

77 of 82

Not-applicable syntax and semantics

Appendix - WebAPI vs ECMAScript built-ins

Shouldn’t be used by

ECMAScript spec/PR/proposals

maplike

async iterable

stringifier

DOM exceptions

...

Current WebIDL

Proposed

modifications

78 of 82

Validator for ECMAScript built-ins WebIDL

Appendix - WebAPI vs ECMAScript built-ins

ECMAScript

Spec WebIDL

Validator

PR WebIDL

Proposal WebIDL

Prohibit non-ECMAScript features

  • Properties are non-enumerable
  • No DOM-specific extended attributes
  • Opt out of brand check
  • Opt out of unforgeables

Also enforce ECMAScript-specific requirements

79 of 82

Validator for WebAPI

Appendix - WebAPI vs ECMAScript built-ins

WebAPI

Spec WebIDL

Validator

for WebAPI

Prohibit ECMAScript-specific features

  • No properties
  • No prototype-only objects
  • No ECMAScript-specific extended attributes

Also enforce WebAPI-specific requirements

  • No legacy extended attributes for new interfaces
  • ...

80 of 82

Appendix (5)

Implementation-specific annotations

81 of 82

Developer Tools Eager Evaluation

interface String {

[Pure]

non_enumerable String toLowerCase();

};

Appendix - Implementation-specific annotations

Evaluation result, without hitting Enter

Mark “side-effect-free”

82 of 82

Features behind runtime settings

interface Promise {

[Func=”JS::Prefs::experimental_promise_try”]

static non_enumerable any try(

any callback, any... args);

};

Features from proposals are hidden behind runtime settings

Appendix - Implementation-specific annotations