diff --git a/design/high-level/FAQ.md b/design/high-level/FAQ.md index 7318c0761..f2316c991 100644 --- a/design/high-level/FAQ.md +++ b/design/high-level/FAQ.md @@ -1,5 +1,11 @@ # FAQ +### How do WebAssembly Components relate to Web Components + +[Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) are a collection of technologies for creating reusable DOM custom elements. WebAssembly Components are a technology for creating reusable software interfaces (not tied to a single language or UI framework). See these [slides](https://docs.google.com/presentation/d/1PSC3Q5oFsJEaYyV5lNJvVgh-SNxhySWUqZ6puyojMi8/edit?slide=id.gced688a2b6_0_9#slide=id.gced688a2b6_0_9) for a rationale for this naming. + +Whenever there is a chance of ambiguity, the full WebAssembly (or Wasm) Component name should be used. + ### How does WASI relate to the Component Model? [WASI] is layered on top of the Component Model, with the Component Model diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md index b0cee058f..55a3c2e22 100644 --- a/design/mvp/Explainer.md +++ b/design/mvp/Explainer.md @@ -3082,227 +3082,7 @@ In particular, the Component Model maintains the following invariants: ## JavaScript Embedding -### JS API - -The [JS API] currently provides `WebAssembly.compile(Streaming)` which take -raw bytes from an `ArrayBuffer` or `Response` object and produces -`WebAssembly.Module` objects that represent decoded and validated modules. To -natively support the Component Model, the JS API would be extended to allow -these same JS API functions to accept component binaries and produce new -`WebAssembly.Component` objects that represent decoded and validated -components. The [binary format of components](Binary.md) is designed to allow -modules and components to be distinguished by the first 8 bytes of the binary -(splitting the 32-bit [`core:version`] field into a 16-bit `version` field and -a 16-bit `layer` field with `0` for modules and `1` for components). - -Once compiled, a `WebAssembly.Component` could be instantiated using the -existing JS API `WebAssembly.instantiate(Streaming)`. Since components have the -same basic import/export structure as modules, this means extending the [*read -the imports*] logic to support single-level imports as well as imports of -modules, components and instances. Since the results of instantiating a -component is a record of JavaScript values, just like an instantiated module, -`WebAssembly.instantiate` would always produce a `WebAssembly.Instance` object -for both module and component arguments. - -Types are a new sort of definition that are not ([yet][type-imports]) present -in Core WebAssembly and so the [*read the imports*] and [*create an exports -object*] steps need to be expanded to cover them: - -For type exports, each type definition would export a JS constructor function. -This function would be callable iff a `[constructor]`-annotated function was -also exported. All `[method]`- and `[static]`-annotated functions would be -dynamically installed on the constructor's prototype chain, making sure to -register `[get]` and `[set]` functions as getters and setters. In the case of -re-exports and multiple exports of the same definition, the same constructor -function object would be exported (following the same rules as WebAssembly -Exported Functions today). In pathological cases (which, importantly, don't -concern the global namespace, but involve the same actual type definition being -imported and re-exported by multiple components), there can be collisions when -installing constructors, methods and statics on the same constructor function -object. In such cases, a conservative option is to undo the initial -installation and require all clients to instead use the full explicit names -as normal instance exports. - -For type imports, the constructors created by type exports would naturally -be importable. Additionally, certain JS- and Web-defined objects that correspond -to types (e.g., the `RegExp` and `ArrayBuffer` constructors or any Web IDL -[interface object]) could be imported. The `ToWebAssemblyValue` checks on -handle values mentioned below can then be defined to perform the associated -[internal slot] type test, thereby providing static type guarantees for -outgoing handles that can avoid runtime dynamic type tests. - -Lastly, when given a component binary, the compile-then-instantiate overloads -of `WebAssembly.instantiate(Streaming)` would inherit the compound behavior of -the abovementioned functions (again, using the `layer` field to eagerly -distinguish between modules and components). - -For example, the following component: -```wat -;; a.wasm -(component - (import "one" (func)) - (import "two" (value string)) ๐Ÿช™ - (import "three" (instance - (export "four" (instance - (export "five" (core module - (import "six" "a" (func)) - (import "six" "b" (func)) - )) - )) - )) - ... -) -``` -and module: -```wat -;; b.wasm -(module - (import "six" "a" (func)) - (import "six" "b" (func)) - ... -) -``` -could be successfully instantiated via: -```js -WebAssembly.instantiateStreaming(fetch('./a.wasm'), { - one: () => (), - two: "hi", ๐Ÿช™ - three: { - four: { - five: await WebAssembly.compileStreaming(fetch('./b.wasm')) - } - } -}); -``` - -The other significant addition to the JS API would be the expansion of the set -of WebAssembly types coerced to and from JavaScript values (by [`ToJSValue`] -and [`ToWebAssemblyValue`]) to include all of [`valtype`](#type-definitions). -At a high level, the additional coercions would be: - -| Type | `ToJSValue` | `ToWebAssemblyValue` | -| ---- | ----------- | -------------------- | -| `bool` | `true` or `false` | `ToBoolean` | -| `s8`, `s16`, `s32` | as a Number value | `ToInt8`, `ToInt16`, `ToInt32` | -| `u8`, `u16`, `u32` | as a Number value | `ToUint8`, `ToUint16`, `ToUint32` | -| `s64` | as a BigInt value | `ToBigInt64` | -| `u64` | as a BigInt value | `ToBigUint64` | -| `f32`, `f64` | as a Number value | `ToNumber` | -| `char` | same as [`USVString`] | same as [`USVString`], throw if the USV length is not 1 | -| `record` | TBD: maybe a [JS Record]? | same as [`dictionary`] | -| `variant` | see below | see below | -| `list` | create a typed array copy for number types; otherwise produce a JS array (like [`sequence`]) | same as [`sequence`] | -| `string` | same as [`USVString`] | same as [`USVString`] | -| `tuple` | TBD: maybe a [JS Tuple]? | TBD | -| `flags` | TBD: maybe a [JS Record]? | same as [`dictionary`] of optional `boolean` fields with default values of `false` | -| `enum` | same as [`enum`] | same as [`enum`] | -| `option` | same as [`T?`] | same as [`T?`] | -| `result` | same as `variant`, but coerce a top-level `error` return value to a thrown exception | same as `variant`, but coerce uncaught exceptions to top-level `error` return values | -| `map` | `new Map(_)` | `Map`s directly or other objects via `Object.entries(_)` | -| `own`, `borrow` | see below | see below | -| `future` | to a `Promise` | from a `Promise` | -| `stream` | to a `ReadableStream` | from a `ReadableStream` | - -Notes: -* Function parameter names are ignored since JavaScript doesn't have named - parameters. -* If a function's result type list is empty, the JavaScript function returns - `undefined`. If the result type list contains a single unnamed result, then - the return value is specified by `ToJSValue` above. Otherwise, the function - result is wrapped into a JS object whose field names are taken from the result - names and whose field values are specified by `ToJSValue` above. -* In lieu of an existing standard JS representation for `variant`, the JS API - would need to define its own custom binding built from objects. As a sketch, - the JS values accepted by `(variant (case "a" u32) (case "b" string))` could - include `{ tag: 'a', value: 42 }` and `{ tag: 'b', value: "hi" }`. -* For `option`, when Web IDL doesn't support particular type - combinations (e.g., `(option (option u32))`), the JS API would fall back to - the JS API of the unspecialized `variant` (e.g., - `(variant (case "some" (option u32)) (case "none"))`, despecializing only - the problematic outer `option`). -* When coercing `ToWebAssemblyValue`, `own` and `borrow` handle types would - dynamically guard that the incoming JS value's dynamic type was compatible - with the imported resource type referenced by the handle type. For example, - if a component contains `(import "Object" (type $Object (sub resource)))` and - is instantiated with the JS `Object` constructor, then `(own $Object)` and - `(borrow $Object)` could accept JS `object` values. -* When coercing `ToJSValue`, handle values would be wrapped with JS objects - that are instances of the handles' resource type's exported constructor - (described above). For `own` handles, a [`FinalizationRegistry`] would be - used to drop the `own` handle (thereby calling the resource destructor) when - its wrapper object was unreachable from JS. For `borrow` handles, the wrapper - object would become dynamically invalid (throwing on any access) at the end - of the export call. -* When an imported JavaScript function is a built-in function wrapping a Web - IDL function, the specified behavior should allow the intermediate JavaScript - call to be optimized away when the types are sufficiently compatible, falling - back to a plain call through JavaScript when the types are incompatible or - when the engine does not provide a separate optimized call path. - - -### ESM-integration - -Like the JS API, [ESM-integration] can be extended to load components in all -the same places where modules can be loaded today, branching on the `layer` -field in the binary format to determine whether to decode as a module or a -component. - -When present, the [`external-id`](#import-and-export-definitions) attribute of -an `import` would be used as the [Module Specifier], thereby giving components -the same naming expressivity as JavaScript (in particular, for importing URLs). -In the absence of an `external-id`, the always-present, but syntactically- -restrictive, `externname` of the import would be used instead. - -The main remaining question is how to deal with component imports having a -single string as well as the new importable component, module and instance -types. Going through these one by one: - -For component imports of module type, we need a new way to request that the ESM -loader parse or decode a module without *also* instantiating that module. -Recognizing this same need from JavaScript, there is a TC39 proposal called -[Import Reflection] that adds the ability to write, in JavaScript: -```js -import Foo from "./foo.wasm" as "wasm-module"; -assert(Foo instanceof WebAssembly.Module); -``` -With this extension to JavaScript and the ESM loader, a component import -of module type can be treated the same as `import ... as "wasm-module"`. - -Component imports of component type would work the same way as modules, -potentially replacing `"wasm-module"` with `"wasm-component"`. - -In all other cases, the (single) string imported by a component is first -resolved to a [Module Record] using the same process as resolving the -[Module Specifier] of a JavaScript `import`. After this, the handling of the -imported Module Record is determined by the import type: - -For imports of instance type, the ESM loader would treat the exports of the -instance type as if they were the [Named Imports] of a JavaScript `import`. -Thus, single-level imports of instance type act like the two-level imports -of Core WebAssembly modules where the first-level has been factored out. Since -the exports of an instance type can themselves be instance types, this process -must be performed recursively. - -Otherwise, function or value imports are treated like an [Imported Default Binding] -and the Module Record is converted to its default value. This allows the following -component: -```wat -;; bar.wasm -(component - (import "./foo.js" (func (result string))) - ... -) -``` -to be satisfied by a JavaScript module via ESM-integration: -```js -// foo.js -export default () => "hi"; -``` -when `bar.wasm` is loaded as an ESM: -```html - -``` - +This has been moved to [JS-Overview.md](JS-Overview.md) and [JS-Reference.md](JS-Reference.md). ## Examples diff --git a/design/mvp/JS-Explainer.md b/design/mvp/JS-Explainer.md new file mode 100644 index 000000000..38036f335 --- /dev/null +++ b/design/mvp/JS-Explainer.md @@ -0,0 +1,352 @@ +# WebAssembly Components JS-API Explainer + +This explainer describes how WebAssembly Components (hereafter 'components' and _not_ [web components](../high-level/FAQ.md#how-do-webassembly-components-relate-to-web-components)) can be used from JS in supported runtimes. + +See the [reference](./JS-Reference.md) for an in-depth walkthrough. + +**This is a draft and is not complete.** + +## Walkthrough + +### Components that export a function + +Let's start with a component that imports nothing: + +```wat +(component + ... + (export "greet" + (func (param "who" string) (result string)) + ) +) +``` + +Given the above component (compiled from the WAT text format to the [binary](./Binary.md) format), you can execute it with: + +```js +const { instance } = await WebAssembly.instantiate(bytes); + +instance.exports.greet("world"); // "hello, world" +``` + +The `exports` property of an instance holds one property per export and `greet` is an ordinary JS function + +Component names are kebab-case and JS function names are [camelCase](./JS-Reference.md#names), so a function export named `greet-loudly` would be `greetLoudly`. + +Arguments are coerced to their expected type, and if that fails a `TypeError` is thrown: + +```js +instance.exports.greet(42); // "hello, 42" +instance.exports.greet(); // TypeError +``` + +`undefined` is substituted for missing arguments, and extra arguments are ignored, just as in JS. + +### Components that import a function + +Now a component that imports: + +```wat +(component + (import "log" + (func (param "message" string)) + ) + ... + (export "run" (func)) +) +``` + +The import `log` must be a JS callable object, and will be called with a JS String. + +We can provide the `console.log` builtin as here: + +```js +const imports = { log: console.log }; + +const { instance } = + await WebAssembly.instantiate(bytes, imports); + +instance.exports.run(); // logs "hello" +``` + +Or provide a custom implementation: + +```js +const lines = []; +const log = (message) => { lines.push(message); }; +const imports = { log }; + +const { instance } = + await WebAssembly.instantiate(bytes, imports); +``` + +### Values at a glance + +Components and JS maintain separate type/value systems, so any value crossing the boundary needs a defined translation in both directions. + +The following table describes how a component value is converted into a JS value. + +| Component type | JS type | +|---|---| +| `bool` | Boolean | +| `s8`-`s32`, `u8`-`u32` | Number, an exact integer | +| `s64`, `u64` | BigInt | +| `f32`, `f64` | Number, including NaN and infinities | +| `char` | String of exactly one Unicode scalar value | +| `string` | String, well formed | +| `list` | `Uint8Array` | +| `list`, `list`, `tuple` | Array | +| `record { field-name: T, ... }` | null-prototype object, `{ fieldName: T, ... }` | +| `flags "flag-a" "flag-b"` | null-prototype object of Booleans, `{ flagA: bool, flagB: bool }` | +| `enum "case-a" "case-b"` | String, the case label verbatim | +| `option` (if `T` is not `option`) | `null`, or the payload | +| `option>` | treated as variant, see below | +| `result` (if in return position of function) | if `E` { thrown as a `WebAssembly.ComponentError` } else { `T` } | +| `variant` | `{ kind: string, value: T }` | +| `map` | `Map` | +| `own`, `borrow` | the original JS value for an imported resource type, an instance of its class for an exported one | +| `future`, `stream`, `error-context` | not yet specified | + +Converting a JS value to a component value accepts all of the above, but also has additional coercions. See [ToJSValue](./JS-Reference.md#tojsvalue) and [ToComponentValue](./JS-Reference.md#tocomponentvalue) for detailed algorithms. + +### Loading with ESM + +[ESM-integration](https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration) extends to components. The loader branches on the `layer` field of the binary, so a component loads anywhere a core module does today. + +Each component import becomes a JS import, and its module specifier is the import's [`external-id`](Explainer.md#import-and-export-definitions) if it has one and its name otherwise: + +```wat +(component + (import "slugify" + (external-id + "https://esm.unpkg.com/slugify@1.6.6") + (func (param "text" string) (result string)) + ) + ... + (export "run" (func)) +) +``` + +```html + +``` + +### Handling expected errors using `result` + +Component functions signal failure using a `result` value: + 1. Exported component functions that return an error `result` throw JS exceptions. + 1. Imported JS functions that throw JS exceptions are captured as a `result`. + +An imported JS function that throws where the component asked for a plain return type results in a trap. + +```wat +(component + (import "lookup" + (func (param "key" string) (result string (error string))) + ) + ... + (export "parse" + (func (param "text" string) (result u32 (error string))) + ) +) +``` + +`parse` tries to parse its `text` argument as an integer, and if that fails performs a fallible lookup. + +```js +const imports = { + lookup: (key) => { throw `no such key: ${key}`; }, +}; +const { instance } = + await WebAssembly.instantiate(bytes, imports); + +instance.exports.parse("42"); // 42 + +try { + instance.exports.parse("$name"); +} catch (e) { + e instanceof WebAssembly.ComponentError; // true + e.data; // "no such key: $name" +} +``` + +In the second call, parsing fails and leads to a call to `lookup` which throws a JS exception. This is converted to a `result` and consumed by the component. The component then propagates it to the original JS caller as a thrown `ComponentError` carrying the original message. + +### Handling unexpected failures with lockdown + +In the case a component is executing and traps, the trap is surfaced as a `WebAssembly.RuntimeError` (as in core wasm), and then the component is locked down to prevent future execution. If an exported function from the component is invoked again, it immediately results in another trap. + +```js +import { buggyFunction, normalFunction } from "component.wasm" + +// normalFunction doesn't trap +normalFunction(); + +try { + // Calling buggyFunction traps and locks down the component. + buggyFunction() +} catch (err) { + assert(err instanceof WebAssembly.RuntimeError); + try { + // Calling normalFunction again leads to a lockdown trap. + normalFunction(); + } catch (err) { + assert(err instanceof WebAssembly.RuntimeError); + } +} +``` + +### Importing JS values as resource types + +Components can also accept JS values as resources. A resource type import is satisfied by passing a constructor function. + +Whenever a JS value must be converted to a resource type, an `instanceof` check is performed against the imported constructor. If the constructor is a [WebIDL interface object](https://webidl.spec.whatwg.org/#interface-object) or an [exported component resource constructor](#exporting-a-resource), a precise [brand check](./JS-Reference.md#brand-checks) is performed. + +Any imported function whose name is tagged `[constructor]`, `[method]`, or `[static]` is looked up on the imported constructor instead of the imports object: + +| name | import lookup | +|---|---| +| `[constructor]R` | `R` | +| `[method]R.M` | `R.prototype.M` | +| `[static]R.S` | `R.S` | + +The above allows most JS classes to be imported as a resource by just passing the constructor function: + +```wat +(component + (import "element" + (type $element (sub resource)) + ) + (import + "[method]element.query-selector" + (func + (param "self" (borrow $element)) + (param "selectors" string) + (result (option (own $element))) + ) + ) + (import "[method]element.get-attribute" + (func + (param "self" (borrow $element)) + (param "name" string) + (result (option string)) + ) + ) + ... + (export "find" + (func + (param "root" (borrow $element)) + (param "selectors" string) + (result (option string)) + ) + ) +) +``` + +```js +const imports = { element: Element }; +const { instance } = + await WebAssembly.instantiate(bytes, imports); + +instance.exports.find(document.body, "h1"); // "page-title" or null +``` + +Component [`plainnames`](./Explainer.md#import-and-export-definitions) as used in imports/exports are converted to idiomatic JS names ([rules here](./JS-Reference.md#names)). Type names are converted to pascal case, and everything else is converted to camel case. This allows the component imports of `element` and `get-attribute` to be satisfied with `Element` and `getAttribute` respectively. + +### Importing from the JS global + +The example above still needs someone to write `{ element: Element }`. A component can skip that and take its imports straight from the global object by importing `wasm:js/global`: + +```wat +(component + (import "wasm:js/global" + (instance $g + (export "btoa" + (func (param "data" string) (result string)) + ) + + (export "element" (type $element (sub resource))) + (export "[method]element.get-attribute" + (func + (param "self" (borrow $element)) + (param "name" string) + (result (option string)) + ) + ) + ) + ) + (alias export $g "element" (type $el)) + + ... + + (export "encode-id" + (func + (param "el" (borrow $el)) + (result (option string)) + ) + ) +) +``` + +```js +const exports = + await WebAssembly.instantiate(bytes, { builtins: ["js/global"] }).exports; + +exports.encodeId(document.body); +``` + +Importing from `wasm:js/global` is equivalent to an imports object with: `{ "wasm:js/global": globalThis }`. The normal rules for reading from the imports object still apply. + +ESM-integration defaults to enabling `js/global` in the compile options (the `wasm:` prefix is implicit) which allows a component to import and use web APIs without any glue code: + +```html + +``` + +### Exporting a resource + +A resource type exported from a component becomes a JS class: + +```wat +(component + ... + + (export "counter" (type $counter (sub resource))) + (export "[constructor]counter" + (func (result (own $counter))) + ) + (export "[method]counter.increment-once" + (func + (param "self" (borrow $counter)) + (result u32) + ) + ) +) +``` + +```js +const { instance } = await WebAssembly.instantiate(bytes); +const { Counter } = instance.exports; + +let c = new Counter(); +c.incrementOnce(); // 1 +c.incrementOnce(); // 2 +``` + +As in the importing a resource case above, component names are converted to JS names. This allows an export of `counter` to become a JS class named `Counter`, and `increment-once` to become `incrementOnce`. + +## Status + +The following have no binding yet: +- Component model async features such as `async` functions, `future`, `stream`. +- `error-context`. + +Everything else we know to be open is collected in the reference's [follow ups](./JS-Reference.md#follow-ups). diff --git a/design/mvp/JS-Reference.md b/design/mvp/JS-Reference.md new file mode 100644 index 000000000..d78cef10b --- /dev/null +++ b/design/mvp/JS-Reference.md @@ -0,0 +1,1107 @@ +# WebAssembly Components JS-API Reference + +This is the in-depth reference for the WebAssembly Component JS-API. See the [component model explainer](./Explainer.md) for an in-depth explanation of WebAssembly Components (hereafter 'components'). See the [JS-API explainer](./JS-Explainer.md) for a higher-level introduction to the JS-API. + +**This is a draft and is not complete. Major details are unresolved, and there are bugs.** + +## Goals + +1. Components can import and use most web and JS APIs +2. Components can export an API usable by JS +3. Components interact with the web platform in similar ways to JS: + 1. Components can feature test whether APIs are present + 1. Components work whether they are importing a web API, or a JS polyfill, or a component polyfill + 1. Components are tolerant of web API evolution + 1. Component misuse of a web API results in failure at that call-site, not a link time error +4. Components have better web API performance than WebAssembly modules today + +## Non-goals + +1. Components importing every kind of web API +1. Components exporting any kind of JS API + +The gaps ideally will narrow over time after a v1.0 release, but may not ever fully close. + +## The WebAssembly namespace + +We extend the imperative WebAssembly JS-API interfaces to also allow validation, compilation, and instantiation of components in addition to modules. + +```webidl +[LegacyNamespace=WebAssembly, Exposed=*] +interface Component { + constructor([AllowResizable] AllowSharedBufferSource bytes, optional WebAssemblyCompileOptions options = {}); +}; + +[LegacyNamespace=WebAssembly, Exposed=*] +interface ComponentInstance { + constructor(Component component, optional object importsObject); + readonly attribute object exports; +}; + +typedef (Component or Module) InstantiateSource; + +dictionary WebAssemblyInstantiatedComponentSource { + required Component component; + required ComponentInstance instance; +}; + +[Exposed=*] +namespace WebAssembly { + // Same as before, but now will detect if the bytes are a component or module and dispatch differently. + boolean validate([AllowResizable] AllowSharedBufferSource bytes, optional WebAssemblyCompileOptions options = {}); + Promise compile([AllowResizable] AllowSharedBufferSource bytes, optional WebAssemblyCompileOptions options = {}); + Promise<(WebAssemblyInstantiatedSource or WebAssemblyInstantiatedComponentSource)> instantiate( + [AllowResizable] AllowSharedBufferSource bytes, optional object importObject, optional WebAssemblyCompileOptions options = {}); + + // Now takes an InstantiateSource instead of just a Module, and returns a + // ComponentInstance for a Component. + Promise<(Instance or ComponentInstance)> instantiate( + InstantiateSource moduleOrComponentObject, optional object importObject); +}; +``` + +We also add an error type for component functions that return `result<_, E>` to JS: + +```webidl +[LegacyNamespace=WebAssembly, Exposed=*] +interface ComponentError : Error { + constructor(optional DOMString message = "", optional any data); + readonly attribute any data; +}; +``` + +`data` is the converted `E` value. See [Create the exports object](#create-the-exports-object). + +## Validation/compilation + +Validation and compilation of components defer to the underlying component embedding interface. This reference adds nothing to it. + +## Component store + +A component store is defined by the [canonical ABI](./CanonicalABI.md#component-instances) and is analogous to the core wasm store. It contains a set of component instances and tasks. A store is also a unit of failure where a trap can trigger a lockdown which prevents further execution within the store. + +In contrast with core wasm, every component instantiated by the JS-API or ESM (i.e. 'top level') is partitioned to its own store. This means that all top-level component interaction is mediated by the JS-API. A component export imported by another component is treated the same as any other JS import. + +This ensures that whether an ESM is implemented as a component or with JS is an implementation detail that can change over time. This also means that a failure within a top-level component instance that triggers a lockdown affects only that component instance. + +Nested components share a common store and directly link following the normal component rules. This should be sufficient for most cases where you want to directly link components. In the future, we could consider an extension to instantiate two components within the same store. + +## Entry points + +A `Component` has the following slots: + 1. [[Component]] - the compiled component. + 1. [[EnabledBuiltins]] - the enabled builtins. + +A `ComponentInstance` has the following slots: + 1. [[Store]] - the component store. + 1. [[ComponentInstance]] - the component instance. + 1. [[Exports]] - the [exports object](#create-the-exports-object). + 1. [[HostResourceTypes]] - map from [abstract type key](#abstract-and-transparent-types) to [host resource type](#host-resource-types-and-values). + 1. [[GuestResourceClasses]] - map from [abstract type key](#abstract-and-transparent-types) to [guest resource class](#guest-resource-classes). + +To `trap` given a component instance |instance|: +1. Invoke |instance|.[[Store]].lock_down(). +1. Throw a `WebAssembly.RuntimeError`. + +A trapped instance is locked down: every later call into it traps again before running any code. + +A call has *entered the guest* once the canonical ABI has, on the call's behalf, invoked any function of the component instance (such as `realloc`) or modified one of its handle tables. A conversion that throws after that point leaves guest state the guest never sees, so the instance is locked down as if the call had trapped, while the original exception still propagates. A conversion that throws before that point leaves the instance untouched. + +To `construct a Component` given |bytes| and |options|: +1. Let |stableBytes| be a copy of the bytes held by |bytes|. +1. Let |enabledBuiltins| be the result of parsing [`builtins`](#builtin-imports) from |options|. +1. Let |component| be the result of compiling |stableBytes| as a component, per the embedding interface. +1. If compilation fails: + 1. Throw a `WebAssembly.CompileError`. +1. Set **this**.[[Component]] to |component|. +1. Set **this**.[[EnabledBuiltins]] to |enabledBuiltins|. + +To `construct a ComponentInstance` given a `Component` |component|, and |importsObject|: +1. Let |enabledBuiltins| be |component|.[[EnabledBuiltins]]. +1. Let |result| be ? [`instantiate a component from an imports object`](#instantiation) given |component|.[[Component]], |importsObject|, |enabledBuiltins|. +1. Set **this**.[[Store]] to |result|.[[Store]]. +1. Set **this**.[[ComponentInstance]] to |result|.[[ComponentInstance]]. +1. Set **this**.[[Exports]] to |result|.[[Exports]]. +1. Set **this**.[[HostResourceTypes]] to |result|.[[HostResourceTypes]]. +1. Set **this**.[[GuestResourceClasses]] to |result|.[[GuestResourceClasses]]. + +The `exports` getter returns **this**.[[Exports]]. + +`validate` is modified to validate the bytes as a component if the `layer` field of the binary indicates a component. +`compile`/`instantiate` are modified to asynchronously compile/instantiate the bytes as a component if the `layer` field indicates a component. + +## Names + +Component import/export `plainname`s contain [`label`s](Explainer.md#import-and-export-definitions) that must be transformed into an identifier for use with JS. + +Fully qualified component import/export `interfacename`s (such as `wasi:http/handler@1.0.0`) are used as-is when converted to JS strings or as [module specifiers](#webassembly-esm-integration). + +We define `PascalCase(label)` and `CamelCase(label)` below. + +| `label` | `PascalCase` | `CamelCase` | +|---|---|---| +| `element` | `Element` | `element` | +| `query-selector` | `QuerySelector` | `querySelector` | +| `inner-HTML` | `InnerHTML` | `innerHTML` | +| `XML-http-request` | `XMLHttpRequest` | `xmlHttpRequest` | +| `URL` | `URL` | `url` | +| `a1-2-3` | `A123` | `a123` | + +Every `plainname` matches exactly one of the four patterns below, and no `interfacename` matches any of them. The algorithms in this document dispatch on these patterns and read their named captures. `