diff options
author | janekptacijarabaci <janekptacijarabaci@seznam.cz> | 2018-03-19 15:47:10 +0100 |
---|---|---|
committer | janekptacijarabaci <janekptacijarabaci@seznam.cz> | 2018-03-19 15:47:10 +0100 |
commit | 5ef44cf6484b9dfd49c0174ac2969a29587a1bbd (patch) | |
tree | ef1e8a83106f6b54504dbe3b29554ef7b349a1bb /js/src/frontend | |
parent | 6822460d3b0d4609ee5d4e1ab4b093799ed06580 (diff) | |
download | UXP-5ef44cf6484b9dfd49c0174ac2969a29587a1bbd.tar UXP-5ef44cf6484b9dfd49c0174ac2969a29587a1bbd.tar.gz UXP-5ef44cf6484b9dfd49c0174ac2969a29587a1bbd.tar.lz UXP-5ef44cf6484b9dfd49c0174ac2969a29587a1bbd.tar.xz UXP-5ef44cf6484b9dfd49c0174ac2969a29587a1bbd.zip |
Part 1: Implement ES6 function name property semantics
Issue #78
Diffstat (limited to 'js/src/frontend')
-rw-r--r-- | js/src/frontend/BytecodeEmitter.cpp | 111 | ||||
-rw-r--r-- | js/src/frontend/BytecodeEmitter.h | 11 | ||||
-rw-r--r-- | js/src/frontend/FullParseHandler.h | 7 | ||||
-rw-r--r-- | js/src/frontend/ParseNode-inl.h | 2 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.cpp | 18 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.h | 15 | ||||
-rw-r--r-- | js/src/frontend/Parser.cpp | 35 | ||||
-rw-r--r-- | js/src/frontend/SyntaxParseHandler.h | 3 |
8 files changed, 178 insertions, 24 deletions
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 0a1b4ffe1..205bbf3d9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4070,7 +4070,7 @@ BytecodeEmitter::isRunOnceLambda() FunctionBox* funbox = sc->asFunctionBox(); return !funbox->argumentsHasLocalBinding() && !funbox->isGenerator() && - !funbox->function()->name(); + !funbox->function()->explicitName(); } bool @@ -4491,7 +4491,7 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted) } bool -BytecodeEmitter::emitDefault(ParseNode* defaultExpr) +BytecodeEmitter::emitDefault(ParseNode* defaultExpr, ParseNode* pattern) { if (!emit1(JSOP_DUP)) // VALUE VALUE return false; @@ -4507,13 +4507,79 @@ BytecodeEmitter::emitDefault(ParseNode* defaultExpr) return false; if (!emit1(JSOP_POP)) // . return false; - if (!emitTreeInBranch(defaultExpr)) // DEFAULTVALUE + if (!emitInitializerInBranch(defaultExpr, pattern)) // DEFAULTVALUE return false; if (!emitJumpTargetAndPatch(jump)) return false; return true; } +bool +BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name, + FunctionPrefixKind prefixKind) +{ + if (maybeFun->isKind(PNK_FUNCTION)) { + // Function doesn't have 'name' property at this point. + // Set function's name at compile time. + RootedFunction fun(cx, maybeFun->pn_funbox->function()); + + // Single node can be emitted multiple times if it appears in + // array destructuring default. If function already has a name, + // just return. + if (fun->hasCompileTimeName()) { +#ifdef DEBUG + RootedAtom funName(cx, NameToFunctionName(cx, name, prefixKind)); + if (!funName) + return false; + MOZ_ASSERT(funName == maybeFun->pn_funbox->function()->compileTimeName()); +#endif + return true; + } + + RootedAtom funName(cx, NameToFunctionName(cx, name, prefixKind)); + if (!funName) + return false; + if (fun->hasGuessedAtom()) + fun->clearGuessedAtom(); + fun->setCompileTimeName(name); + return true; + } + + uint32_t nameIndex; + if (!makeAtomIndex(name, &nameIndex)) + return false; + if (!emitIndexOp(JSOP_STRING, nameIndex)) // FUN NAME + return false; + uint8_t kind = uint8_t(prefixKind); + if (!emit2(JSOP_SETFUNNAME, kind)) // FUN + return false; + return true; +} + +bool +BytecodeEmitter::emitInitializer(ParseNode* initializer, ParseNode* pattern) +{ + if (!emitTree(initializer)) + return false; + + if (!pattern->isInParens() && pattern->isKind(PNK_NAME) && + initializer->isDirectRHSAnonFunction()) + { + RootedAtom name(cx, pattern->name()); + if (!setOrEmitSetFunName(initializer, name, FunctionPrefixKind::None)) + return false; + } + + return true; +} + +bool +BytecodeEmitter::emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern) +{ + TDZCheckCache tdzCache(this); + return emitInitializer(initializer, pattern); +} + class MOZ_STACK_CLASS IfThenElseEmitter { BytecodeEmitter* bce_; @@ -4834,7 +4900,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav if (pndefault) { // Emit only pndefault tree here, as undefined check in emitDefault // should always be true. - if (!emitTreeInBranch(pndefault)) // ... OBJ? ITER VALUE + if (!emitInitializerInBranch(pndefault, subpattern)) // ... OBJ? ITER VALUE return false; } else { if (!isElision) { @@ -4872,7 +4938,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav return false; if (pndefault) { - if (!emitDefault(pndefault)) // ... OBJ? ITER VALUE + if (!emitDefault(pndefault, subpattern)) // ... OBJ? ITER VALUE return false; } @@ -4980,7 +5046,7 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla return false; if (subpattern->isKind(PNK_ASSIGN)) { - if (!emitDefault(subpattern->pn_right)) + if (!emitDefault(subpattern->pn_right, subpattern->pn_left)) return false; subpattern = subpattern->pn_left; } @@ -5094,7 +5160,7 @@ BytecodeEmitter::emitSingleDeclaration(ParseNode* declList, ParseNode* decl, if (!initializer && declList->isKind(PNK_VAR)) return true; - auto emitRhs = [initializer, declList](BytecodeEmitter* bce, const NameLocation&, bool) { + auto emitRhs = [initializer, declList, decl](BytecodeEmitter* bce, const NameLocation&, bool) if (!initializer) { // Lexical declarations are initialized to undefined without an // initializer. @@ -5105,7 +5171,7 @@ BytecodeEmitter::emitSingleDeclaration(ParseNode* declList, ParseNode* decl, } MOZ_ASSERT(initializer); - return bce->emitTree(initializer); + return bce->emitInitializer(initializer, decl); }; if (!emitInitializeName(decl, emitRhs)) @@ -5164,6 +5230,12 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs) if (!EmitAssignmentRhs(bce, rhs, emittedBindOp ? 2 : 1)) return false; + if (!lhs->isInParens() && op == JSOP_NOP && rhs && rhs->isDirectRHSAnonFunction()) { + RootedAtom name(bce->cx, lhs->name()); + if (!bce->setOrEmitSetFunName(rhs, name, FunctionPrefixKind::None)) + return false; + } + // Emit the compound assignment op if there is one. if (op != JSOP_NOP && !bce->emit1(op)) return false; @@ -6283,8 +6355,8 @@ BytecodeEmitter::emitForIn(ParseNode* forInLoop, EmitterScope* headLexicalEmitte if (!updateSourceCoordNotes(decl->pn_pos.begin)) return false; - auto emitRhs = [initializer](BytecodeEmitter* bce, const NameLocation&, bool) { - return bce->emitTree(initializer); + auto emitRhs = [decl, initializer](BytecodeEmitter* bce, const NameLocation&, bool) { + return bce->emitInitializer(initializer, decl); }; if (!emitInitializeName(decl, emitRhs)) @@ -6903,7 +6975,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) { FunctionBox* funbox = pn->pn_funbox; RootedFunction fun(cx, funbox->function()); - RootedAtom name(cx, fun->name()); + RootedAtom name(cx, fun->explicitName()); MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript()); MOZ_ASSERT_IF(pn->isOp(JSOP_FUNWITHPROTO), needsProto); @@ -8532,6 +8604,10 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER); + FunctionPrefixKind prefixKind = op == JSOP_INITPROP_GETTER ? FunctionPrefixKind::Get + : op == JSOP_INITPROP_SETTER ? FunctionPrefixKind::Set + : FunctionPrefixKind::None; + if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER) objp.set(nullptr); @@ -8573,6 +8649,12 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, case JSOP_INITHIDDENPROP_SETTER: op = JSOP_INITHIDDENELEM_SETTER; break; default: MOZ_CRASH("Invalid op"); } + if (propdef->pn_right->isDirectRHSAnonFunction()) { + if (!emitDupAt(1)) + return false; + if (!emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) + return false; + } if (!emit1(op)) return false; } else { @@ -8597,6 +8679,11 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, objp.set(nullptr); } + if (propdef->pn_right->isDirectRHSAnonFunction()) { + RootedAtom keyName(cx, key->pn_atom); + if (!setOrEmitSetFunName(propdef->pn_right, keyName, prefixKind)) + return false; + } if (!emitIndex32(op, index)) return false; } @@ -9017,7 +9104,7 @@ BytecodeEmitter::emitFunctionFormalParameters(ParseNode* pn) return false; if (!emit1(JSOP_POP)) return false; - if (!emitTreeInBranch(initializer)) + if (!emitInitializerInBranch(initializer, bindingElement)) return false; if (!emitJumpTargetAndPatch(jump)) return false; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 08e0eb54f..9a2ddb568 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -677,7 +677,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter // Check if the value on top of the stack is "undefined". If so, replace // that value on the stack with the value defined by |defaultExpr|. - MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr); + // |pattern| is a lhs node of the default expression. If it's an + // identifier and |defaultExpr| is an anonymous function, |SetFunctionName| + // is called at compile time. + MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern); + + MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name, + FunctionPrefixKind prefixKind); + + MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern); + MOZ_MUST_USE bool emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern); MOZ_MUST_USE bool emitCallSiteObject(ParseNode* pn); MOZ_MUST_USE bool emitTemplateString(ParseNode* pn); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index add881900..0fd137796 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -665,6 +665,11 @@ class FullParseHandler ParseNode* pn); inline void setLastFunctionFormalParameterDestructuring(ParseNode* funcpn, ParseNode* pn); + void checkAndSetIsDirectRHSAnonFunction(ParseNode* pn) { + if (IsAnonymousFunctionDefinition(pn)) + pn->setDirectRHSAnonFunction(true); + } + ParseNode* newFunctionDefinition() { return new_<CodeNode>(PNK_FUNCTION, pos()); } @@ -942,6 +947,8 @@ FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, Parse if (!pn) return false; + checkAndSetIsDirectRHSAnonFunction(defaultValue); + funcpn->pn_body->pn_pos.end = pn->pn_pos.end; ParseNode* pnchild = funcpn->pn_body->pn_head; ParseNode* pnlast = funcpn->pn_body->last(); diff --git a/js/src/frontend/ParseNode-inl.h b/js/src/frontend/ParseNode-inl.h index 395d09b5b..21bd83766 100644 --- a/js/src/frontend/ParseNode-inl.h +++ b/js/src/frontend/ParseNode-inl.h @@ -18,7 +18,7 @@ inline PropertyName* ParseNode::name() const { MOZ_ASSERT(isKind(PNK_FUNCTION) || isKind(PNK_NAME)); - JSAtom* atom = isKind(PNK_FUNCTION) ? pn_funbox->function()->name() : pn_atom; + JSAtom* atom = isKind(PNK_FUNCTION) ? pn_funbox->function()->explicitName() : pn_atom; return atom->asPropertyName(); } diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index f79baba9e..ece3a45df 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -902,3 +902,21 @@ FunctionBox::trace(JSTracer* trc) if (enclosingScope_) TraceRoot(trc, &enclosingScope_, "funbox-enclosingScope"); } + +bool +js::frontend::IsAnonymousFunctionDefinition(ParseNode* pn) +{ + // ES 2017 draft + // 12.15.2 (ArrowFunction, AsyncArrowFunction). + // 14.1.12 (FunctionExpression). + // 14.4.8 (GeneratorExpression). + // 14.6.8 (AsyncFunctionExpression) + if (pn->isKind(PNK_FUNCTION) && !pn->pn_funbox->function()->explicitName()) + return true; + + // 14.5.8 (ClassExpression) + if (pn->is<ClassNode>() && !pn->as<ClassNode>().names()) + return true; + + return false; +} diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index d37aaaae0..ff26279af 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -450,6 +450,9 @@ class ParseNode uint8_t pn_op; /* see JSOp enum and jsopcode.tbl */ uint8_t pn_arity:4; /* see ParseNodeArity enum */ bool pn_parens:1; /* this expr was enclosed in parens */ + bool pn_rhs_anon_fun:1; /* this expr is anonymous function or class that + * is a direct RHS of PNK_ASSIGN or PNK_COLON of + * property, that needs SetFunctionName. */ ParseNode(const ParseNode& other) = delete; void operator=(const ParseNode& other) = delete; @@ -460,6 +463,7 @@ class ParseNode pn_op(op), pn_arity(arity), pn_parens(false), + pn_rhs_anon_fun(false), pn_pos(0, 0), pn_next(nullptr) { @@ -472,6 +476,7 @@ class ParseNode pn_op(op), pn_arity(arity), pn_parens(false), + pn_rhs_anon_fun(false), pn_pos(pos), pn_next(nullptr) { @@ -512,6 +517,13 @@ class ParseNode bool isLikelyIIFE() const { return isInParens(); } void setInParens(bool enabled) { pn_parens = enabled; } + bool isDirectRHSAnonFunction() const { + return pn_rhs_anon_fun; + } + void setDirectRHSAnonFunction(bool enabled) { + pn_rhs_anon_fun = enabled; + } + TokenPos pn_pos; /* two 16-bit pairs here, for 64 bits */ ParseNode* pn_next; /* intrinsic link in parent PN_LIST */ @@ -1444,6 +1456,9 @@ FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals) return argsBody->pn_head; } +bool +IsAnonymousFunctionDefinition(ParseNode* pn); + } /* namespace frontend */ } /* namespace js */ diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 78e47ceb3..3106702cf 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -326,10 +326,14 @@ ParseContext::init() if (fun->isNamedLambda()) { if (!namedLambdaScope_->init(this)) return false; - AddDeclaredNamePtr p = namedLambdaScope_->lookupDeclaredNameForAdd(fun->name()); + AddDeclaredNamePtr p = + namedLambdaScope_->lookupDeclaredNameForAdd(fun->explicitName()); MOZ_ASSERT(!p); - if (!namedLambdaScope_->addDeclaredName(this, p, fun->name(), DeclarationKind::Const)) + if (!namedLambdaScope_->addDeclaredName(this, p, fun->explicitName(), + DeclarationKind::Const)) + { return false; + } } if (!functionScope_->init(this)) @@ -367,7 +371,7 @@ ParseContext::removeInnerFunctionBoxesForAnnexB(JSAtom* name) { for (uint32_t i = 0; i < innerFunctionBoxesForAnnexB_->length(); i++) { if (FunctionBox* funbox = innerFunctionBoxesForAnnexB_[i]) { - if (funbox->function()->name() == name) + if (funbox->function()->explicitName() == name) innerFunctionBoxesForAnnexB_[i] = nullptr; } } @@ -3477,8 +3481,8 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, if (!body) return false; - if ((kind != Method && !IsConstructorKind(kind)) && fun->name()) { - RootedPropertyName propertyName(context, fun->name()->asPropertyName()); + if ((kind != Method && !IsConstructorKind(kind)) && fun->explicitName()) { + RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName()); if (!checkStrictBinding(propertyName, handler.getPosition(pn))) return false; } @@ -4337,6 +4341,8 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To if (!init) return null(); + handler.checkAndSetIsDirectRHSAnonFunction(init); + if (forHeadKind) { // For for(;;) declarations, consistency with |for (;| parsing requires // that the ';' first be examined as Operand, even though absence of a @@ -4366,6 +4372,8 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding, if (!initializer) return false; + handler.checkAndSetIsDirectRHSAnonFunction(initializer); + if (forHeadKind) { if (initialDeclaration) { bool isForIn, isForOf; @@ -5063,7 +5071,7 @@ Parser<FullParseHandler>::exportDeclaration() if (!kid) return null(); - if (!checkExportedName(kid->pn_funbox->function()->name())) + if (!checkExportedName(kid->pn_funbox->function()->explicitName())) return null(); break; @@ -6670,8 +6678,6 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, return null(); } - // FIXME: Implement ES6 function "name" property semantics - // (bug 883377). RootedAtom funName(context); switch (propType) { case PropertyType::GetterNoExpressionClosure: @@ -6694,6 +6700,8 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, if (!fn) return null(); + handler.checkAndSetIsDirectRHSAnonFunction(fn); + JSOp op = JSOpFromPropertyType(propType); if (!handler.addClassMethodDefinition(classMethods, propName, fn, op, isStatic)) return null(); @@ -7753,6 +7761,9 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl return null(); } + if (kind == PNK_ASSIGN) + handler.checkAndSetIsDirectRHSAnonFunction(rhs); + return handler.newAssignment(kind, lhs, rhs, op); } @@ -9155,6 +9166,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* if (!propExpr) return null(); + handler.checkAndSetIsDirectRHSAnonFunction(propExpr); + if (foldConstants && !FoldConstants(context, &propExpr, this)) return null(); @@ -9268,6 +9281,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* return null(); } + handler.checkAndSetIsDirectRHSAnonFunction(rhs); + Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); if (!propExpr) return null(); @@ -9278,8 +9293,6 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* if (!abortIfSyntaxParser()) return null(); } else { - // FIXME: Implement ES6 function "name" property semantics - // (bug 883377). RootedAtom funName(context); if (!tokenStream.isCurrentTokenType(TOK_RB)) { funName = propAtom; @@ -9295,6 +9308,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* if (!fn) return null(); + handler.checkAndSetIsDirectRHSAnonFunction(fn); + JSOp op = JSOpFromPropertyType(propType); if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) return null(); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 75c7e3333..b7f00605b 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -339,6 +339,9 @@ class SyntaxParseHandler Node catchGuard, Node catchBody) { return true; } MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(Node funcpn, Node pn) { return true; } + + void checkAndSetIsDirectRHSAnonFunction(Node pn) {} + Node newFunctionDefinition() { return NodeFunctionDefinition; } bool setComprehensionLambdaBody(Node pn, Node body) { return true; } void setFunctionFormalParametersAndBody(Node pn, Node kid) {} |