From 4c7ce37bd0cca149dab56c54e416c10280134581 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 26 Mar 2021 09:56:49 +0100 Subject: [PATCH 1/3] Deoptimize ObjectExpression when a __proto__ property is present Closes #4014 --- src/ast/nodes/ObjectExpression.ts | 10 ++++++++++ .../proto-property/_config.js | 3 +++ .../proto-property/_expected.js | 13 +++++++++++++ .../object-expression/proto-property/main.js | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 test/form/samples/object-expression/proto-property/_config.js create mode 100644 test/form/samples/object-expression/proto-property/_expected.js create mode 100644 test/form/samples/object-expression/proto-property/main.js diff --git a/src/ast/nodes/ObjectExpression.ts b/src/ast/nodes/ObjectExpression.ts index 1925c70bc93..b8a410c5e91 100644 --- a/src/ast/nodes/ObjectExpression.ts +++ b/src/ast/nodes/ObjectExpression.ts @@ -54,6 +54,16 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE super.bind(); // ensure the propertyMap is set for the tree-shaking passes this.getPropertyMap(); + if ( + !this.hasUnknownDeoptimizedProperty && + this.properties.some(prop => { + if (prop instanceof SpreadElement || prop.computed) return false; + if (prop.key instanceof Identifier) return prop.key.name == "__proto__"; + return String((prop.key as Literal).value) == "__proto__"; + }) + ) { + this.deoptimizeAllProperties(); + } } // We could also track this per-property but this would quickly become much more complex diff --git a/test/form/samples/object-expression/proto-property/_config.js b/test/form/samples/object-expression/proto-property/_config.js new file mode 100644 index 00000000000..f70608e1109 --- /dev/null +++ b/test/form/samples/object-expression/proto-property/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'Deoptimize when __proto__ is used' +}; diff --git a/test/form/samples/object-expression/proto-property/_expected.js b/test/form/samples/object-expression/proto-property/_expected.js new file mode 100644 index 00000000000..c9a95c57a57 --- /dev/null +++ b/test/form/samples/object-expression/proto-property/_expected.js @@ -0,0 +1,13 @@ +let proto = { + get a() { log(); } +}; + +let plainProto = { + __proto__: proto +}; +if (plainProto.a) log("plainProto"); + +let quotedProto = { + "__proto__": proto +}; +if (quotedProto.a) log("quotedProto"); diff --git a/test/form/samples/object-expression/proto-property/main.js b/test/form/samples/object-expression/proto-property/main.js new file mode 100644 index 00000000000..b67c5124869 --- /dev/null +++ b/test/form/samples/object-expression/proto-property/main.js @@ -0,0 +1,18 @@ +let basic = { + a: false +}; +if (basic.a) log("basic"); + +let proto = { + get a() { log(); } +}; + +let plainProto = { + __proto__: proto +}; +if (plainProto.a) log("plainProto"); + +let quotedProto = { + "__proto__": proto +}; +if (quotedProto.a) log("quotedProto"); From 0707e36859ea0f49664b44812752019af09a3125 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 27 Mar 2021 13:25:35 +0100 Subject: [PATCH 2/3] Integrate __proto__ detection with getPropertyMap --- src/ast/nodes/ObjectExpression.ts | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/ast/nodes/ObjectExpression.ts b/src/ast/nodes/ObjectExpression.ts index b8a410c5e91..756167b2ba7 100644 --- a/src/ast/nodes/ObjectExpression.ts +++ b/src/ast/nodes/ObjectExpression.ts @@ -54,16 +54,6 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE super.bind(); // ensure the propertyMap is set for the tree-shaking passes this.getPropertyMap(); - if ( - !this.hasUnknownDeoptimizedProperty && - this.properties.some(prop => { - if (prop instanceof SpreadElement || prop.computed) return false; - if (prop.key instanceof Identifier) return prop.key.name == "__proto__"; - return String((prop.key as Literal).value) == "__proto__"; - }) - ) { - this.deoptimizeAllProperties(); - } } // We could also track this per-property but this would quickly become much more complex @@ -308,25 +298,27 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE const isWrite = property.kind !== 'get'; const isRead = property.kind !== 'set'; let key; + let unmatchable = false; if (property.computed) { const keyValue = property.key.getLiteralValueAtPath( EMPTY_PATH, SHARED_RECURSION_TRACKER, this ); - if (keyValue === UnknownValue) { + if (keyValue === UnknownValue) unmatchable = true; + key = String(keyValue); + } else if (property.key instanceof Identifier) { + key = property.key.name; + } else { + key = String((property.key as Literal).value); + } + if (key === '__proto__' && !property.computed || unmatchable) { if (isRead) { this.unmatchablePropertiesRead.push(property); } else { this.unmatchablePropertiesWrite.push(property); } continue; - } - key = String(keyValue); - } else if (property.key instanceof Identifier) { - key = property.key.name; - } else { - key = String((property.key as Literal).value); } const propertyMapProperty = propertyMap[key]; if (!propertyMapProperty) { From 87f59d309847a322fbbfd1dcce56996577c0d0fe Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Mon, 29 Mar 2021 13:18:45 +0200 Subject: [PATCH 3/3] Fix formatting --- src/ast/nodes/ObjectExpression.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ast/nodes/ObjectExpression.ts b/src/ast/nodes/ObjectExpression.ts index ffd23ea4b54..d4190a3d4d0 100644 --- a/src/ast/nodes/ObjectExpression.ts +++ b/src/ast/nodes/ObjectExpression.ts @@ -307,7 +307,7 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE } const isWrite = property.kind !== 'get'; const isRead = property.kind !== 'set'; - let key; + let key: string; let unmatchable = false; if (property.computed) { const keyValue = property.key.getLiteralValueAtPath( @@ -322,13 +322,13 @@ export default class ObjectExpression extends NodeBase implements DeoptimizableE } else { key = String((property.key as Literal).value); } - if (key === '__proto__' && !property.computed || unmatchable) { - if (isRead) { - this.unmatchablePropertiesRead.push(property); - } else { - this.unmatchablePropertiesWrite.push(property); - } - continue; + if (unmatchable || (key === '__proto__' && !property.computed)) { + if (isRead) { + this.unmatchablePropertiesRead.push(property); + } else { + this.unmatchablePropertiesWrite.push(property); + } + continue; } const propertyMapProperty = propertyMap[key]; if (!propertyMapProperty) {