mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 17:31:47 +09:00
Issue #2142 - Implement class static block
Based-on: m-c 1712138/{2,3}, 1713155/2
This commit is contained in:
parent
22a9d46ef1
commit
1fac189aea
12 changed files with 277 additions and 34 deletions
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue