Implement optional catch binding.
parent
bf460a5ed3
commit
b09c5594de
|
@ -1547,7 +1547,7 @@ NodeBuilder::catchClause(HandleValue var, HandleValue guard, HandleValue body, T
|
||||||
{
|
{
|
||||||
RootedValue cb(cx, callbacks[AST_CATCH]);
|
RootedValue cb(cx, callbacks[AST_CATCH]);
|
||||||
if (!cb.isNull())
|
if (!cb.isNull())
|
||||||
return callback(cb, var, opt(guard), body, pos, dst);
|
return callback(cb, opt(var), opt(guard), body, pos, dst);
|
||||||
|
|
||||||
return newNode(AST_CATCH, pos,
|
return newNode(AST_CATCH, pos,
|
||||||
"param", var,
|
"param", var,
|
||||||
|
@ -1803,6 +1803,14 @@ class ASTSerializer
|
||||||
bool identifier(ParseNode* pn, MutableHandleValue dst);
|
bool identifier(ParseNode* pn, MutableHandleValue dst);
|
||||||
bool literal(ParseNode* pn, MutableHandleValue dst);
|
bool literal(ParseNode* pn, MutableHandleValue dst);
|
||||||
|
|
||||||
|
bool optPattern(ParseNode* pn, MutableHandleValue dst) {
|
||||||
|
if (!pn) {
|
||||||
|
dst.setMagic(JS_SERIALIZE_NO_NODE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return pattern(pn, dst);
|
||||||
|
}
|
||||||
|
|
||||||
bool pattern(ParseNode* pn, MutableHandleValue dst);
|
bool pattern(ParseNode* pn, MutableHandleValue dst);
|
||||||
bool arrayPattern(ParseNode* pn, MutableHandleValue dst);
|
bool arrayPattern(ParseNode* pn, MutableHandleValue dst);
|
||||||
bool objectPattern(ParseNode* pn, MutableHandleValue dst);
|
bool objectPattern(ParseNode* pn, MutableHandleValue dst);
|
||||||
|
@ -2265,13 +2273,13 @@ ASTSerializer::switchStatement(ParseNode* pn, MutableHandleValue dst)
|
||||||
bool
|
bool
|
||||||
ASTSerializer::catchClause(ParseNode* pn, bool* isGuarded, MutableHandleValue dst)
|
ASTSerializer::catchClause(ParseNode* pn, bool* isGuarded, MutableHandleValue dst)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
|
MOZ_ASSERT_IF(pn->pn_kid1, pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
|
||||||
MOZ_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
|
MOZ_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
|
||||||
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
|
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
|
||||||
|
|
||||||
RootedValue var(cx), guard(cx), body(cx);
|
RootedValue var(cx), guard(cx), body(cx);
|
||||||
|
|
||||||
if (!pattern(pn->pn_kid1, &var) ||
|
if (!optPattern(pn->pn_kid1, &var) ||
|
||||||
!optExpression(pn->pn_kid2, &guard)) {
|
!optExpression(pn->pn_kid2, &guard)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3459,10 +3459,12 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||||
|
|
||||||
case PNK_CATCH:
|
case PNK_CATCH:
|
||||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||||
if (!checkSideEffects(pn->pn_kid1, answer))
|
if (ParseNode* name = pn->pn_kid1) {
|
||||||
return false;
|
if (!checkSideEffects(name, answer))
|
||||||
if (*answer)
|
return false;
|
||||||
return true;
|
if (*answer)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (ParseNode* cond = pn->pn_kid2) {
|
if (ParseNode* cond = pn->pn_kid2) {
|
||||||
if (!checkSideEffects(cond, answer))
|
if (!checkSideEffects(cond, answer))
|
||||||
return false;
|
return false;
|
||||||
|
@ -6638,24 +6640,31 @@ BytecodeEmitter::emitCatch(ParseNode* pn)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ParseNode* pn2 = pn->pn_kid1;
|
ParseNode* pn2 = pn->pn_kid1;
|
||||||
switch (pn2->getKind()) {
|
if (!pn2) {
|
||||||
case PNK_ARRAY:
|
// See ES2019 13.15.7 Runtime Semantics: CatchClauseEvaluation
|
||||||
case PNK_OBJECT:
|
// Catch variable was omitted: discard the exception.
|
||||||
if (!emitDestructuringOps(pn2, DestructuringDeclaration))
|
if (!emit1(JSOP_POP))
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_POP))
|
} else {
|
||||||
return false;
|
switch (pn2->getKind()) {
|
||||||
break;
|
case PNK_ARRAY:
|
||||||
|
case PNK_OBJECT:
|
||||||
|
if (!emitDestructuringOps(pn2, DestructuringDeclaration))
|
||||||
|
return false;
|
||||||
|
if (!emit1(JSOP_POP))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
case PNK_NAME:
|
case PNK_NAME:
|
||||||
if (!emitLexicalInitialization(pn2))
|
if (!emitLexicalInitialization(pn2))
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_POP))
|
if (!emit1(JSOP_POP))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
MOZ_ASSERT(0);
|
MOZ_ASSERT(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a guard expression, emit it and arrange to jump to the next
|
// If there is a guard expression, emit it and arrange to jump to the next
|
||||||
|
@ -6899,7 +6908,9 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn)
|
||||||
EmitterScope emitterScope(this);
|
EmitterScope emitterScope(this);
|
||||||
ScopeKind kind;
|
ScopeKind kind;
|
||||||
if (body->isKind(PNK_CATCH))
|
if (body->isKind(PNK_CATCH))
|
||||||
kind = body->pn_kid1->isKind(PNK_NAME) ? ScopeKind::SimpleCatch : ScopeKind::Catch;
|
kind = (!body->pn_kid1 || body->pn_kid1->isKind(PNK_NAME)) ?
|
||||||
|
ScopeKind::SimpleCatch :
|
||||||
|
ScopeKind::Catch;
|
||||||
else
|
else
|
||||||
kind = ScopeKind::Lexical;
|
kind = ScopeKind::Lexical;
|
||||||
|
|
||||||
|
|
|
@ -1260,9 +1260,10 @@ FoldCatch(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parse
|
||||||
MOZ_ASSERT(node->isKind(PNK_CATCH));
|
MOZ_ASSERT(node->isKind(PNK_CATCH));
|
||||||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||||
|
|
||||||
ParseNode*& declPattern = node->pn_kid1;
|
if (ParseNode*& declPattern = node->pn_kid1) {
|
||||||
if (!Fold(cx, &declPattern, parser, inGenexpLambda))
|
if (!Fold(cx, &declPattern, parser, inGenexpLambda))
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (ParseNode*& cond = node->pn_kid2) {
|
if (ParseNode*& cond = node->pn_kid2) {
|
||||||
if (!FoldCondition(cx, &cond, parser, inGenexpLambda))
|
if (!FoldCondition(cx, &cond, parser, inGenexpLambda))
|
||||||
|
|
|
@ -651,8 +651,10 @@ class NameResolver
|
||||||
// contain arbitrary expressions.
|
// contain arbitrary expressions.
|
||||||
case PNK_CATCH:
|
case PNK_CATCH:
|
||||||
MOZ_ASSERT(cur->isArity(PN_TERNARY));
|
MOZ_ASSERT(cur->isArity(PN_TERNARY));
|
||||||
if (!resolve(cur->pn_kid1, prefix))
|
if (cur->pn_kid1) {
|
||||||
return false;
|
if (!resolve(cur->pn_kid1, prefix))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (cur->pn_kid2) {
|
if (cur->pn_kid2) {
|
||||||
if (!resolve(cur->pn_kid2, prefix))
|
if (!resolve(cur->pn_kid2, prefix))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -409,13 +409,14 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
||||||
return PushResult::Recyclable;
|
return PushResult::Recyclable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A catch node has first kid as catch-variable pattern, the second kid
|
// A catch node has an (optional) first kid as catch-variable pattern,
|
||||||
// as catch condition (which, if non-null, records the |<cond>| in
|
// the second kid as (optional) catch condition (which, records the
|
||||||
// SpiderMonkey's |catch (e if <cond>)| extension), and third kid as the
|
// |<cond>| in SpiderMonkey's |catch (e if <cond>)| extension), and
|
||||||
// statements in the catch block.
|
// third kid as the statements in the catch block.
|
||||||
case PNK_CATCH: {
|
case PNK_CATCH: {
|
||||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||||
stack->push(pn->pn_kid1);
|
if (pn->pn_kid1)
|
||||||
|
stack->push(pn->pn_kid1);
|
||||||
if (pn->pn_kid2)
|
if (pn->pn_kid2)
|
||||||
stack->push(pn->pn_kid2);
|
stack->push(pn->pn_kid2);
|
||||||
stack->push(pn->pn_kid3);
|
stack->push(pn->pn_kid3);
|
||||||
|
|
|
@ -6901,57 +6901,67 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
||||||
/*
|
/*
|
||||||
* Legal catch forms are:
|
* Legal catch forms are:
|
||||||
* catch (lhs)
|
* catch (lhs)
|
||||||
* catch (lhs if <boolean_expression>)
|
* catch (lhs if <boolean_expression>) ** non-standard **
|
||||||
|
* catch ** ES2019 **
|
||||||
* where lhs is a name or a destructuring left-hand side.
|
* where lhs is a name or a destructuring left-hand side.
|
||||||
* (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
|
* The second is legal only #ifdef JS_HAS_CATCH_GUARD
|
||||||
*/
|
*/
|
||||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
|
bool omittedBinding;
|
||||||
|
if (!tokenStream.matchToken(&omittedBinding, TOK_LC))
|
||||||
if (!tokenStream.getToken(&tt))
|
|
||||||
return null();
|
return null();
|
||||||
Node catchName;
|
|
||||||
switch (tt) {
|
|
||||||
case TOK_LB:
|
|
||||||
case TOK_LC:
|
|
||||||
catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
|
|
||||||
yieldHandling, tt);
|
|
||||||
if (!catchName)
|
|
||||||
return null();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: {
|
Node catchName;
|
||||||
if (!TokenKindIsPossibleIdentifierName(tt)) {
|
Node catchGuard = null();
|
||||||
error(JSMSG_CATCH_IDENTIFIER);
|
|
||||||
|
if (omittedBinding) {
|
||||||
|
catchName = null();
|
||||||
|
} else {
|
||||||
|
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
|
||||||
|
|
||||||
|
if (!tokenStream.getToken(&tt))
|
||||||
return null();
|
return null();
|
||||||
|
switch (tt) {
|
||||||
|
case TOK_LB:
|
||||||
|
case TOK_LC:
|
||||||
|
catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
|
||||||
|
yieldHandling, tt);
|
||||||
|
if (!catchName)
|
||||||
|
return null();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
if (!TokenKindIsPossibleIdentifierName(tt)) {
|
||||||
|
error(JSMSG_CATCH_IDENTIFIER);
|
||||||
|
return null();
|
||||||
|
}
|
||||||
|
|
||||||
|
catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter,
|
||||||
|
yieldHandling);
|
||||||
|
if (!catchName)
|
||||||
|
return null();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter,
|
|
||||||
yieldHandling);
|
|
||||||
if (!catchName)
|
|
||||||
return null();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Node catchGuard = null();
|
|
||||||
#if JS_HAS_CATCH_GUARD
|
#if JS_HAS_CATCH_GUARD
|
||||||
/*
|
/*
|
||||||
* We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
|
* We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
|
||||||
* to avoid conflicting with the JS2/ECMAv4 type annotation
|
* to avoid conflicting with the JS2/ECMAv4 type annotation
|
||||||
* catchguard syntax.
|
* catchguard syntax.
|
||||||
*/
|
*/
|
||||||
bool matched;
|
bool matched;
|
||||||
if (!tokenStream.matchToken(&matched, TOK_IF))
|
if (!tokenStream.matchToken(&matched, TOK_IF))
|
||||||
return null();
|
|
||||||
if (matched) {
|
|
||||||
catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited);
|
|
||||||
if (!catchGuard)
|
|
||||||
return null();
|
return null();
|
||||||
}
|
if (matched) {
|
||||||
|
catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited);
|
||||||
|
if (!catchGuard)
|
||||||
|
return null();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
|
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
|
||||||
|
|
||||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
|
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
|
||||||
|
}
|
||||||
|
|
||||||
Node catchBody = catchBlockStatement(yieldHandling, scope);
|
Node catchBody = catchBlockStatement(yieldHandling, scope);
|
||||||
if (!catchBody)
|
if (!catchBody)
|
||||||
|
|
|
@ -77,7 +77,16 @@ assertStmt("try { } catch (e if foo) { } catch (e if bar) { } catch (e) { } fina
|
||||||
catchClause(ident("e"), ident("bar"), blockStmt([])) ],
|
catchClause(ident("e"), ident("bar"), blockStmt([])) ],
|
||||||
catchClause(ident("e"), null, blockStmt([])),
|
catchClause(ident("e"), null, blockStmt([])),
|
||||||
blockStmt([])));
|
blockStmt([])));
|
||||||
|
assertStmt("try { } catch { }",
|
||||||
|
tryStmt(blockStmt([]),
|
||||||
|
[],
|
||||||
|
catchClause(null, null, blockStmt([])),
|
||||||
|
null));
|
||||||
|
assertStmt("try { } catch { } finally { }",
|
||||||
|
tryStmt(blockStmt([]),
|
||||||
|
[],
|
||||||
|
catchClause(null, null, blockStmt([])),
|
||||||
|
blockStmt([])));
|
||||||
|
|
||||||
// Bug 632028: yield outside of a function should throw
|
// Bug 632028: yield outside of a function should throw
|
||||||
(function() {
|
(function() {
|
||||||
|
|
Loading…
Reference in New Issue