Skip to content

Commit

Permalink
Implement [LegacyLenientSetter] and fix [LegacyLenientThis]
Browse files Browse the repository at this point in the history
For [LegacyLenientThis], this fixes an issue where it would still try to get the impl value, even when the thisArg did not pass the is() check, resulting in a TypeError.

This also adds test coverage for [LegacyLenientThis] and [Replaceable], as part of #207.
  • Loading branch information
ExE-Boss authored and domenic committed Apr 26, 2020
1 parent a272dce commit 1ec22bf
Show file tree
Hide file tree
Showing 5 changed files with 792 additions and 71 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ webidl2js is implementing an ever-growing subset of the Web IDL specification. S
- `[EnforceRange]`
- `[Exposed]`
- `[LegacyLenientThis]`
- `[LegacyLenientSetter]`
- `[LegacyNoInterfaceObject]`
- `[LegacyNullToEmptyString]`
- `[LegacyOverrideBuiltins]`
Expand All @@ -494,7 +495,6 @@ Notable missing features include:
- `[Default]` (for `toJSON()` operations)
- `[Global]`'s various consequences, including the named properties object and `[[SetPrototypeOf]]`
- `[LegacyFactoryFunction]`
- `[LegacyLenientSetter]`
- `[LegacyNamespace]`
- `[LegacyTreatNonObjectAsNull]`
- `[SecureContext]`
Expand Down
70 changes: 51 additions & 19 deletions lib/constructs/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,16 @@ class Attribute {
setterBody = processedOutput.set;
}

if (utils.getExtAttr(this.idl.extAttrs, "LegacyLenientThis")) {
brandCheck = "";
const replaceable = utils.getExtAttr(this.idl.extAttrs, "Replaceable");
const legacyLenientSetter = utils.getExtAttr(this.idl.extAttrs, "LegacyLenientSetter");
const legacyLenientThis = utils.getExtAttr(this.idl.extAttrs, "LegacyLenientThis");

if (legacyLenientThis) {
brandCheck = `
if (!exports.is(esValue)) {
return;
}
`;
}

if (sameObject) {
Expand Down Expand Up @@ -94,23 +102,47 @@ class Attribute {
${idlConversion}
${setterBody}
`, "set", { configurable });
} else if (utils.getExtAttr(this.idl.extAttrs, "PutForwards")) {
addMethod(this.idl.name, ["V"], `
const esValue = this !== null && this !== undefined ? this : globalObject;
${brandCheck}
this.${this.idl.name}.${utils.getExtAttr(this.idl.extAttrs, "PutForwards").rhs.value} = V;
`, "set", { configurable });
} else if (utils.getExtAttr(this.idl.extAttrs, "Replaceable")) {
addMethod(this.idl.name, ["V"], `
const esValue = this !== null && this !== undefined ? this : globalObject;
${brandCheck}
Object.defineProperty(esValue, "${this.idl.name}", {
configurable: true,
enumerable: true,
value: V,
writable: true
});
`, "set", { configurable });
} else {
const putForwards = utils.getExtAttr(this.idl.extAttrs, "PutForwards");

setterBody = "";
if (replaceable) {
if (legacyLenientThis) {
brandCheck = "";
}

setterBody = `
Object.defineProperty(esValue, "${this.idl.name}", {
configurable: true,
enumerable: true,
value: V,
writable: true
});
`;
} else if (putForwards) {
setterBody = `
const Q = esValue["${this.idl.name}"];
if (!utils.isObject(Q)) {
throw new TypeError("Property '${this.idl.name}' is not an object");
}
`;

// WebIDL calls the `Set` abstract operation with a `Throw` value of `false`:
setterBody += `Reflect.set(Q, "${putForwards.rhs.value}", V);`;
}

if (setterBody) {
addMethod(this.idl.name, ["V"], `
const esValue = this !== null && this !== undefined ? this : globalObject;
${brandCheck}
${setterBody}
`, "set", { configurable });
} else if (legacyLenientSetter) {
addMethod(this.idl.name, ["V"], legacyLenientThis ? "" : `
const esValue = this !== null && this !== undefined ? this : globalObject;
${brandCheck}
`, "set", { configurable });
}
}

if (!this.static && this.idl.special === "stringifier") {
Expand Down
Loading

0 comments on commit 1ec22bf

Please sign in to comment.