diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 812fa605a6af..be53854b62dc 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 }, + propHash: { [key: string]: 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"); + propHash.proto = (propHash.proto || 0) + 1; + + // Store the first redefinition's position + if (propHash.proto === 2) { + propHash.protoStart = key.start; } - propHash.proto = true; } } @@ -1509,7 +1510,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) { @@ -1519,6 +1520,10 @@ export default class ExpressionParser extends LValParser { node.properties.push(prop); } + if (!this.match(tt.eq) && propHash.protoStart !== undefined) { + this.raise(propHash.protoStart, "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..b0e5a6acaed1 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 }, + propHash: { [key: string]: 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"); + propHash.proto = (propHash.proto || 0) + 1; + + // Store the first redefinition's position + if (propHash.proto === 2) { + propHash.protoStart = key.start; } - propHash.proto = 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