Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicate __proto__ key should be allowed in object patterns #10987

Merged
merged 4 commits into from Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
145 changes: 76 additions & 69 deletions packages/babel-parser/src/parser/expression.js

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions packages/babel-parser/src/parser/lval.js
Expand Up @@ -21,6 +21,7 @@ import {
} from "../util/identifier";
import { NodeUtils } from "./node";
import { type BindingTypes, BIND_NONE } from "../util/scopeflags";
import { ExpressionErrors } from "./util";

const unwrapParenthesizedExpression = (node: Node) => {
return node.type === "ParenthesizedExpression"
Expand All @@ -33,13 +34,13 @@ export default class LValParser extends NodeUtils {
+parseIdentifier: (liberal?: boolean) => Identifier;
+parseMaybeAssign: (
noIn?: ?boolean,
refShorthandDefaultPos?: ?Pos,
refExpressionErrors?: ?ExpressionErrors,
afterLeftParse?: Function,
refNeedsArrowPos?: ?Pos,
) => Expression;
+parseObj: <T: ObjectPattern | ObjectExpression>(
isPattern: boolean,
refShorthandDefaultPos?: ?Pos,
refExpressionErrors?: ?ExpressionErrors,
) => T;
// Forward-declaration: defined in statement.js
+parseDecorator: () => Decorator;
Expand Down Expand Up @@ -241,14 +242,14 @@ export default class LValParser extends NodeUtils {
// Parses spread element.

parseSpread(
refShorthandDefaultPos: ?Pos,
refExpressionErrors: ?ExpressionErrors,
refNeedsArrowPos?: ?Pos,
): SpreadElement {
const node = this.startNode();
this.next();
node.argument = this.parseMaybeAssign(
false,
refShorthandDefaultPos,
refExpressionErrors,
undefined,
refNeedsArrowPos,
);
Expand Down
9 changes: 5 additions & 4 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -27,6 +27,7 @@ import {
CLASS_ELEMENT_STATIC_SETTER,
type BindingTypes,
} from "../util/scopeflags";
import { ExpressionErrors } from "./util";

const loopLabel = { kind: "loop" },
switchLabel = { kind: "switch" };
Expand Down Expand Up @@ -533,17 +534,17 @@ export default class StatementParser extends ExpressionParser {
return this.parseFor(node, init);
}

const refShorthandDefaultPos = { start: 0 };
const init = this.parseExpression(true, refShorthandDefaultPos);
const refExpressionErrors = new ExpressionErrors();
const init = this.parseExpression(true, refExpressionErrors);
if (this.match(tt._in) || this.isContextual("of")) {
const description = this.isContextual("of")
? "for-of statement"
: "for-in statement";
this.toAssignable(init, undefined, description);
this.checkLVal(init, undefined, undefined, description);
return this.parseForIn(node, init, awaitAt);
} else if (refShorthandDefaultPos.start) {
this.unexpected(refShorthandDefaultPos.start);
} else {
this.checkExpressionErrors(refExpressionErrors, true);
}
if (awaitAt > -1) {
this.unexpected(awaitAt);
Expand Down
31 changes: 31 additions & 0 deletions packages/babel-parser/src/parser/util.js
Expand Up @@ -268,4 +268,35 @@ export default class UtilParser extends Tokenizer {
throw error;
}
}

checkExpressionErrors(
refExpressionErrors: ?ExpressionErrors,
andThrow: boolean,
) {
if (!refExpressionErrors) return false;
const { shorthandAssign, doubleProto } = refExpressionErrors;
if (!andThrow) return shorthandAssign >= 0 || doubleProto >= 0;
if (shorthandAssign >= 0) {
this.unexpected(shorthandAssign);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(In a separate PR)
Probably this error would be easily recoverable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I try to preserve the error output of shorthand assign error so it's indeed a refactor.

}
if (doubleProto >= 0) {
this.raise(doubleProto, "Redefinition of __proto__ property");
}
}
}

/**
* The ExpressionErrors is a context struct used to track
* - **shorthandAssign**: track initializer `=` position when parsing ambiguous
* patterns. When we are sure the parsed pattern is a RHS, which means it is
* not a pattern, we will throw on this position on invalid assign syntax,
* otherwise it will be reset to -1
* - **doubleProto**: track the duplicate `__proto__` key position when parsing
* ambiguous object patterns. When we are sure the parsed pattern is a RHS,
* which means it is an object literal, we will throw on this position for
* __proto__ redefinition, otherwise it will be reset to -1
*/
export class ExpressionErrors {
shorthandAssign = -1;
doubleProto = -1;
}
22 changes: 14 additions & 8 deletions packages/babel-parser/src/plugins/estree.js
Expand Up @@ -4,8 +4,9 @@

import { types as tt, TokenType } from "../tokenizer/types";
import type Parser from "../parser";
import type { ExpressionErrors } from "../parser/util";
import * as N from "../types";
import type { Pos, Position } from "../util/location";
import type { Position } from "../util/location";
import { type BindingTypes, BIND_NONE } from "../util/scopeflags";

function isSimpleProperty(node: N.Node): boolean {
Expand Down Expand Up @@ -148,7 +149,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>

checkDuplicatedProto(
prop: N.ObjectMember | N.SpreadElement,
protoRef: { used: boolean, start?: number },
protoRef: { used: boolean },
refExpressionErrors: ?ExpressionErrors,
): void {
if (
prop.type === "SpreadElement" ||
Expand All @@ -166,8 +168,12 @@ export default (superClass: Class<Parser>): Class<Parser> =>

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;
Expand Down Expand Up @@ -234,7 +240,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
classBody.body.push(method);
}

parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression {
parseExprAtom(refExpressionErrors?: ?ExpressionErrors): N.Expression {
switch (this.state.type) {
case tt.num:
case tt.string:
Expand All @@ -256,7 +262,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.estreeParseLiteral(false);

default:
return super.parseExprAtom(refShorthandDefaultPos);
return super.parseExprAtom(refExpressionErrors);
}
}

Expand Down Expand Up @@ -340,14 +346,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
startPos: ?number,
startLoc: ?Position,
isPattern: boolean,
refShorthandDefaultPos: ?Pos,
refExpressionErrors: ?ExpressionErrors,
): ?N.ObjectProperty {
const node: N.EstreeProperty = (super.parseObjectProperty(
prop,
startPos,
startLoc,
isPattern,
refShorthandDefaultPos,
refExpressionErrors,
): any);

if (node) {
Expand Down
13 changes: 7 additions & 6 deletions packages/babel-parser/src/plugins/flow.js
Expand Up @@ -21,6 +21,7 @@ import {
SCOPE_ARROW,
SCOPE_OTHER,
} from "../util/scopeflags";
import type { ExpressionErrors } from "../parser/util";

const reservedTypes = new Set([
"_",
Expand Down Expand Up @@ -2283,7 +2284,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
isGenerator: boolean,
isAsync: boolean,
isPattern: boolean,
refShorthandDefaultPos: ?Pos,
refExpressionErrors: ?ExpressionErrors,
containsEsc: boolean,
): void {
if ((prop: $FlowFixMe).variance) {
Expand All @@ -2306,7 +2307,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
isGenerator,
isAsync,
isPattern,
refShorthandDefaultPos,
refExpressionErrors,
containsEsc,
);

Expand Down Expand Up @@ -2559,7 +2560,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// 3. This is neither. Just call the super method
parseMaybeAssign(
noIn?: ?boolean,
refShorthandDefaultPos?: ?Pos,
refExpressionErrors?: ?ExpressionErrors,
afterLeftParse?: Function,
refNeedsArrowPos?: ?Pos,
): N.Expression {
Expand All @@ -2577,7 +2578,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
() =>
super.parseMaybeAssign(
noIn,
refShorthandDefaultPos,
refExpressionErrors,
afterLeftParse,
refNeedsArrowPos,
),
Expand Down Expand Up @@ -2611,7 +2612,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
() =>
super.parseMaybeAssign(
noIn,
refShorthandDefaultPos,
refExpressionErrors,
afterLeftParse,
refNeedsArrowPos,
),
Expand Down Expand Up @@ -2659,7 +2660,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>

return super.parseMaybeAssign(
noIn,
refShorthandDefaultPos,
refExpressionErrors,
afterLeftParse,
refNeedsArrowPos,
);
Expand Down
7 changes: 4 additions & 3 deletions packages/babel-parser/src/plugins/jsx/index.js
Expand Up @@ -4,11 +4,12 @@ import * as charCodes from "charcodes";

import XHTMLEntities from "./xhtml";
import type Parser from "../../parser";
import type { ExpressionErrors } from "../../parser/util";
import { TokenType, types as tt } from "../../tokenizer/types";
import { TokContext, types as tc } from "../../tokenizer/context";
import * as N from "../../types";
import { isIdentifierChar, isIdentifierStart } from "../../util/identifier";
import type { Pos, Position } from "../../util/location";
import type { Position } from "../../util/location";
import { isNewLine } from "../../util/whitespace";

const HEX_NUMBER = /^[\da-fA-F]+$/;
Expand Down Expand Up @@ -510,7 +511,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// Overrides
// ==================================

parseExprAtom(refShortHandDefaultPos: ?Pos): N.Expression {
parseExprAtom(refExpressionErrors: ?ExpressionErrors): N.Expression {
if (this.match(tt.jsxText)) {
return this.parseLiteral(this.state.value, "JSXText");
} else if (this.match(tt.jsxTagStart)) {
Expand All @@ -524,7 +525,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.finishToken(tt.jsxTagStart);
return this.jsxParseElement();
} else {
return super.parseExprAtom(refShortHandDefaultPos);
return super.parseExprAtom(refExpressionErrors);
}
}

Expand Down
5 changes: 3 additions & 2 deletions packages/babel-parser/src/plugins/typescript/index.js
Expand Up @@ -25,6 +25,7 @@ import {
} from "../../util/scopeflags";
import TypeScriptScopeHandler from "./scope";
import * as charCodes from "charcodes";
import type { ExpressionErrors } from "../../parser/util";

type TsModifier =
| "readonly"
Expand Down Expand Up @@ -2340,11 +2341,11 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}

// Handle type assertions
parseMaybeUnary(refShorthandDefaultPos?: ?Pos): N.Expression {
parseMaybeUnary(refExpressionErrors?: ?ExpressionErrors): N.Expression {
if (!this.hasPlugin("jsx") && this.isRelational("<")) {
return this.tsParseTypeAssertion();
} else {
return super.parseMaybeUnary(refShorthandDefaultPos);
return super.parseMaybeUnary(refExpressionErrors);
}
}

Expand Down
@@ -0,0 +1 @@
([{ __proto__: x, __proto__: y }] = [{}]);