WebIDL
in ECMAScript
Language Specification
Introduction
WebIDL in ECMAScript Specification
Introduction
ECMAScript
Spec HTML
ECMAScript
Spec WebIDL
C++ Binding
Annotations
Types
...
...
...
Motivation
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
[Exposed=Window,
InstrumentedProps=(attributeStyleMap,hidePopover,popover,showPopover,togglePopover)]
interface HTMLElement : Element {
[HTMLConstructor] constructor();
// metadata attributes
[CEReactions] attribute DOMString title;
...
}
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
ECMAScript built-ins - current
Motivation
Hand-written
C++/JS
Implementation
ECMAScript
Spec
manually
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
Why WebIDL
Motivation
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
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
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
Previous Works
IDL for ECMAScript
https://github.com/tc39/proposal-idl
Previous Works
JSIDL
https://w3ctag.github.io/jsidl/jsidl.html
Previous Works
Syntactic changes to WebIDL
https://github.com/whatwg/webidl/issues/485
Previous Works
Proposed Method
Fundamental policy (1)
Proposed Method
Fundamental policy (2)
Proposed Method
Fundamental policy (3)
2 syntactic options
Proposed Method
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]] |
Proposed Method
Required modifications
(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
(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.
(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
(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
(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
(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
(5) Exotic/custom prototypes
Some ECMAScript built-in prototypes has custom internal slots
[CustomPrototype]
interface Number {
...
};
The Number prototype object:
Proposed Method - Required modifications
Extended attribute
The create custom prototype object steps for Number
is the following:
(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
(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𝔽.
(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.
...
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
Example
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
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
Applying Types
Coercion with abstract operations (1)
Applying Types
BigInt.asIntN ( bits, bigint )
...
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
Coercion with abstract operations (3)
Applying Types
BigInt.asIntN ( bits, bigint )
...
BigInt.asIntN (
type<ToIndex> bits,
type<ToBigInt> bigint )
...
Coercion with abstract operations (4)
Applying Types
BigInt.asIntN ( bits, bigint )
...
BigInt.asIntN (
type<ToIndex> bits,
bigint bigint )
...
Execution order (1)
Applying Types
String.prototype.slice (
start, end )
...
It performs the following steps when called:
Extra operations before
parameter type coercion
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
Execution order (3)
Applying Types
String.prototype.slice (
start, end )
...
It performs the following steps when called:
String.prototype.slice (
lazy<ToIntegerOrInfinity> start,
lazy<ToIntegerOrInfinity> end )
...
It performs the following steps when called:
These steps must be performed before the parameter coercion
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.
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
Integration
Possible integration plans
(stand-alone, or incremental)
Integration
Integration Examples
Array interface
Integration Examples
Steps for [[CustomPrototype]]
Integration Examples
Array Iterator Prototype
Integration Examples
Thank You
Appendix
Appendix (1)
Converting the spec HTML to WebIDL
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
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);
...
};
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
Appendix (2)
Applications
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
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
Appendix (3)
Proposed Method
Required modifications
"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
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
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
Aliases (1)
Some built-in members share single function value
interface Date {
[Alias="toGMTString"]
any toUTCString();
};
Appendix - Proposed Method - Required modifications
Extended attribute
Aliases (2)
Alias across interfaces
interface Array {
[Alias="TypedArray.prototype.toString"]
non_enumerable any toString();
};
Appendix - Proposed Method - Required modifications
Extended attribute
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
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
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
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
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
[[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
Appendix (4)
WebAPI vs ECMAScript built-ins
WebIDL and modifications
Appendix - WebAPI vs ECMAScript built-ins
Proposed
modifications
Current WebIDL
Used by WebAPI
property
prototype
@@symbol
instance
...
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
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
Validator for ECMAScript built-ins WebIDL
Appendix - WebAPI vs ECMAScript built-ins
ECMAScript
Spec WebIDL
Validator
PR WebIDL
Proposal WebIDL
Prohibit non-ECMAScript features
Also enforce ECMAScript-specific requirements
Validator for WebAPI
Appendix - WebAPI vs ECMAScript built-ins
WebAPI
Spec WebIDL
Validator
for WebAPI
Prohibit ECMAScript-specific features
Also enforce WebAPI-specific requirements
Appendix (5)
Implementation-specific annotations
Developer Tools Eager Evaluation
interface String {
[Pure]
non_enumerable String toLowerCase();
};
Appendix - Implementation-specific annotations
Evaluation result, without hitting Enter
Mark “side-effect-free”
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