From 3ce326c32df64d19f5295a72b6a1c315aed660e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sat, 11 Jan 2020 17:36:51 -0500 Subject: [PATCH] fix: duplicate __proto__ keys should be allowed in patterns --- .../babel-parser/src/parser/expression.js | 30 ++- packages/babel-parser/src/parser/util.js | 8 +- packages/babel-parser/src/plugins/estree.js | 11 +- .../input.js | 0 .../output.json | 0 .../destructuring/duplicate-proto-2/input.js | 1 + .../duplicate-proto-2/output.json | 241 ++++++++++++++++++ .../destructuring/duplicate-proto-3/input.js | 1 + .../duplicate-proto-3/output.json | 208 +++++++++++++++ 9 files changed, 483 insertions(+), 17 deletions(-) rename packages/babel-parser/test/fixtures/es2015/destructuring/{duplicate-proto => duplicate-proto-1}/input.js (100%) rename packages/babel-parser/test/fixtures/es2015/destructuring/{duplicate-proto => duplicate-proto-1}/output.json (100%) create mode 100644 packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-2/input.js create mode 100644 packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-2/output.json create mode 100644 packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-3/input.js create mode 100644 packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-3/output.json diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index c841935a51f8..86c4f0808d78 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -70,7 +70,8 @@ export default class ExpressionParser extends LValParser { checkDuplicatedProto( prop: N.ObjectMember | N.SpreadElement, - protoRef: { used: boolean, start?: number }, + protoRef: { used: boolean }, + refExpressionErrors: ?ExpressionErrors, ): void { if ( prop.type === "SpreadElement" || @@ -88,8 +89,12 @@ export default class ExpressionParser extends LValParser { if (name === "__proto__") { // Store the first redefinition's position - if (protoRef.used && !protoRef.start) { - protoRef.start = key.start; + if (protoRef.used) { + if (refExpressionErrors && refExpressionErrors.doubleProto === -1) { + refExpressionErrors.doubleProto = key.start; + } else { + this.raise(key.start, "Redefinition of __proto__ property"); + } } protoRef.used = true; @@ -203,9 +208,12 @@ export default class ExpressionParser extends LValParser { if (operator === "||=" || operator === "&&=") { this.expectPlugin("logicalAssignment"); } - node.left = this.match(tt.eq) - ? this.toAssignable(left, undefined, "assignment expression") - : left; + if (this.match(tt.eq)) { + node.left = this.toAssignable(left, undefined, "assignment expression"); + refExpressionErrors.doubleProto = -1; // reset because double __proto__ is valid in assignment expression + } else { + node.left = left; + } if (refExpressionErrors.shorthandAssign >= node.left.start) { refExpressionErrors.shorthandAssign = -1; // reset because shorthand default was used correctly @@ -1513,8 +1521,10 @@ export default class ExpressionParser extends LValParser { } const prop = this.parseObjectMember(isPattern, refExpressionErrors); - // $FlowIgnore RestElement will never be returned if !isPattern - if (!isPattern) this.checkDuplicatedProto(prop, propHash); + if (!isPattern) { + // $FlowIgnore RestElement will never be returned if !isPattern + this.checkDuplicatedProto(prop, propHash, refExpressionErrors); + } // $FlowIgnore if (prop.shorthand) { @@ -1524,10 +1534,6 @@ export default class ExpressionParser extends LValParser { node.properties.push(prop); } - if (!this.match(tt.eq) && propHash.start !== undefined) { - this.raise(propHash.start, "Redefinition of __proto__ property"); - } - return this.finishNode( node, isPattern ? "ObjectPattern" : "ObjectExpression", diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index 68e742365dc3..ee420b7ab96b 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -274,11 +274,14 @@ export default class UtilParser extends Tokenizer { andThrow: boolean, ) { if (!refExpressionErrors) return false; - const { shorthandAssign } = refExpressionErrors; - if (!andThrow) return shorthandAssign >= 0; + const { shorthandAssign, doubleProto } = refExpressionErrors; + if (!andThrow) return shorthandAssign >= 0 || doubleProto >= 0; if (shorthandAssign >= 0) { this.unexpected(shorthandAssign); } + if (doubleProto >= 0) { + this.raise(doubleProto, "Redefinition of __proto__ property"); + } } } @@ -290,4 +293,5 @@ export default class UtilParser extends Tokenizer { */ export class ExpressionErrors { shorthandAssign = -1; + doubleProto = -1; } diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index 78fddf6829d9..9cd825945015 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -149,7 +149,8 @@ export default (superClass: Class): Class => checkDuplicatedProto( prop: N.ObjectMember | N.SpreadElement, - protoRef: { used: boolean, start?: number }, + protoRef: { used: boolean }, + refExpressionErrors: ?ExpressionErrors, ): void { if ( prop.type === "SpreadElement" || @@ -167,8 +168,12 @@ export default (superClass: Class): Class => if (name === "__proto__" && prop.kind === "init") { // Store the first redefinition's position - if (protoRef.used && !protoRef.start) { - protoRef.start = key.start; + if (protoRef.used) { + if (refExpressionErrors && refExpressionErrors.doubleProto === -1) { + refExpressionErrors.doubleProto = key.start; + } else { + this.raise(key.start, "Redefinition of __proto__ property"); + } } protoRef.used = true; diff --git a/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto/input.js b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-1/input.js similarity index 100% rename from packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto/input.js rename to packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-1/input.js diff --git a/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto/output.json b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-1/output.json similarity index 100% rename from packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto/output.json rename to packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-1/output.json diff --git a/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-2/input.js b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-2/input.js new file mode 100644 index 000000000000..73d46e315061 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-2/input.js @@ -0,0 +1 @@ +([{ __proto__: x, __proto__: y }] = [{}]); diff --git a/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-2/output.json b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-2/output.json new file mode 100644 index 000000000000..3646ce22df38 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-2/output.json @@ -0,0 +1,241 @@ +{ + "type": "File", + "start": 0, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "expression": { + "type": "AssignmentExpression", + "start": 1, + "end": 40, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 40 + } + }, + "operator": "=", + "left": { + "type": "ArrayPattern", + "start": 1, + "end": 33, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 33 + } + }, + "elements": [ + { + "type": "ObjectPattern", + "start": 2, + "end": 32, + "loc": { + "start": { + "line": 1, + "column": 2 + }, + "end": { + "line": 1, + "column": 32 + } + }, + "properties": [ + { + "type": "ObjectProperty", + "start": 4, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "method": false, + "key": { + "type": "Identifier", + "start": 4, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 13 + }, + "identifierName": "__proto__" + }, + "name": "__proto__" + }, + "computed": false, + "shorthand": false, + "value": { + "type": "Identifier", + "start": 15, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 16 + }, + "identifierName": "x" + }, + "name": "x" + } + }, + { + "type": "ObjectProperty", + "start": 18, + "end": 30, + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 30 + } + }, + "method": false, + "key": { + "type": "Identifier", + "start": 18, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 27 + }, + "identifierName": "__proto__" + }, + "name": "__proto__" + }, + "computed": false, + "shorthand": false, + "value": { + "type": "Identifier", + "start": 29, + "end": 30, + "loc": { + "start": { + "line": 1, + "column": 29 + }, + "end": { + "line": 1, + "column": 30 + }, + "identifierName": "y" + }, + "name": "y" + } + } + ] + } + ] + }, + "right": { + "type": "ArrayExpression", + "start": 36, + "end": 40, + "loc": { + "start": { + "line": 1, + "column": 36 + }, + "end": { + "line": 1, + "column": 40 + } + }, + "elements": [ + { + "type": "ObjectExpression", + "start": 37, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "properties": [] + } + ] + }, + "extra": { + "parenthesized": true, + "parenStart": 0 + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-3/input.js b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-3/input.js new file mode 100644 index 000000000000..478c33774b46 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-3/input.js @@ -0,0 +1 @@ +({ __proto__: x, __proto__: y }) => {}; diff --git a/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-3/output.json b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-3/output.json new file mode 100644 index 000000000000..27df8bb4401e --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto-3/output.json @@ -0,0 +1,208 @@ +{ + "type": "File", + "start": 0, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "expression": { + "type": "ArrowFunctionExpression", + "start": 0, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "ObjectPattern", + "start": 1, + "end": 31, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 31 + } + }, + "properties": [ + { + "type": "ObjectProperty", + "start": 3, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 3 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "method": false, + "key": { + "type": "Identifier", + "start": 3, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 3 + }, + "end": { + "line": 1, + "column": 12 + }, + "identifierName": "__proto__" + }, + "name": "__proto__" + }, + "computed": false, + "shorthand": false, + "value": { + "type": "Identifier", + "start": 14, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "x" + }, + "name": "x" + } + }, + { + "type": "ObjectProperty", + "start": 17, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 29 + } + }, + "method": false, + "key": { + "type": "Identifier", + "start": 17, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 26 + }, + "identifierName": "__proto__" + }, + "name": "__proto__" + }, + "computed": false, + "shorthand": false, + "value": { + "type": "Identifier", + "start": 28, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 28 + }, + "end": { + "line": 1, + "column": 29 + }, + "identifierName": "y" + }, + "name": "y" + } + } + ] + } + ], + "body": { + "type": "BlockStatement", + "start": 36, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 36 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file