Skip to content

Commit

Permalink
feat(Global): Add support for named getters
Browse files Browse the repository at this point in the history
  • Loading branch information
ExE-Boss committed Feb 13, 2020
1 parent 2aecfb6 commit 63516e6
Show file tree
Hide file tree
Showing 2 changed files with 554 additions and 0 deletions.
172 changes: 172 additions & 0 deletions lib/constructs/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ class Interface {
return !this.isGlobal && (this.supportsIndexedProperties || this.supportsNamedProperties);
}

get needsNamedPropertiesObject() {
return this.isGlobal && this.supportsNamedProperties;
}

includes(source) {
const mixin = this.ctx.interfaceMixins.get(source);
if (!mixin) {
Expand Down Expand Up @@ -1124,6 +1128,170 @@ class Interface {
`;
}

// https://heycam.github.io/webidl/#named-properties-object
generateNamedPropertiesObject() {
const { idl, name, namedGetter } = this;
const overrideBuiltins = Boolean(utils.getExtAttr(idl.extAttrs, "OverrideBuiltins"));

function supportsPropertyName(O, P, namedValue) {
let unsupportedValue = utils.getExtAttr(namedGetter.extAttrs, "WebIDL2JSValueAsUnsupported");
if (unsupportedValue) {
unsupportedValue = unsupportedValue.rhs.value;
}
if (unsupportedValue) {
const func = namedGetter.name ? `.${namedGetter.name}` : "[utils.namedGet]";
const value = namedValue || `${O}[impl]${func}(${P})`;
return `${value} !== ${unsupportedValue}`;
}
return `${O}[impl][utils.supportsPropertyName](${P})`;
}

// "named property visibility algorithm"
// If `supports` is true then skip the supportsPropertyName check.
function namedPropertyVisible(P, O, supports = false) {
const conditions = [];
if (!supports) {
conditions.push(supportsPropertyName(O, P));
}
if (overrideBuiltins) {
conditions.push(`!utils.hasOwn(${O}, ${P})`);
} else {
// TODO: create a named properties object.
conditions.push(`!(${P} in ${O})`);
}
return conditions.join(" && ");
}

this.str += `
const NamedPropertiesObject = new Proxy(
Object.create(
globalObject.${idl.inheritance ? idl.inheritance : "Object"}.prototype,
{ [Symbol.toStringTag]: { value: "${name}Properties", configurable: true } }
),
{
`;

// [[Get]] (necessary because of proxy semantics)
this.str += `
get(target, P, receiver) {
if (typeof P === "symbol") {
return Reflect.get(target, P, receiver);
}
const desc = this.getOwnPropertyDescriptor(target, P);
if (desc === undefined) {
const parent = Object.getPrototypeOf(target);
if (parent === null) {
return undefined;
}
return Reflect.get(parent, P, receiver);
}
if (!desc.get && !desc.set) {
return desc.value;
}
const getter = desc.get;
if (getter === undefined) {
return undefined;
}
return Reflect.apply(getter, receiver, []);
},
`;

// [[HasProperty]] (necessary because of proxy semantics)
this.str += `
has(target, P) {
if (typeof P === "symbol") {
return Reflect.has(target, P);
}
const desc = this.getOwnPropertyDescriptor(target, P);
if (desc !== undefined) {
return true;
}
const parent = Object.getPrototypeOf(target);
if (parent !== null) {
return Reflect.has(parent, P);
}
return false;
},
`;

// [[GetOwnProperty]]
this.str += `
getOwnPropertyDescriptor(target, P) {
const object = globalObject;
if (typeof P === "symbol") {
return Reflect.getOwnPropertyDescriptor(target, P);
}
`;

const func = namedGetter.name ? `.${namedGetter.name}` : "[utils.namedGet]";
const enumerable = !utils.getExtAttr(idl.extAttrs, "LegacyUnenumerableNamedProperties");
let preamble = "";
const conditions = [];
if (utils.getExtAttr(namedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) {
this.str += `
const namedValue = object[impl]${func}(P);
`;
conditions.push(supportsPropertyName("object", "index", "namedValue"));
conditions.push(namedPropertyVisible("P", "object", true));
} else {
preamble = `
const namedValue = object[impl]${func}(P);
`;
conditions.push(namedPropertyVisible("P", "object"));
}
this.str += `
if (${conditions.join(" && ")}) {
${preamble}
return {
writable: true,
enumerable: ${enumerable},
configurable: true,
value: utils.tryWrapperForImpl(namedValue)
};
}
return Reflect.getOwnPropertyDescriptor(target, P);
},
`;

// [[DefineOwnProperty]]
this.str += `
defineProperty() {
return false;
},
`;

// [[Delete]]
this.str += `
deleteProperty() {
return false;
},
`;

// [[SetPrototypeOf]]
this.str += `
setPrototypeOf(O, V) {
// 1. Return ? SetImmutablePrototype(O, V).
return Reflect.getPrototypeOf(O) === V;
},
`;

// [[PreventExtensions]]
this.str += `
preventExtensions() {
return false;
}
`;

this.str += `
}
);
`;

this.str += `
Object.setPrototypeOf(${name}.prototype, NamedPropertiesObject);
`;
}

generateIface() {
this.str += `
exports.create = function create(globalObject, constructorArgs, privateData) {
Expand Down Expand Up @@ -1470,6 +1638,10 @@ class Interface {

this.generateOffInstanceAfterClass();

if (this.needsNamedPropertiesObject) {
this.generateNamedPropertiesObject();
}

this.str += `
if (globalObject[ctorRegistry] === undefined) {
globalObject[ctorRegistry] = Object.create(null);
Expand Down
Loading

0 comments on commit 63516e6

Please sign in to comment.