Skip to content

Commit

Permalink
Handle exprAllowed before ObjectLike is parsed (#12267)
Browse files Browse the repository at this point in the history
* fix: disallow expression after `}` is consumed in parseObjectLike

* refactor: avoid accessing this.prodParam in context update
  • Loading branch information
JLHwung committed Oct 28, 2020
1 parent f04bbf6 commit a8c66f4
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 11 deletions.
18 changes: 12 additions & 6 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -241,15 +241,15 @@ export default class ExpressionParser extends LValParser {
const startLoc = this.state.startLoc;
if (this.isContextual("yield")) {
if (this.prodParam.hasYield) {
// If we have [Yield] production, `yield` will start a YieldExpression thus
// regex is allowed following. Otherwise `yield` is an identifier and regex
// is disallowed in tt.name.updateContext
this.state.exprAllowed = true;
let left = this.parseYield();
if (afterLeftParse) {
left = afterLeftParse.call(this, left, startPos, startLoc);
}
return left;
} else {
// The tokenizer will assume an expression is allowed after
// `yield`, but this isn't that kind of yield
this.state.exprAllowed = false;
}
}

Expand Down Expand Up @@ -1603,14 +1603,13 @@ export default class ExpressionParser extends LValParser {
node.properties = [];
this.next();

while (!this.eat(close)) {
while (!this.match(close)) {
if (first) {
first = false;
} else {
this.expect(tt.comma);
if (this.match(close)) {
this.addExtra(node, "trailingComma", this.state.lastTokStart);
this.next();
break;
}
}
Expand All @@ -1637,6 +1636,13 @@ export default class ExpressionParser extends LValParser {
node.properties.push(prop);
}

// The tokenizer uses `braceIsBlock` to detect whether `{` starts a block statement.
// If `{` is a block statement, `exprAllowed` will be `true`.
// However the tokenizer can not handle edge cases like `0 ? a : { a : 1 } / 2`, here
// we update `exprAllowed` when an object-like is parsed.
this.state.exprAllowed = false;
this.next();

this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody;
let type = "ObjectExpression";
if (isPattern) {
Expand Down
17 changes: 12 additions & 5 deletions packages/babel-parser/src/tokenizer/context.js
Expand Up @@ -40,6 +40,14 @@ export const types: {
};

// Token-specific context update code
// Note that we should avoid accessing `this.prodParam` in context update,
// because it is executed immediately when last token is consumed, which may be
// before `this.prodParam` is updated. e.g.
// ```
// function *g() { () => yield / 2 }
// ```
// When `=>` is eaten, the context update of `yield` is executed, however,
// `this.prodParam` still has `[Yield]` production because it is not yet updated

tt.parenR.updateContext = tt.braceR.updateContext = function () {
if (this.state.context.length === 1) {
Expand All @@ -59,11 +67,10 @@ tt.name.updateContext = function (prevType) {
let allowed = false;
if (prevType !== tt.dot) {
if (
(this.state.value === "of" &&
!this.state.exprAllowed &&
prevType !== tt._function &&
prevType !== tt._class) ||
(this.state.value === "yield" && this.prodParam.hasYield)
this.state.value === "of" &&
!this.state.exprAllowed &&
prevType !== tt._function &&
prevType !== tt._class
) {
allowed = true;
}
Expand Down
@@ -0,0 +1 @@
a : { b : 1 }/2/;
@@ -0,0 +1,64 @@
{
"type": "File",
"start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}},
"program": {
"type": "Program",
"start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "LabeledStatement",
"start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}},
"body": {
"type": "BlockStatement",
"start":4,"end":13,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":13}},
"body": [
{
"type": "LabeledStatement",
"start":6,"end":11,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":11}},
"body": {
"type": "ExpressionStatement",
"start":10,"end":11,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":11}},
"expression": {
"type": "NumericLiteral",
"start":10,"end":11,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":11}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
}
},
"label": {
"type": "Identifier",
"start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"b"},
"name": "b"
}
}
],
"directives": []
},
"label": {
"type": "Identifier",
"start":0,"end":1,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":1},"identifierName":"a"},
"name": "a"
}
},
{
"type": "ExpressionStatement",
"start":13,"end":17,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":17}},
"expression": {
"type": "RegExpLiteral",
"start":13,"end":16,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":16}},
"extra": {
"raw": "/2/"
},
"pattern": "2",
"flags": ""
}
}
],
"directives": []
}
}
@@ -0,0 +1 @@
0 ? a : { b : 1 }/2;
@@ -0,0 +1,76 @@
{
"type": "File",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"program": {
"type": "Program",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"expression": {
"type": "ConditionalExpression",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"test": {
"type": "NumericLiteral",
"start":0,"end":1,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":1}},
"extra": {
"rawValue": 0,
"raw": "0"
},
"value": 0
},
"consequent": {
"type": "Identifier",
"start":4,"end":5,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":5},"identifierName":"a"},
"name": "a"
},
"alternate": {
"type": "BinaryExpression",
"start":8,"end":19,"loc":{"start":{"line":1,"column":8},"end":{"line":1,"column":19}},
"left": {
"type": "ObjectExpression",
"start":8,"end":17,"loc":{"start":{"line":1,"column":8},"end":{"line":1,"column":17}},
"properties": [
{
"type": "ObjectProperty",
"start":10,"end":15,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":15}},
"method": false,
"key": {
"type": "Identifier",
"start":10,"end":11,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":11},"identifierName":"b"},
"name": "b"
},
"computed": false,
"shorthand": false,
"value": {
"type": "NumericLiteral",
"start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
}
}
]
},
"operator": "/",
"right": {
"type": "NumericLiteral",
"start":18,"end":19,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":19}},
"extra": {
"rawValue": 2,
"raw": "2"
},
"value": 2
}
}
}
}
],
"directives": []
}
}

0 comments on commit a8c66f4

Please sign in to comment.