Skip to content

Constructor & prototype reform #140

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

Merged
merged 8 commits into from
Nov 22, 2019
Merged
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
57 changes: 17 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ will generate a JavaScript wrapper class file roughly like this:
```js
const conversions = require("webidl-conversions");
const impl = require("./utils.js").implSymbol;
const ctorRegistry = require("./utils.js").ctorRegistrySymbol;

const Impl = require("./SomeInterface-impl.js").implementation;

Expand Down Expand Up @@ -64,10 +65,9 @@ Object.defineProperties(SomeInterface.prototype, {
[Symbol.toStringTag]: { value: "SomeInterface", configurable: true }
});

exports.interface = SomeInterface;

exports.create = (constructorArgs = [], privateData = {}) => {
const obj = Object.create(SomeInterface.prototype);
exports.create = (globalObject, constructorArgs = [], privateData = {}) => {
const ctor = globalObject[ctorRegistry].SomeInterface;
const obj = Object.create(ctor.prototype);
obj[impl] = new Impl(constructorArgs, privateData);
return obj;
};
Expand Down Expand Up @@ -151,46 +151,28 @@ Performs the Web IDL conversion algorithm for this interface, converting _value_

In practice, this means doing a type-check equivalent to `is(value)`, and if it passes, returns the corresponding impl. If the type-check fails, it throws an informative exception. _context_ can be used to describe the provided value in any resulting error message.

#### `create(constructorArgs, privateData)`
#### `install(globalObject)`

This method creates a brand new wrapper constructor and prototype and attach it to the passed `globalObject`. It also registers the created constructor with the `globalObject`'s global constructor registry, which makes `create()`, `createImpl()`, and `setup()` work. (Thus, it is important to invoke `install()` before invoking those methods, as otherwise they will throw.)

Creates a new instance of the wrapper class and corresponding implementation class, passing in the `constructorArgs` array and `privateData` object to the implementation class constructor. Then returns the wrapper class.
#### `create(globalObject, constructorArgs, privateData)`

Creates a new instance of the wrapper class and corresponding implementation class, passing in the `globalObject`, the `constructorArgs` array and `privateData` object to the implementation class constructor. Then returns the wrapper class.

This is useful in other parts of your program that are not implementation class files, but instead want to interface with them from the outside. It's also mostly useful when creating instances of classes that do not have a `[Constructor]`, i.e. are not constructible via their wrapper class constructor.

#### `createImpl(constructorArgs, privateData)`
#### `createImpl(globalObject, constructorArgs, privateData)`

Creates a new instance of the wrapper class and corresponding implementation class, passing in the `constructorArgs` array and `privateData` object to the implementation class constructor. Then returns the implementation class.
Creates a new instance of the wrapper class and corresponding implementation class, passing in the `globalObject`, the `constructorArgs` array and `privateData` object to the implementation class constructor. Then returns the implementation class.

This is useful inside implementation class files, where it is easiest to only deal with impls, not wrappers.

#### `setup(obj, constructorArgs, privateData)`
#### `setup(obj, globalObject, constructorArgs, privateData)`

This function is mostly used internally, and almost never should be called by your code. The one exception is if you need to inherit from a wrapper class corresponding to an interface without a `[Constructor]`, from a non-webidl2js-generated class. Then, you can call `SuperClass.setup(this, [], privateData)` as a substitute for doing `super()` (which would throw).
This function is mostly used internally, and almost never should be called by your code. The one exception is if you need to inherit from a wrapper class corresponding to an interface without a `[Constructor]`, from a non-webidl2js-generated class. Then, you can call `SuperClass.setup(this, globalObject, [], privateData)` as a substitute for doing `super()` (which would throw).

jsdom does this for `Window`, which is written in custom, non-webidl2js-generated code, but inherits from `EventTarget`, which is generated by webidl2js.

#### `interface`

This export is the wrapper class interface, suitable for example for putting on a global scope or exporting to module consumers who don't know anything about webidl2js.

#### `expose`

This export contains information about where an interface is supposed to be exposed as a property. It takes into account the Web IDL extended attribute `[Exposed]` to generate a data structure of the form:

```js
{
nameOfGlobal1: {
nameOfInterface: InterfaceClass
},
nameOfGlobal2: {
nameOfInterface: InterfaceClass
},
// etc.
}
```

This format may seem a bit verbose, but eventually when we support `[NamedConstructor]`, there will be potentially more than one key/value pair per global, and it will show its worth.

### For dictionaries

#### `convert(value, { context })`
Expand All @@ -207,9 +189,10 @@ Implementation class files contain a single export, `implementation`, whose valu

### The constructor

A constructor for your implementation class, with signature `(constructorArgs, privateData)` can serve several purposes:
A constructor for your implementation class, with signature `(globalObject, constructorArgs, privateData)` can serve several purposes:

- Setting up initial state that will always be used, such as caches or default values
- Keep a reference to the relevant `globalObject` for later consumption.
- Processing constructor arguments `constructorArgs` passed to the wrapper class constructor, if the interface in question has a `[Constructor]` extended attribute.
- Processing any private data `privateData` which is provided when other parts of your program use the generated `create()` or `createImpl()` exports of the wrapper class file. This is useful for constructing instances with specific state that cannot be constructed via the wrapper class constructor.

Expand Down Expand Up @@ -247,7 +230,7 @@ Note that for IDL attributes that are `readonly`, these properties do not need t

#### Static attributes

Just like static operations, static attributes are defined as properties on the constructor of the implementation class. And just like other attributes, the attribute can either be implemented as an accessor attribute or (if it is readonly) a data attribute. Note that, unless the `[WebIDL2JSFactory]` extended attribute is specified on the interface, any mutations to writable static attributes of the class will reflect on other places that use the same interface.
Just like static operations, static attributes are defined as properties on the constructor of the implementation class. And just like other attributes, the attribute can either be implemented as an accessor attribute or (if it is readonly) a data attribute.

### toString method implementing IDL stringifier

Expand Down Expand Up @@ -386,12 +369,6 @@ Note that only the basics of the reflect algorithm are implemented so far: `bool

In the future we may move this extended attribute out into some sort of plugin, since it is more related to HTML than to Web IDL.

### `[WebIDL2JSFactory]`

This extended attribute can be applied to interfaces to cause them to generate a factory that generates wrapper classes, instead of generating a single wrapper class.

It is currently used by [jsdom](https://github.com/tmpvar/jsdom) for classes which need to specialize their behavior per `Window` object; by default [jsdom shares classes across all `Window`s](https://github.com/tmpvar/jsdom#shared-constructors-and-prototypes), but with `[WebIDL2JSFactory]`, an exception can be made for certain classes that need it.

### `[WebIDL2JSValueAsUnsupported=value]`

This extended attribute can be applied to named or indexed getters or setters. It says that whether the interface supports a given property name/index can be automatically derived by looking at the return value of its indexed getter/setter: whenever `value` is returned, the name/index is unsupported. Typically, `value` is either `undefined` or `_null`.
Expand Down
Loading