diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index cd090b1fc890..556ac6daa7be 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -67,14 +67,13 @@ export default class ExpressionParser extends LValParser { +parseFunctionParams: (node: N.Function, allowModifiers?: boolean) => void; +takeDecorators: (node: N.HasDecorators) => void; - // Check if property name clashes with already added. - // Object/class getters and setters are not allowed to clash — - // either with each other or with an init property — and in - // strict mode, init properties are also not allowed to be repeated. + // Check if property __proto__ has been used more than once. + // If the expression is a destructuring assignment, then __proto__ may appear + // multiple times. Otherwise, __proto__ is a duplicated key. - checkPropClash( + checkDuplicatedProto( prop: N.ObjectMember | N.SpreadElement, - propHash: { [key: string]: boolean }, + protoRef: { used: boolean, start?: number }, ): void { if ( prop.type === "SpreadElement" || @@ -91,10 +90,12 @@ export default class ExpressionParser extends LValParser { const name = key.type === "Identifier" ? key.name : String(key.value); if (name === "__proto__") { - if (propHash.proto) { - this.raise(key.start, "Redefinition of __proto__ property"); + // Store the first redefinition's position + if (protoRef.used && !protoRef.start) { + protoRef.start = key.start; } - propHash.proto = true; + + protoRef.used = true; } } @@ -1515,7 +1516,7 @@ export default class ExpressionParser extends LValParser { const prop = this.parseObjectMember(isPattern, refShorthandDefaultPos); // $FlowIgnore RestElement will never be returned if !isPattern - if (!isPattern) this.checkPropClash(prop, propHash); + if (!isPattern) this.checkDuplicatedProto(prop, propHash); // $FlowIgnore if (prop.shorthand) { @@ -1525,6 +1526,10 @@ 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/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index a407b8ac84ca..eae357921453 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -125,9 +125,9 @@ export default (superClass: Class): Class => } } - checkPropClash( + checkDuplicatedProto( prop: N.ObjectMember | N.SpreadElement, - propHash: { [key: string]: boolean }, + protoRef: { used: boolean, start?: number }, ): void { if ( prop.type === "SpreadElement" || @@ -144,10 +144,12 @@ export default (superClass: Class): Class => const name = key.type === "Identifier" ? key.name : String(key.value); if (name === "__proto__" && prop.kind === "init") { - if (propHash.proto) { - this.raise(key.start, "Redefinition of __proto__ property"); + // Store the first redefinition's position + if (protoRef.used && !protoRef.start) { + protoRef.start = key.start; } - propHash.proto = true; + + 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/input.js new file mode 100644 index 000000000000..494000aa3793 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto/input.js @@ -0,0 +1 @@ +({ __proto__: x, __proto__: y } = {}); diff --git a/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto/output.json b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto/output.json new file mode 100644 index 000000000000..f82a656eae43 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/destructuring/duplicate-proto/output.json @@ -0,0 +1,207 @@ +{ + "type": "File", + "start": 0, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "expression": { + "type": "AssignmentExpression", + "start": 1, + "end": 36, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 36 + } + }, + "operator": "=", + "left": { + "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" + } + } + ] + }, + "right": { + "type": "ObjectExpression", + "start": 34, + "end": 36, + "loc": { + "start": { + "line": 1, + "column": 34 + }, + "end": { + "line": 1, + "column": 36 + } + }, + "properties": [] + }, + "extra": { + "parenthesized": true, + "parenStart": 0 + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/scripts/tests/test262/test262_whitelist.txt b/scripts/tests/test262/test262_whitelist.txt index 977cd38ed79a..8ed606443333 100644 --- a/scripts/tests/test262/test262_whitelist.txt +++ b/scripts/tests/test262/test262_whitelist.txt @@ -1,5 +1,3 @@ -language/expressions/assignment/destructuring/obj-prop-__proto__dup.js(default) -language/expressions/assignment/destructuring/obj-prop-__proto__dup.js(strict mode) language/expressions/class/elements/fields-duplicate-privatenames.js(default) language/expressions/class/elements/fields-duplicate-privatenames.js(strict mode) language/expressions/class/elements/syntax/early-errors/grammar-private-environment-on-class-heritage-chained-usage.js(default)