Issue #2142 - Implement class static block

Based-on: m-c 1712138/{2,3}, 1713155/2
This commit is contained in:
Martok 2023-04-11 09:48:34 +02:00 committed by roytam1
commit 1fac189aea
12 changed files with 277 additions and 34 deletions

View file

@ -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
@ -1736,6 +1737,18 @@ NodeBuilder::classField(HandleValue name, HandleValue initializer,
dst);
}
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)
{
@ -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>();
@ -2787,6 +2808,21 @@ ASTSerializer::classField(ClassField* classField, MutableHandleValue dst)
builder.classField(key, val, &classField->pn_pos, 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)
{

View file

@ -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);

View file

@ -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:

View file

@ -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)));

View file

@ -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:

View file

@ -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:

View file

@ -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 (isGenexpLambda)
thisBinding_ = sc->thisBinding();
else
thisBinding_ = ThisBinding::Function;
if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
setDerivedClassConstructor();
allowSuperCall_ = true;
needsThisTDZChecks_ = true;
}
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 {
@ -7968,6 +7993,108 @@ Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t class
return funNode;
}
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)
@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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; }

View file

@ -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")

View file

@ -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 */