diff options
author | Martok <martok@martoks-place.de> | 2023-04-11 09:48:34 +0200 |
---|---|---|
committer | Martok <martok@martoks-place.de> | 2023-05-01 17:16:21 +0200 |
commit | 5e3219018643b3886611d4f63ed905d28aaf7b8f (patch) | |
tree | 0920f6ddfc3f263932c2ff2636e965f580574723 | |
parent | 7b74607f1fa0e93d00dc9261b6cd47d10324c92e (diff) | |
download | uxp-5e3219018643b3886611d4f63ed905d28aaf7b8f.tar.gz |
Issue #2142 - Implement class static block
Based-on: m-c 1712138/{2,3}, 1713155/2
-rw-r--r-- | js/src/builtin/ReflectParse.cpp | 38 | ||||
-rw-r--r-- | js/src/frontend/BytecodeEmitter.cpp | 41 | ||||
-rw-r--r-- | js/src/frontend/FoldConstants.cpp | 2 | ||||
-rw-r--r-- | js/src/frontend/FullParseHandler.h | 6 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.cpp | 1 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.h | 30 | ||||
-rw-r--r-- | js/src/frontend/Parser.cpp | 167 | ||||
-rw-r--r-- | js/src/frontend/Parser.h | 19 | ||||
-rw-r--r-- | js/src/frontend/SharedContext.h | 4 | ||||
-rw-r--r-- | js/src/frontend/SyntaxParseHandler.h | 1 | ||||
-rw-r--r-- | js/src/js.msg | 1 | ||||
-rw-r--r-- | js/src/jsast.tbl | 1 |
12 files changed, 277 insertions, 34 deletions
diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 6ca0c30e16..b40c5fb408 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -544,6 +544,7 @@ class NodeBuilder TokenPos* pos, MutableHandleValue dst); MOZ_MUST_USE bool classField(HandleValue name, HandleValue initializer, TokenPos* pos, MutableHandleValue dst); + MOZ_MUST_USE bool staticClassBlock(HandleValue body, TokenPos* pos, MutableHandleValue dst); /* * expressions @@ -1737,6 +1738,18 @@ NodeBuilder::classField(HandleValue name, HandleValue initializer, } bool +NodeBuilder::staticClassBlock(HandleValue body, TokenPos* pos, MutableHandleValue dst) +{ + RootedValue cb(cx, callbacks[AST_STATIC_CLASS_BLOCK]); + if (!cb.isNull()) + return callback(cb, body, pos, dst); + + return newNode(AST_STATIC_CLASS_BLOCK, pos, + "body", body, + dst); +} + +bool NodeBuilder::classMembers(NodeVector& members, MutableHandleValue dst) { return newArray(members, dst); @@ -1870,6 +1883,7 @@ class ASTSerializer bool classMethod(ClassMethod* classMethod, MutableHandleValue dst); bool classField(ClassField* classField, MutableHandleValue dst); + bool staticClassBlock(StaticClassBlock* staticClassBlock, MutableHandleValue dst); bool optIdentifier(HandleAtom atom, TokenPos* pos, MutableHandleValue dst) { if (!atom) { @@ -2709,7 +2723,14 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) RootedValue prop(cx); if (!classField(field, &prop)) - return false; + return false; + members.infallibleAppend(prop); + } else if (item->is<StaticClassBlock>()) { + StaticClassBlock* scb = &item->as<StaticClassBlock>(); + MOZ_ASSERT(memberList->pn_pos.encloses(scb->pn_pos)); + RootedValue prop(cx); + if (!staticClassBlock(scb, &prop)) + return false; members.infallibleAppend(prop); } else { ClassMethod* method = &item->as<ClassMethod>(); @@ -2788,6 +2809,21 @@ ASTSerializer::classField(ClassField* classField, MutableHandleValue dst) } bool +ASTSerializer::staticClassBlock(StaticClassBlock* staticClassBlock, MutableHandleValue dst) +{ + FunctionNode* fun = staticClassBlock->function(); + + NodeVector args(cx); + NodeVector defaults(cx); + + RootedValue body(cx), rest(cx); + rest.setNull(); + return functionArgsAndBody(fun->body(), args, defaults, false, false, + &body, &rest) && + builder.staticClassBlock(body, &staticClassBlock->pn_pos, dst); +} + +bool ASTSerializer::leftAssociate(ListNode* node, MutableHandleValue dst) { MOZ_ASSERT(!node->empty()); diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 1524493cb0..eb25903911 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1529,6 +1529,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_CLASSMETHOD: // by PNK_CLASS case PNK_CLASSFIELD: // by PNK_CLASS case PNK_CLASSNAMES: // by PNK_CLASS + case PNK_STATICCLASSBLOCK:// by PNK_CLASS case PNK_CLASSMEMBERLIST: // by PNK_CLASS case PNK_IMPORT_SPEC_LIST: // by PNK_IMPORT case PNK_IMPORT_SPEC: // by PNK_IMPORT @@ -7702,6 +7703,12 @@ BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe, PropListTy continue; } + if (propdef->is<StaticClassBlock>()) { + // Static class blocks are emitted as part of + // emitCreateFieldInitializers. + continue; + } + if (propdef->is<LexicalScopeNode>()) { // Constructors are sometimes wrapped in LexicalScopeNodes. As we already // handled emitting the constructor, skip it. @@ -7943,13 +7950,32 @@ BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe, PropListTy return true; } +static bool +HasInitializer(ParseNode* node, bool isStaticContext) +{ + // For the purposes of bytecode emission, StaticClassBlocks are treated as if + // they were static initializers. + return (node->is<ClassField>() && + node->as<ClassField>().isStatic() == isStaticContext) || + (isStaticContext && node->is<StaticClassBlock>()); +} + +static FunctionNode* +GetInitializer(ParseNode* node, bool isStaticContext) +{ + MOZ_ASSERT(HasInitializer(node, isStaticContext)); + MOZ_ASSERT_IF(!node->is<ClassField>(), isStaticContext); + return node->is<ClassField>() ? node->as<ClassField>().initializer() + : node->as<StaticClassBlock>().function(); +} + + FieldInitializers BytecodeEmitter::setupFieldInitializers(ListNode* classMembers, FieldPlacement placement) { bool isStatic = placement == FieldPlacement::Static; size_t numFields = classMembers->count_if([isStatic](ParseNode* propdef) { - return propdef->is<ClassField>()&& - propdef->as<ClassField>().isStatic() == isStatic; + return HasInitializer(propdef, isStatic); }); return FieldInitializers(numFields); @@ -8040,11 +8066,10 @@ BytecodeEmitter::emitCreateFieldInitializers(ClassEmitter& ce, ListNode* obj, } for (ParseNode* propdef : obj->contents()) { - if (!propdef->is<ClassField>() || - propdef->as<ClassField>().isStatic() != isStatic) + if (!HasInitializer(propdef, isStatic)) continue; - FunctionNode* initializer = propdef->as<ClassField>().initializer(); + FunctionNode* initializer = GetInitializer(propdef, isStatic); if (!ce.prepareForFieldInitializer()) return false; if (!emitTree(initializer)) { @@ -8173,8 +8198,7 @@ bool BytecodeEmitter::emitInitializeStaticFields(ListNode* classMembers) { size_t numFields = classMembers->count_if([](ParseNode* propdef) { - return propdef->is<ClassField>()&& - propdef->as<ClassField>().isStatic(); + return HasInitializer(propdef, true); }); if (numFields == 0) { @@ -8816,8 +8840,7 @@ BytecodeEmitter::emitClass(ClassNode* classNode) // As an optimization omit the |.initializers| binding when no instance // fields are present. bool hasInstanceFields = classMembers->any_of([](ParseNode* propdef) { - return propdef->is<ClassField>() && - !propdef->as<ClassField>().isStatic(); + return HasInitializer(propdef, false); }); if (hasInstanceFields) { lse.emplace(this); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index bb8f0d11b6..813900a23f 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -403,6 +403,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_FOROF: case PNK_FORHEAD: case PNK_CLASSFIELD: + case PNK_STATICCLASSBLOCK: case PNK_CLASSMEMBERLIST: case PNK_CLASSNAMES: case PNK_NEWTARGET: @@ -1749,6 +1750,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo case PNK_ARRAYPUSH: case PNK_MUTATEPROTO: case PNK_COMPUTED_NAME: + case PNK_STATICCLASSBLOCK: case PNK_SPREAD: case PNK_EXPORT: case PNK_VOID: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 4f3492af4d..14733d74f2 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -474,12 +474,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) return new_<ClassField>(name, initializer, isStatic); } + MOZ_MUST_USE StaticClassBlock* newStaticClassBlock(FunctionNodeType block) + { + return new_<StaticClassBlock>(block); + } + MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList, Node member) { MOZ_ASSERT(memberList->isKind(PNK_CLASSMEMBERLIST)); // Constructors can be surrounded by LexicalScopes. MOZ_ASSERT(member->isKind(PNK_CLASSMETHOD) || member->isKind(PNK_CLASSFIELD) || + member->isKind(PNK_STATICCLASSBLOCK) || (member->isKind(PNK_LEXICALSCOPE) && member->as<LexicalScopeNode>().scopeBody()->isKind(PNK_CLASSMETHOD))); diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index de3ea60931..a529c93d14 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -236,6 +236,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_PREDECREMENT: case PNK_POSTDECREMENT: case PNK_COMPUTED_NAME: + case PNK_STATICCLASSBLOCK: case PNK_ARRAYPUSH: case PNK_SPREAD: case PNK_MUTATEPROTO: diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 4c9f1cc914..471f42f905 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -117,6 +117,7 @@ class ObjectBox; F(MUTATEPROTO) \ F(CLASS) \ F(CLASSMETHOD) \ + F(STATICCLASSBLOCK) \ F(CLASSFIELD) \ F(CLASSMEMBERLIST) \ F(CLASSNAMES) \ @@ -259,7 +260,7 @@ IsTypeofKind(ParseNodeKind kind) * that doesn't create an outer binding * right: Name node for inner binding * PNK_CLASSMEMBERLIST (ListNode) - * head: list of N PNK_CLASSMETHOD or PNK_CLASSFIELD nodes + * head: list of N PNK_CLASSMETHOD, PNK_CLASSFIELD or PNK_STATICCLASSBLOCK nodes * count: N >= 0 * PNK_CLASSMETHOD (ClassMethod) * name: propertyName @@ -267,6 +268,8 @@ IsTypeofKind(ParseNodeKind kind) * PNK_CLASSFIELD (ClassField) * name: fieldName * initializer: field initializer or null + * PNK_STATICCLASSBLOCK (StaticClassBlock) + * block: block initializer * PNK_MODULE (ModuleNode) * body: statement list of the module * @@ -574,6 +577,7 @@ enum ParseNodeArity macro(CaseClause, CaseClauseType, asCaseClause) \ macro(ClassMethod, ClassMethodType, asClassMethod) \ macro(ClassField, ClassFieldType, asClassField) \ + macro(StaticClassBlock, StaticClassBlockType, asStaticClassBlock) \ macro(ClassNames, ClassNamesType, asClassNames) \ macro(ForNode, ForNodeType, asFor) \ macro(PropertyAccess, PropertyAccessType, asPropertyAccess) \ @@ -627,6 +631,8 @@ enum class FunctionSyntaxKind Arrow, Method, // Method of a class or object. FieldInitializer, // Field initializers desugar to methods. + StaticClassBlock, // Mostly static class blocks act similar to field initializers, however, + // there is some difference in static semantics. ClassConstructor, DerivedClassConstructor, Getter, @@ -2161,6 +2167,28 @@ class ClassField : public BinaryNode } }; +// Hold onto the function generated for a class static block like +// +// class A { +// static { /* this static block */ } +// } +// +class StaticClassBlock : public UnaryNode +{ + public: + explicit StaticClassBlock(FunctionNode* function) + : UnaryNode(PNK_STATICCLASSBLOCK, JSOP_NOP, function->pn_pos, function) { + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(PNK_STATICCLASSBLOCK); + MOZ_ASSERT_IF(match, node.is<UnaryNode>()); + return match; + } + FunctionNode* function() const { return &kid()->as<FunctionNode>(); } +}; + + class SwitchStatement : public BinaryNode { public: diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index f69e3cf08a..7c8df7d498 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -463,6 +463,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac hasParameterExprs(false), hasDirectEvalInParameterExpr(false), hasDuplicateParameters(false), + allowReturn_(true), useAsm(false), insideUseAsm(false), isAnnexB(false), @@ -526,27 +527,34 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt allowNewTarget_ = true; allowSuperProperty_ = fun->allowSuperProperty(); - if (kind == FunctionSyntaxKind::FieldInitializer) { - setFieldInitializer(); - allowArguments_ = false; - } + if (isGenexpLambda) + thisBinding_ = sc->thisBinding(); + else + thisBinding_ = ThisBinding::Function; if (IsConstructorKind(kind)) { auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>(); MOZ_ASSERT(stmt); stmt->constructorBox = this; + } - if (kind == FunctionSyntaxKind::DerivedClassConstructor) { - setDerivedClassConstructor(); - allowSuperCall_ = true; - needsThisTDZChecks_ = true; - } + if (kind == FunctionSyntaxKind::DerivedClassConstructor) { + setDerivedClassConstructor(); + allowSuperCall_ = true; + needsThisTDZChecks_ = true; } - if (isGenexpLambda) - thisBinding_ = sc->thisBinding(); - else - thisBinding_ = ThisBinding::Function; + if (kind == FunctionSyntaxKind::FieldInitializer || + kind == FunctionSyntaxKind::StaticClassBlock) { + allowArguments_ = false; + if (kind == FunctionSyntaxKind::StaticClassBlock) { + allowSuperCall_ = false; + allowReturn_ = false; + } else { + MOZ_ASSERT(kind == FunctionSyntaxKind::FieldInitializer); + setFieldInitializer(); + } + } } if (sc->inWith()) { @@ -2809,6 +2817,7 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, break; case FunctionSyntaxKind::Method: case FunctionSyntaxKind::FieldInitializer: + case FunctionSyntaxKind::StaticClassBlock: MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator); flags = (generatorKind == NotGenerator && asyncKind == SyncFunction ? JSFunction::INTERPRETED_METHOD @@ -3686,10 +3695,12 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, // See below for an explanation why arrow function parameters and arrow // function bodies are parsed with different yield/await settings. { - AwaitHandling awaitHandling = funbox->isAsync() || - (kind == FunctionSyntaxKind::Arrow && awaitIsKeyword()) - ? AwaitIsKeyword - : AwaitIsName; + AwaitHandling awaitHandling = kind == FunctionSyntaxKind::StaticClassBlock + ? AwaitIsDisallowed + : (funbox->isAsync() || + (kind == FunctionSyntaxKind::Arrow && awaitIsKeyword())) + ? AwaitIsKeyword + : AwaitIsName; AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, awaitHandling); if (!functionArguments(yieldHandling, kind, funNode)) return false; @@ -7438,12 +7449,26 @@ Parser<ParseHandler>::classMember(YieldHandling yieldHandling, if (tt == TOK_STATIC) { if (!tokenStream.peekToken(&tt)) return false; + if (tt == TOK_RC) { tokenStream.consumeKnownToken(tt); error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return false; } + if (tt == TOK_LC) { + /* Parsing static class block: static { ... } */ + FunctionNodeType staticBlockBody = staticClassBlock(classFields); + if (!staticBlockBody) + return false; + + StaticClassBlockType classBlock = handler.newStaticClassBlock(staticBlockBody); + if (!classBlock) + return false; + + return handler.addClassMemberDefinition(classMembers, classBlock); + } + if (tt != TOK_LP) { isStatic = true; } else { @@ -7970,6 +7995,108 @@ Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t class template <class ParseHandler> typename ParseHandler::FunctionNodeType +Parser<ParseHandler>::staticClassBlock(ClassFields& classFields) +{ + // Both for getting-this-done, and because this will invariably be executed, + // syntax parsing should be aborted. + if (!abortIfSyntaxParser()) + return null(); + + TokenPos firstTokenPos(pos()); + + // Create the anonymous function object. + FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::StaticClassBlock; + AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, AwaitIsDisallowed); + + RootedFunction fun(context, + newFunction(nullptr, syntaxKind, + GeneratorKind::NotGenerator, + FunctionAsyncKind::SyncFunction)); + if (!fun) + return null(); + + // Create the function node for the static class body. + FunctionNodeType funNode = handler.newFunction(syntaxKind, firstTokenPos); + if (!funNode) + return null(); + + // Create the FunctionBox and link it to the function object. + Directives directives(true); + FunctionBox* funbox = newFunctionBox(funNode, fun, firstTokenPos.begin, directives, + GeneratorKind::NotGenerator, + FunctionAsyncKind::SyncFunction, false); + if (!funbox) + return null(); + funbox->initWithEnclosingParseContext(pc, syntaxKind); + MOZ_ASSERT(!funbox->allowSuperCall()); + MOZ_ASSERT(!funbox->allowArguments()); + MOZ_ASSERT(!funbox->allowReturn()); + + // Set start at `static` token. + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_STATIC)); + funbox->setStart(tokenStream, firstTokenPos); + + // Push a SourceParseContext on to the stack. + ParseContext* outerpc = pc; + ParseContext funpc(this, funbox, /* newDirectives = */ nullptr); + if (!funpc.init()) + return null(); + + pc->functionScope().useAsVarScope(pc); + + uint32_t start = firstTokenPos.begin; + + tokenStream.consumeKnownToken(TOK_LC); + + // Static class blocks are code-generated as if they were static field + // initializers, so we bump the staticFields count here, which ensures + // .staticInitializers is noted as used. + classFields.staticFields++; + + LexicalScopeNodeType body = functionBody(InAllowed, YieldIsKeyword, syntaxKind, + StatementListBody); + if (!body) + return null(); + + if (tokenStream.isEOF()) { + error(JSMSG_UNTERMINATED_STATIC_CLASS_BLOCK); + return null(); + } + + tokenStream.consumeKnownToken(TOK_RC, TokenStream::Operand); + + TokenPos wholeBodyPos(start, pos().end); + + handler.setEndPosition(funNode, wholeBodyPos.end); + funbox->setEnd(pos().end); + + // Create a ListNode for the parameters + body (there are no parameters). + ListNodeType argsbody = handler.newList(PNK_PARAMSBODY, wholeBodyPos); + if (!argsbody) + return null(); + + handler.setFunctionFormalParametersAndBody(funNode, argsbody); + funbox->function()->setArgCount(0); + + if (pc->superScopeNeedsHomeObject()) { + funbox->setNeedsHomeObject(); + } + + handler.setEndPosition(body, pos().begin); + handler.setEndPosition(funNode, pos().end); + handler.setFunctionBody(funNode, body); + + if (!finishFunction()) + return null(); + + if (!leaveInnerFunction(outerpc)) + return null(); + + return funNode; +} + +template <class ParseHandler> +typename ParseHandler::FunctionNodeType Parser<ParseHandler>::fieldInitializerOpt(HandleAtom propAtom, ClassFields& classFields, bool isStatic) { bool hasInitializer = false; @@ -8349,7 +8476,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) // The Return parameter is only used here, and the effect is easily // detected this way, so don't bother passing around an extra parameter // everywhere. - if (!pc->isFunctionBox()) { + if (!pc->allowReturn()) { error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str); return null(); } @@ -8535,7 +8662,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, // The Return parameter is only used here, and the effect is easily // detected this way, so don't bother passing around an extra parameter // everywhere. - if (!pc->isFunctionBox()) { + if (!pc->allowReturn()) { error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str); return null(); } @@ -10261,7 +10388,7 @@ Parser<ParseHandler>::checkLabelOrIdentifierReference(PropertyName* ident, return true; } if (tt == TOK_AWAIT) { - if (awaitIsKeyword()) { + if (awaitIsKeyword() || awaitIsDisallowed()) { errorAt(offset, JSMSG_RESERVED_ID, "await"); return false; } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index d0318e405d..6976487c50 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -526,6 +526,10 @@ class ParseContext : public Nestable<ParseContext> return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isMethod(); } + bool allowReturn() const { + return sc_->isFunctionBox() && sc_->asFunctionBox()->allowReturn(); + } + uint32_t scriptId() const { return scriptId_; } @@ -588,11 +592,11 @@ enum class PropertyType { }; // Specify a value for an ES6 grammar parametrization. We have no enum for -// [Return] because its behavior is exactly equivalent to checking whether +// [Return] because its behavior is almost exactly equivalent to checking whether // we're in a function box -- easier and simpler than passing an extra // parameter everywhere. enum YieldHandling { YieldIsName, YieldIsKeyword }; -enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword }; +enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword, AwaitIsDisallowed }; enum InHandling { InAllowed, InProhibited }; enum DefaultHandling { NameRequired, AllowDefaultName }; enum TripledotHandling { TripledotAllowed, TripledotProhibited }; @@ -817,7 +821,10 @@ class ParserBase : public StrictModeGetter public: bool awaitIsKeyword() const { - return awaitHandling_ != AwaitIsName; + return awaitHandling_ == AwaitIsKeyword || awaitHandling_ == AwaitIsModuleKeyword; + } + bool awaitIsDisallowed() const { + return awaitHandling_ == AwaitIsDisallowed; } ParseGoal parseGoal() const { @@ -1450,6 +1457,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) // Parse a function body. Pass StatementListBody if the body is a list of // statements; pass ExpressionBody if the body is a single expression. + // + // Don't include opening LeftCurly token when invoking. enum FunctionBodyType { StatementListBody, ExpressionBody }; LexicalScopeNodeType functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, FunctionBodyType type); @@ -1498,6 +1507,9 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) // The number of static class fields. size_t staticFields = 0; + // The number of static blocks + size_t staticBlocks = 0; + // The number of static class fields with computed property names. size_t staticFieldKeys = 0; }; @@ -1514,6 +1526,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) const ClassFields& classFields, ListNodeType& classMembers); FunctionNodeType fieldInitializerOpt(HandleAtom atom, ClassFields& classFields, bool isStatic); + FunctionNodeType staticClassBlock(ClassFields& classFields); FunctionNodeType synthesizeConstructor(HandleAtom className, uint32_t classNameOffset, bool hasHeritage); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 4ac2da6fcd..29a8cbd18f 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -432,6 +432,8 @@ class FunctionBox : public ObjectBox, public SharedContext bool isExprBody_:1; /* arrow function with expression * body or expression closure: * function(x) x*x */ + bool allowReturn_ : 1; /* Used to issue an early error in static class blocks. */ + FunctionContextFlags funCxFlags; @@ -523,6 +525,8 @@ class FunctionBox : public ObjectBox, public SharedContext isExprBody_ = true; } + bool allowReturn() const { return allowReturn_; } + void setGeneratorKind(GeneratorKind kind) { // A generator kind can be set at initialization, or when "yield" is // first seen. In both cases the transition can only happen from diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index b274ac642b..130a5da61d 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -333,6 +333,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node name, FunctionNodeType funNode, JSOp op) { return true; } MOZ_MUST_USE Node newClassMethodDefinition(Node key, FunctionNodeType funNode, JSOp op, bool isStatic) { return NodeGeneric; } MOZ_MUST_USE Node newClassFieldDefinition(Node name, FunctionNodeType initializer, bool isStatic) { return NodeGeneric; } + MOZ_MUST_USE Node newStaticClassBlock(FunctionNodeType block) { return NodeGeneric; } MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList, Node member) { return true; } UnaryNodeType newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; } UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; } diff --git a/js/src/js.msg b/js/src/js.msg index 2cae3ff125..91637edc6a 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -347,6 +347,7 @@ MSG_DEF(JSMSG_UNNAMED_CLASS_STMT, 0, JSEXN_SYNTAXERR, "class statement requ MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 0, JSEXN_SYNTAXERR, "function statement requires a name") MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 0, JSEXN_SYNTAXERR, "unterminated comment") MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal") +MSG_DEF(JSMSG_UNTERMINATED_STATIC_CLASS_BLOCK, 0, JSEXN_SYNTAXERR, "unterminated static class block") MSG_DEF(JSMSG_UNTERMINATED_STRING, 0, JSEXN_SYNTAXERR, "unterminated string literal") MSG_DEF(JSMSG_USELESS_EXPR, 0, JSEXN_TYPEERR, "useless expression") MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 0, JSEXN_SYNTAXERR, "\"use asm\" is only meaningful in the Directive Prologue of a function body") diff --git a/js/src/jsast.tbl b/js/src/jsast.tbl index ba6b60b68c..0ef51622b1 100644 --- a/js/src/jsast.tbl +++ b/js/src/jsast.tbl @@ -87,4 +87,5 @@ ASTDEF(AST_COMPUTED_NAME, "ComputedName", "computedNam ASTDEF(AST_CLASS_STMT, "ClassStatement", "classStatement") ASTDEF(AST_CLASS_METHOD, "ClassMethod", "classMethod") ASTDEF(AST_CLASS_FIELD, "ClassField", "classField") +ASTDEF(AST_STATIC_CLASS_BLOCK, "StaticClassBlock", "staticClassBlock") /* AST_LIMIT = last + 1 */ |