Skip to content

Commit

Permalink
forbid yield/await expressions in default parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea authored and marijnh committed Sep 5, 2016
1 parent 0d2070f commit 3bc023e
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 69 deletions.
105 changes: 59 additions & 46 deletions src/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pp.parseExpression = function(noIn, refDestructuringErrors) {
// operators like `+=`.

pp.parseMaybeAssign = function(noIn, refDestructuringErrors, afterLeftParse) {
if (this.inGenerator && this.isContextual("yield")) return this.parseYield()
if (this.inGenerator && this.isContextual("yield")) return this.parseYield(refDestructuringErrors)

let ownDestructuringErrors = false
if (!refDestructuringErrors) {
Expand All @@ -105,14 +105,14 @@ pp.parseMaybeAssign = function(noIn, refDestructuringErrors, afterLeftParse) {
if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc)
if (this.type.isAssign) {
this.checkPatternErrors(refDestructuringErrors, true)
if (!ownDestructuringErrors) DestructuringErrors.call(refDestructuringErrors)
let node = this.startNodeAt(startPos, startLoc)
node.operator = this.value
node.left = this.type === tt.eq ? this.toAssignable(left) : left
refDestructuringErrors.shorthandAssign = 0 // reset because shorthand default was used correctly
this.checkLVal(left)
this.next()
node.right = this.parseMaybeAssign(noIn)
node.right = this.parseMaybeAssign(noIn, refDestructuringErrors)
if (ownDestructuringErrors) this.checkExpressionErrors(refDestructuringErrors, true)
return this.finishNode(node, "AssignmentExpression")
} else {
if (ownDestructuringErrors) this.checkExpressionErrors(refDestructuringErrors, true)
Expand All @@ -129,9 +129,9 @@ pp.parseMaybeConditional = function(noIn, refDestructuringErrors) {
if (this.eat(tt.question)) {
let node = this.startNodeAt(startPos, startLoc)
node.test = expr
node.consequent = this.parseMaybeAssign()
node.consequent = this.parseMaybeAssign(false, refDestructuringErrors)
this.expect(tt.colon)
node.alternate = this.parseMaybeAssign(noIn)
node.alternate = this.parseMaybeAssign(noIn, refDestructuringErrors)
return this.finishNode(node, "ConditionalExpression")
}
return expr
Expand All @@ -143,7 +143,7 @@ pp.parseExprOps = function(noIn, refDestructuringErrors) {
let startPos = this.start, startLoc = this.startLoc
let expr = this.parseMaybeUnary(refDestructuringErrors, false)
if (this.checkExpressionErrors(refDestructuringErrors)) return expr
return this.parseExprOp(expr, startPos, startLoc, -1, noIn)
return this.parseExprOp(expr, startPos, startLoc, -1, noIn, refDestructuringErrors)
}

// Parse binary operators with the operator precedence parsing
Expand All @@ -152,17 +152,17 @@ pp.parseExprOps = function(noIn, refDestructuringErrors) {
// defer further parser to one of its callers when it encounters an
// operator that has a lower precedence than the set it is parsing.

pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn, refDestructuringErrors) {
let prec = this.type.binop
if (prec != null && (!noIn || this.type !== tt._in)) {
if (prec > minPrec) {
let logical = this.type === tt.logicalOR || this.type === tt.logicalAND
let op = this.value
this.next()
let startPos = this.start, startLoc = this.startLoc
let right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn)
let right = this.parseExprOp(this.parseMaybeUnary(refDestructuringErrors, false), startPos, startLoc, prec, noIn, refDestructuringErrors)
let node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical)
return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn, refDestructuringErrors)
}
}
return left
Expand All @@ -188,7 +188,7 @@ pp.parseMaybeUnary = function(refDestructuringErrors, sawUnary) {
node.operator = this.value
node.prefix = true
this.next()
node.argument = this.parseMaybeUnary(null, true)
node.argument = this.parseMaybeUnary(refDestructuringErrors, true)
this.checkExpressionErrors(refDestructuringErrors, true)
if (update) this.checkLVal(node.argument)
else if (this.strict && node.operator === "delete" &&
Expand All @@ -211,7 +211,7 @@ pp.parseMaybeUnary = function(refDestructuringErrors, sawUnary) {
}

if (!sawUnary && this.eat(tt.starstar))
return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false), "**", false)
return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(refDestructuringErrors, false), "**", false)
else
return expr
}
Expand All @@ -223,10 +223,10 @@ pp.parseExprSubscripts = function(refDestructuringErrors) {
let expr = this.parseExprAtom(refDestructuringErrors)
let skipArrowSubscripts = expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")"
if (this.checkExpressionErrors(refDestructuringErrors) || skipArrowSubscripts) return expr
return this.parseSubscripts(expr, startPos, startLoc)
return this.parseSubscripts(expr, startPos, startLoc, false, refDestructuringErrors)
}

pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {
pp.parseSubscripts = function(base, startPos, startLoc, noCalls, refContextDestructuringErrors) {
for (;;) {
let maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" && !this.canInsertSemicolon()
if (this.eat(tt.dot)) {
Expand All @@ -238,25 +238,30 @@ pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {
} else if (this.eat(tt.bracketL)) {
let node = this.startNodeAt(startPos, startLoc)
node.object = base
node.property = this.parseExpression()
node.property = this.parseExpression(false, refContextDestructuringErrors)
node.computed = true
this.expect(tt.bracketR)
base = this.finishNode(node, "MemberExpression")
} else if (!noCalls && this.eat(tt.parenL)) {
let refDestructuringErrors = new DestructuringErrors
let exprList = this.parseExprList(tt.parenR, false, false, refDestructuringErrors)
if (maybeAsyncArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
this.checkDefaultValueErrors(refDestructuringErrors, true, true)
return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true)
}
this.checkExpressionErrors(refDestructuringErrors, true)
if (refContextDestructuringErrors) {
refContextDestructuringErrors.yield = refContextDestructuringErrors.yield || refDestructuringErrors.yield
refContextDestructuringErrors.await = refContextDestructuringErrors.await || refDestructuringErrors.await
}
let node = this.startNodeAt(startPos, startLoc)
node.callee = base
node.arguments = exprList
base = this.finishNode(node, "CallExpression")
} else if (this.type === tt.backQuote) {
let node = this.startNodeAt(startPos, startLoc)
node.tag = base
node.quasi = this.parseTemplate()
node.quasi = this.parseTemplate(refContextDestructuringErrors)
base = this.finishNode(node, "TaggedTemplateExpression")
} else {
return base
Expand Down Expand Up @@ -316,7 +321,7 @@ pp.parseExprAtom = function(refDestructuringErrors) {
return this.finishNode(node, "Literal")

case tt.parenL:
return this.parseParenAndDistinguishExpression(canBeArrow)
return this.parseParenAndDistinguishExpression(canBeArrow, refDestructuringErrors)

case tt.bracketL:
node = this.startNode()
Expand All @@ -330,16 +335,16 @@ pp.parseExprAtom = function(refDestructuringErrors) {
case tt._function:
node = this.startNode()
this.next()
return this.parseFunction(node, false)
return this.parseFunction(node, false, false, false)

case tt._class:
return this.parseClass(this.startNode(), false)
return this.parseClass(this.startNode(), false, refDestructuringErrors)

case tt._new:
return this.parseNew()
return this.parseNew(refDestructuringErrors)

case tt.backQuote:
return this.parseTemplate()
return this.parseTemplate(refDestructuringErrors)

default:
this.unexpected()
Expand All @@ -354,14 +359,14 @@ pp.parseLiteral = function(value) {
return this.finishNode(node, "Literal")
}

pp.parseParenExpression = function() {
pp.parseParenExpression = function(refDestructuringErrors) {
this.expect(tt.parenL)
let val = this.parseExpression()
let val = this.parseExpression(false, refDestructuringErrors)
this.expect(tt.parenR)
return val
}

pp.parseParenAndDistinguishExpression = function(canBeArrow) {
pp.parseParenAndDistinguishExpression = function(canBeArrow, refContextDestructuringErrors) {
let startPos = this.start, startLoc = this.startLoc, val
if (this.options.ecmaVersion >= 6) {
this.next()
Expand All @@ -373,7 +378,7 @@ pp.parseParenAndDistinguishExpression = function(canBeArrow) {
first ? first = false : this.expect(tt.comma)
if (this.type === tt.ellipsis) {
spreadStart = this.start
exprList.push(this.parseParenItem(this.parseRest()))
exprList.push(this.parseParenItem(this.parseRest(false, refDestructuringErrors)))
break
} else {
if (this.type === tt.parenL && !innerParenStart) {
Expand All @@ -387,13 +392,18 @@ pp.parseParenAndDistinguishExpression = function(canBeArrow) {

if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
this.checkPatternErrors(refDestructuringErrors, true)
this.checkDefaultValueErrors(refDestructuringErrors, true, true)
if (innerParenStart) this.unexpected(innerParenStart)
return this.parseParenArrowList(startPos, startLoc, exprList)
}

if (!exprList.length) this.unexpected(this.lastTokStart)
if (spreadStart) this.unexpected(spreadStart)
this.checkExpressionErrors(refDestructuringErrors, true)
if (refContextDestructuringErrors) {
refContextDestructuringErrors.yield = refContextDestructuringErrors.yield || refDestructuringErrors.yield
refContextDestructuringErrors.await = refContextDestructuringErrors.await || refDestructuringErrors.await
}

if (exprList.length > 1) {
val = this.startNodeAt(innerStartPos, innerStartLoc)
Expand All @@ -403,7 +413,7 @@ pp.parseParenAndDistinguishExpression = function(canBeArrow) {
val = exprList[0]
}
} else {
val = this.parseParenExpression()
val = this.parseParenExpression(refContextDestructuringErrors)
}

if (this.options.preserveParens) {
Expand All @@ -420,7 +430,7 @@ pp.parseParenItem = function(item) {
}

pp.parseParenArrowList = function(startPos, startLoc, exprList) {
return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)
return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, false)
}

// New's precedence is slightly tricky. It must allow its argument to
Expand All @@ -431,7 +441,7 @@ pp.parseParenArrowList = function(startPos, startLoc, exprList) {

const empty = []

pp.parseNew = function() {
pp.parseNew = function(refDestructuringErrors) {
let node = this.startNode()
let meta = this.parseIdent(true)
if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {
Expand All @@ -444,8 +454,8 @@ pp.parseNew = function() {
return this.finishNode(node, "MetaProperty")
}
let startPos = this.start, startLoc = this.startLoc
node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true)
if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false)
node.callee = this.parseSubscripts(this.parseExprAtom(refDestructuringErrors), startPos, startLoc, true, refDestructuringErrors)
if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false, false, refDestructuringErrors)
else node.arguments = empty
return this.finishNode(node, "NewExpression")
}
Expand All @@ -463,15 +473,15 @@ pp.parseTemplateElement = function() {
return this.finishNode(elem, "TemplateElement")
}

pp.parseTemplate = function() {
pp.parseTemplate = function(refDestructuringErrors) {
let node = this.startNode()
this.next()
node.expressions = []
let curElt = this.parseTemplateElement()
node.quasis = [curElt]
while (!curElt.tail) {
this.expect(tt.dollarBraceL)
node.expressions.push(this.parseExpression())
node.expressions.push(this.parseExpression(false, refDestructuringErrors))
this.expect(tt.braceR)
node.quasis.push(curElt = this.parseTemplateElement())
}
Expand Down Expand Up @@ -502,7 +512,7 @@ pp.parseObj = function(isPattern, refDestructuringErrors) {
if (!isPattern)
isGenerator = this.eat(tt.star)
}
this.parsePropertyName(prop)
this.parsePropertyName(prop, refDestructuringErrors)
if (!isPattern && this.options.ecmaVersion >= 8 && !isGenerator && !prop.computed &&
prop.key.type === "Identifier" && prop.key.name === "async" && this.type !== tt.parenL &&
!this.canInsertSemicolon()) {
Expand All @@ -520,7 +530,7 @@ pp.parseObj = function(isPattern, refDestructuringErrors) {

pp.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors) {
if (this.eat(tt.colon)) {
prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors)
prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc, null, refDestructuringErrors) : this.parseMaybeAssign(false, refDestructuringErrors)
prop.kind = "init"
} else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) {
if (isPattern) this.unexpected()
Expand All @@ -532,8 +542,8 @@ pp.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos
(this.type != tt.comma && this.type != tt.braceR)) {
if (isGenerator || isAsync || isPattern) this.unexpected()
prop.kind = prop.key.name
this.parsePropertyName(prop)
prop.value = this.parseMethod(false)
this.parsePropertyName(prop, refDestructuringErrors)
prop.value = this.parseMethod(false, false)
let paramCount = prop.kind === "get" ? 0 : 1
if (prop.value.params.length !== paramCount) {
let start = prop.value.start
Expand All @@ -553,30 +563,30 @@ pp.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos
this.raiseRecoverable(prop.key.start, "'" + prop.key.name + "' can not be used as shorthand property")
prop.kind = "init"
if (isPattern) {
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key, refDestructuringErrors)
} else if (this.type === tt.eq && refDestructuringErrors) {
if (!refDestructuringErrors.shorthandAssign)
refDestructuringErrors.shorthandAssign = this.start
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key, refDestructuringErrors)
} else {
prop.value = prop.key
}
prop.shorthand = true
} else this.unexpected()
}

pp.parsePropertyName = function(prop) {
pp.parsePropertyName = function(prop, refDestructuringErrors) {
if (this.options.ecmaVersion >= 6) {
if (this.eat(tt.bracketL)) {
prop.computed = true
prop.key = this.parseMaybeAssign()
prop.key = this.parseMaybeAssign(false, refDestructuringErrors)
this.expect(tt.bracketR)
return prop.key
} else {
prop.computed = false
}
}
return prop.key = this.type === tt.num || this.type === tt.string ? this.parseExprAtom() : this.parseIdent(true)
return prop.key = this.type === tt.num || this.type === tt.string ? this.parseExprAtom(refDestructuringErrors) : this.parseIdent(true)
}

// Initialize empty function node.
Expand All @@ -594,7 +604,7 @@ pp.initFunction = function(node) {
// Parse object or class method.

pp.parseMethod = function(isGenerator, isAsync) {
let node = this.startNode(), oldInGen = this.inGenerator, oldInAsync = this.inAsync
let node = this.startNode(), refDestructuringErrors = new DestructuringErrors, oldInGen = this.inGenerator, oldInAsync = this.inAsync
this.initFunction(node)
if (this.options.ecmaVersion >= 6)
node.generator = isGenerator
Expand All @@ -603,7 +613,8 @@ pp.parseMethod = function(isGenerator, isAsync) {
this.inGenerator = node.generator
this.inAsync = node.async
this.expect(tt.parenL)
node.params = this.parseBindingList(tt.parenR, false, false)
node.params = this.parseBindingList(tt.parenR, false, false, false, refDestructuringErrors)
this.checkDefaultValueErrors(refDestructuringErrors, false, true)
this.parseFunctionBody(node, false)
this.inGenerator = oldInGen
this.inAsync = oldInAsync
Expand Down Expand Up @@ -734,22 +745,24 @@ pp.parseIdent = function(liberal) {

// Parses yield expression inside generator.

pp.parseYield = function() {
pp.parseYield = function(refDestructuringErrors) {
let node = this.startNode()
if (refDestructuringErrors) refDestructuringErrors.yield = this.start
this.next()
if (this.type == tt.semi || this.canInsertSemicolon() || (this.type != tt.star && !this.type.startsExpr)) {
node.delegate = false
node.argument = null
} else {
node.delegate = this.eat(tt.star)
node.argument = this.parseMaybeAssign()
node.argument = this.parseMaybeAssign(false, refDestructuringErrors)
}
return this.finishNode(node, "YieldExpression")
}

pp.parseAwait = function() {
pp.parseAwait = function(refDestructuringErrors) {
let node = this.startNode()
if (refDestructuringErrors) refDestructuringErrors.await = this.start
this.next()
node.argument = this.parseMaybeUnary(null, true)
node.argument = this.parseMaybeUnary(refDestructuringErrors, true)
return this.finishNode(node, "AwaitExpression")
}
Loading

0 comments on commit 3bc023e

Please sign in to comment.