зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1530832 - Refactor classDefinition and replace Maybe::reset() with a block. r=jorendorff
Differential Revision: https://phabricator.services.mozilla.com/D21270 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
981bfc4fb9
Коммит
6efac32ac8
|
@ -6734,6 +6734,222 @@ static AccessorType ToAccessorType(PropertyType propType) {
|
|||
}
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename Unit>
|
||||
bool GeneralParser<ParseHandler, Unit>::classMember(
|
||||
YieldHandling yieldHandling, DefaultHandling defaultHandling,
|
||||
const ParseContext::ClassStatement& classStmt, HandlePropertyName className,
|
||||
uint32_t classStartOffset, bool hasHeritage,
|
||||
size_t& numFieldsWithInitializers, ListNodeType& classMembers, bool* done) {
|
||||
*done = false;
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt)) {
|
||||
return false;
|
||||
}
|
||||
if (tt == TokenKind::RightCurly) {
|
||||
*done = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tt == TokenKind::Semi) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isStatic = false;
|
||||
if (tt == TokenKind::Static) {
|
||||
if (!tokenStream.peekToken(&tt)) {
|
||||
return false;
|
||||
}
|
||||
if (tt == TokenKind::RightCurly) {
|
||||
tokenStream.consumeKnownToken(tt);
|
||||
error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tt != TokenKind::LeftParen) {
|
||||
isStatic = true;
|
||||
} else {
|
||||
anyChars.ungetToken();
|
||||
}
|
||||
} else {
|
||||
anyChars.ungetToken();
|
||||
}
|
||||
|
||||
uint32_t propNameOffset;
|
||||
if (!tokenStream.peekOffset(&propNameOffset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedAtom propAtom(cx_);
|
||||
PropertyType propType;
|
||||
Node propName = propertyName(yieldHandling, PropertyNameInClass,
|
||||
/* maybeDecl = */ Nothing(), classMembers,
|
||||
&propType, &propAtom);
|
||||
if (!propName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (propType == PropertyType::Field) {
|
||||
// TODO(khyperia): Delete the two lines below once fields are fully
|
||||
// supported in the backend. We can't fail in BytecodeCompiler because of
|
||||
// lazy parsing.
|
||||
errorAt(propNameOffset, JSMSG_FIELDS_NOT_SUPPORTED);
|
||||
return false;
|
||||
|
||||
if (isStatic) {
|
||||
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
|
||||
return false;
|
||||
}
|
||||
if (!tokenStream.getToken(&tt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionNodeType initializer = null();
|
||||
if (tt == TokenKind::Assign) {
|
||||
initializer = fieldInitializer(yieldHandling, propAtom);
|
||||
if (!initializer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
numFieldsWithInitializers++;
|
||||
if (!tokenStream.getToken(&tt)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(khyperia): Implement ASI
|
||||
if (tt != TokenKind::Semi) {
|
||||
error(JSMSG_MISSING_SEMI_FIELD);
|
||||
return false;
|
||||
}
|
||||
|
||||
return handler_.addClassFieldDefinition(classMembers, propName,
|
||||
initializer);
|
||||
}
|
||||
|
||||
if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
|
||||
propType != PropertyType::Method &&
|
||||
propType != PropertyType::GeneratorMethod &&
|
||||
propType != PropertyType::AsyncMethod &&
|
||||
propType != PropertyType::AsyncGeneratorMethod) {
|
||||
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isConstructor = !isStatic && propAtom == cx_->names().constructor;
|
||||
if (isConstructor) {
|
||||
if (propType != PropertyType::Method) {
|
||||
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
|
||||
return false;
|
||||
}
|
||||
if (classStmt.constructorBox) {
|
||||
errorAt(propNameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor");
|
||||
return false;
|
||||
}
|
||||
propType = hasHeritage ? PropertyType::DerivedConstructor
|
||||
: PropertyType::Constructor;
|
||||
} else if (isStatic && propAtom == cx_->names().prototype) {
|
||||
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedAtom funName(cx_);
|
||||
switch (propType) {
|
||||
case PropertyType::Getter:
|
||||
case PropertyType::Setter:
|
||||
if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) {
|
||||
funName = prefixAccessorName(propType, propAtom);
|
||||
if (!funName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PropertyType::Constructor:
|
||||
case PropertyType::DerivedConstructor:
|
||||
funName = className;
|
||||
break;
|
||||
default:
|
||||
if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) {
|
||||
funName = propAtom;
|
||||
}
|
||||
}
|
||||
|
||||
// Calling toString on constructors need to return the source text for
|
||||
// the entire class. The end offset is unknown at this point in
|
||||
// parsing and will be amended when class parsing finishes below.
|
||||
FunctionNodeType funNode = methodDefinition(
|
||||
isConstructor ? classStartOffset : propNameOffset, propType, funName);
|
||||
if (!funNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AccessorType atype = ToAccessorType(propType);
|
||||
return handler_.addClassMethodDefinition(classMembers, propName, funNode,
|
||||
atype, isStatic);
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename Unit>
|
||||
bool GeneralParser<ParseHandler, Unit>::finishClassConstructor(
|
||||
const ParseContext::ClassStatement& classStmt, HandlePropertyName className,
|
||||
uint32_t classStartOffset, uint32_t classEndOffset,
|
||||
size_t numFieldsWithInitializers, ListNodeType& classMembers) {
|
||||
// Fields cannot re-use the constructor obtained via JSOP_CLASSCONSTRUCTOR or
|
||||
// JSOP_DERIVEDCONSTRUCTOR due to needing to emit calls to the field
|
||||
// initializers in the constructor. So, synthesize a new one.
|
||||
if (classStmt.constructorBox == nullptr && numFieldsWithInitializers > 0) {
|
||||
// synthesizeConstructor assigns to classStmt.constructorBox
|
||||
FunctionNodeType synthesizedCtor =
|
||||
synthesizeConstructor(className, classStartOffset);
|
||||
if (!synthesizedCtor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(classStmt.constructorBox != nullptr);
|
||||
|
||||
// Note: the *function* has the name of the class, but the *property*
|
||||
// containing the function has the name "constructor"
|
||||
Node constructorNameNode =
|
||||
handler_.newObjectLiteralPropertyName(cx_->names().constructor, pos());
|
||||
if (!constructorNameNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handler_.addClassMethodDefinition(classMembers, constructorNameNode,
|
||||
synthesizedCtor, AccessorType::None,
|
||||
/* isStatic = */ false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (FunctionBox* ctorbox = classStmt.constructorBox) {
|
||||
// Amend the toStringEnd offset for the constructor now that we've
|
||||
// finished parsing the class.
|
||||
ctorbox->toStringEnd = classEndOffset;
|
||||
|
||||
if (numFieldsWithInitializers > 0) {
|
||||
// Field initialization need access to `this`.
|
||||
ctorbox->setHasThisBinding();
|
||||
}
|
||||
|
||||
// Set the same information, but on the lazyScript.
|
||||
if (ctorbox->function()->isInterpretedLazy()) {
|
||||
ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset);
|
||||
|
||||
if (numFieldsWithInitializers > 0) {
|
||||
ctorbox->function()->lazyScript()->setHasThisBinding();
|
||||
}
|
||||
|
||||
// Field initializers can be retrieved if the class and constructor are
|
||||
// being compiled at the same time, but we need to stash the field
|
||||
// information if the constructor is being compiled lazily.
|
||||
FieldInitializers fieldInfo(numFieldsWithInitializers);
|
||||
ctorbox->function()->lazyScript()->setFieldInitializers(fieldInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename Unit>
|
||||
typename ParseHandler::ClassNodeType
|
||||
GeneralParser<ParseHandler, Unit>::classDefinition(
|
||||
|
@ -6769,284 +6985,93 @@ GeneralParser<ParseHandler, Unit>::classDefinition(
|
|||
anyChars.ungetToken();
|
||||
}
|
||||
|
||||
// Because the binding definitions keep track of their blockId, we need to
|
||||
// create at least the inner binding later. Keep track of the name's
|
||||
// position in order to provide it for the nodes created later.
|
||||
TokenPos namePos = pos();
|
||||
|
||||
// Push a ParseContext::ClassStatement to keep track of the constructor
|
||||
// funbox.
|
||||
ParseContext::ClassStatement classStmt(pc_);
|
||||
|
||||
RootedAtom propAtom(cx_);
|
||||
|
||||
// A named class creates a new lexical scope with a const binding of the
|
||||
// class name for the "inner name".
|
||||
Maybe<ParseContext::Statement> innerScopeStmt;
|
||||
Maybe<ParseContext::Scope> innerScope;
|
||||
if (className) {
|
||||
innerScopeStmt.emplace(pc_, StatementKind::Block);
|
||||
innerScope.emplace(this);
|
||||
if (!innerScope->init(pc_)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
// Because the binding definitions keep track of their blockId, we need to
|
||||
// create at least the inner binding later. Keep track of the name's position
|
||||
// in order to provide it for the nodes created later.
|
||||
TokenPos namePos = pos();
|
||||
|
||||
NameNodeType innerName;
|
||||
Node nameNode = null();
|
||||
Node classHeritage = null();
|
||||
bool hasHeritage;
|
||||
if (!tokenStream.matchToken(&hasHeritage, TokenKind::Extends)) {
|
||||
return null();
|
||||
}
|
||||
if (hasHeritage) {
|
||||
if (!tokenStream.getToken(&tt)) {
|
||||
return null();
|
||||
}
|
||||
classHeritage = memberExpr(yieldHandling, TripledotProhibited, tt);
|
||||
if (!classHeritage) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CLASS)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
ListNodeType classMembers = handler_.newClassMemberList(pos().begin);
|
||||
if (!classMembers) {
|
||||
return null();
|
||||
}
|
||||
|
||||
Maybe<DeclarationKind> declKind = Nothing();
|
||||
size_t numFieldsWithInitializers = 0;
|
||||
for (;;) {
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt)) {
|
||||
return null();
|
||||
}
|
||||
if (tt == TokenKind::RightCurly) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tt == TokenKind::Semi) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isStatic = false;
|
||||
if (tt == TokenKind::Static) {
|
||||
if (!tokenStream.peekToken(&tt)) {
|
||||
return null();
|
||||
}
|
||||
if (tt == TokenKind::RightCurly) {
|
||||
tokenStream.consumeKnownToken(tt);
|
||||
error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
|
||||
return null();
|
||||
}
|
||||
|
||||
if (tt != TokenKind::LeftParen) {
|
||||
isStatic = true;
|
||||
} else {
|
||||
anyChars.ungetToken();
|
||||
}
|
||||
} else {
|
||||
anyChars.ungetToken();
|
||||
}
|
||||
|
||||
uint32_t propNameOffset;
|
||||
if (!tokenStream.peekOffset(&propNameOffset)) {
|
||||
Node classBlock = null();
|
||||
uint32_t classEndOffset;
|
||||
{
|
||||
// A named class creates a new lexical scope with a const binding of the
|
||||
// class name for the "inner name".
|
||||
ParseContext::Statement innerScopeStmt(pc_, StatementKind::Block);
|
||||
ParseContext::Scope innerScope(this);
|
||||
if (!innerScope.init(pc_)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
PropertyType propType;
|
||||
Node propName = propertyName(yieldHandling, PropertyNameInClass, declKind,
|
||||
classMembers, &propType, &propAtom);
|
||||
if (!propName) {
|
||||
bool hasHeritage;
|
||||
if (!tokenStream.matchToken(&hasHeritage, TokenKind::Extends)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
if (propType == PropertyType::Field) {
|
||||
// TODO(khyperia): Delete the two lines below once fields are fully
|
||||
// supported in the backend. We can't fail in BytecodeCompiler because of
|
||||
// lazy parsing.
|
||||
errorAt(propNameOffset, JSMSG_FIELDS_NOT_SUPPORTED);
|
||||
return null();
|
||||
|
||||
if (isStatic) {
|
||||
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
|
||||
return null();
|
||||
}
|
||||
if (hasHeritage) {
|
||||
if (!tokenStream.getToken(&tt)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
FunctionNodeType initializer = null();
|
||||
if (tt == TokenKind::Assign) {
|
||||
initializer = fieldInitializer(yieldHandling, propAtom);
|
||||
if (!initializer) {
|
||||
return null();
|
||||
}
|
||||
|
||||
numFieldsWithInitializers++;
|
||||
if (!tokenStream.getToken(&tt)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(khyperia): Implement ASI
|
||||
if (tt != TokenKind::Semi) {
|
||||
error(JSMSG_MISSING_SEMI_FIELD);
|
||||
classHeritage = memberExpr(yieldHandling, TripledotProhibited, tt);
|
||||
if (!classHeritage) {
|
||||
return null();
|
||||
}
|
||||
|
||||
if (!handler_.addClassFieldDefinition(classMembers, propName,
|
||||
initializer)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
|
||||
propType != PropertyType::Method &&
|
||||
propType != PropertyType::GeneratorMethod &&
|
||||
propType != PropertyType::AsyncMethod &&
|
||||
propType != PropertyType::AsyncGeneratorMethod) {
|
||||
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
|
||||
if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CLASS)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
bool isConstructor = !isStatic && propAtom == cx_->names().constructor;
|
||||
if (isConstructor) {
|
||||
if (propType != PropertyType::Method) {
|
||||
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
|
||||
return null();
|
||||
}
|
||||
if (classStmt.constructorBox) {
|
||||
errorAt(propNameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor");
|
||||
return null();
|
||||
}
|
||||
propType = hasHeritage ? PropertyType::DerivedConstructor
|
||||
: PropertyType::Constructor;
|
||||
} else if (isStatic && propAtom == cx_->names().prototype) {
|
||||
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
|
||||
ListNodeType classMembers = handler_.newClassMemberList(pos().begin);
|
||||
if (!classMembers) {
|
||||
return null();
|
||||
}
|
||||
|
||||
RootedAtom funName(cx_);
|
||||
switch (propType) {
|
||||
case PropertyType::Getter:
|
||||
case PropertyType::Setter:
|
||||
if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) {
|
||||
funName = prefixAccessorName(propType, propAtom);
|
||||
if (!funName) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
size_t numFieldsWithInitializers = 0;
|
||||
for (;;) {
|
||||
bool done;
|
||||
if (!classMember(yieldHandling, defaultHandling, classStmt, className,
|
||||
classStartOffset, hasHeritage, numFieldsWithInitializers,
|
||||
classMembers, &done)) {
|
||||
return null();
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
case PropertyType::Constructor:
|
||||
case PropertyType::DerivedConstructor:
|
||||
funName = className;
|
||||
break;
|
||||
default:
|
||||
if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) {
|
||||
funName = propAtom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calling toString on constructors need to return the source text for
|
||||
// the entire class. The end offset is unknown at this point in
|
||||
// parsing and will be amended when class parsing finishes below.
|
||||
FunctionNodeType funNode = methodDefinition(
|
||||
isConstructor ? classStartOffset : propNameOffset, propType, funName);
|
||||
if (!funNode) {
|
||||
classEndOffset = pos().end;
|
||||
if (!finishClassConstructor(classStmt, className, classStartOffset,
|
||||
classEndOffset, numFieldsWithInitializers,
|
||||
classMembers)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
AccessorType atype = ToAccessorType(propType);
|
||||
if (!handler_.addClassMethodDefinition(classMembers, propName, funNode,
|
||||
atype, isStatic)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
// Fields cannot re-use the constructor obtained via JSOP_CLASSCONSTRUCTOR or
|
||||
// JSOP_DERIVEDCONSTRUCTOR due to needing to emit calls to the field
|
||||
// initializers in the constructor. So, synthesize a new one.
|
||||
if (classStmt.constructorBox == nullptr && numFieldsWithInitializers > 0) {
|
||||
// synthesizeConstructor assigns to classStmt.constructorBox
|
||||
FunctionNodeType synthesizedCtor =
|
||||
synthesizeConstructor(className, classStartOffset);
|
||||
if (!synthesizedCtor) {
|
||||
return null();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(classStmt.constructorBox != nullptr);
|
||||
|
||||
// Note: the *function* has the name of the class, but the *property*
|
||||
// containing the function has the name "constructor"
|
||||
Node constructorNameNode =
|
||||
handler_.newObjectLiteralPropertyName(cx_->names().constructor, pos());
|
||||
if (!constructorNameNode) {
|
||||
return null();
|
||||
}
|
||||
|
||||
if (!handler_.addClassMethodDefinition(classMembers, constructorNameNode,
|
||||
synthesizedCtor, AccessorType::None,
|
||||
/* isStatic = */ false)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t classEndOffset = pos().end;
|
||||
if (FunctionBox* ctorbox = classStmt.constructorBox) {
|
||||
// Amend the toStringEnd offset for the constructor now that we've
|
||||
// finished parsing the class.
|
||||
ctorbox->toStringEnd = classEndOffset;
|
||||
|
||||
if (numFieldsWithInitializers > 0) {
|
||||
// Field initialization need access to `this`.
|
||||
ctorbox->setHasThisBinding();
|
||||
}
|
||||
|
||||
// Set the same information, but on the lazyScript.
|
||||
if (ctorbox->function()->isInterpretedLazy()) {
|
||||
ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset);
|
||||
|
||||
if (numFieldsWithInitializers > 0) {
|
||||
ctorbox->function()->lazyScript()->setHasThisBinding();
|
||||
if (className) {
|
||||
// The inner name is immutable.
|
||||
if (!noteDeclaredName(className, DeclarationKind::Const, namePos)) {
|
||||
return null();
|
||||
}
|
||||
|
||||
// Field initializers can be retrieved if the class and constructor are
|
||||
// being compiled at the same time, but we need to stash the field
|
||||
// information if the constructor is being compiled lazily.
|
||||
FieldInitializers fieldInfo(numFieldsWithInitializers);
|
||||
ctorbox->function()->lazyScript()->setFieldInitializers(fieldInfo);
|
||||
}
|
||||
}
|
||||
|
||||
Node nameNode = null();
|
||||
Node membersOrBlock = classMembers;
|
||||
if (className) {
|
||||
// The inner name is immutable.
|
||||
if (!noteDeclaredName(className, DeclarationKind::Const, namePos)) {
|
||||
return null();
|
||||
innerName = newName(className, namePos);
|
||||
if (!innerName) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
NameNodeType innerName = newName(className, namePos);
|
||||
if (!innerName) {
|
||||
return null();
|
||||
}
|
||||
|
||||
Node classBlock = finishLexicalScope(*innerScope, classMembers);
|
||||
classBlock = finishLexicalScope(innerScope, classMembers);
|
||||
if (!classBlock) {
|
||||
return null();
|
||||
}
|
||||
|
||||
membersOrBlock = classBlock;
|
||||
|
||||
// Pop the inner scope.
|
||||
innerScope.reset();
|
||||
innerScopeStmt.reset();
|
||||
}
|
||||
|
||||
if (className) {
|
||||
NameNodeType outerName = null();
|
||||
if (classContext == ClassStatement) {
|
||||
// The outer name is mutable.
|
||||
|
@ -7068,7 +7093,7 @@ GeneralParser<ParseHandler, Unit>::classDefinition(
|
|||
|
||||
MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
|
||||
|
||||
return handler_.newClass(nameNode, classHeritage, membersOrBlock,
|
||||
return handler_.newClass(nameNode, classHeritage, classBlock,
|
||||
TokenPos(classStartOffset, classEndOffset));
|
||||
}
|
||||
|
||||
|
|
|
@ -1303,6 +1303,18 @@ class MOZ_STACK_CLASS GeneralParser : public PerHandlerParser<ParseHandler> {
|
|||
ClassNodeType classDefinition(YieldHandling yieldHandling,
|
||||
ClassContext classContext,
|
||||
DefaultHandling defaultHandling);
|
||||
MOZ_MUST_USE bool classMember(YieldHandling yieldHandling,
|
||||
DefaultHandling defaultHandling,
|
||||
const ParseContext::ClassStatement& classStmt,
|
||||
HandlePropertyName className,
|
||||
uint32_t classStartOffset, bool hasHeritage,
|
||||
size_t& numFieldsWithInitializers,
|
||||
ListNodeType& classMembers, bool* done);
|
||||
MOZ_MUST_USE bool finishClassConstructor(
|
||||
const ParseContext::ClassStatement& classStmt,
|
||||
HandlePropertyName className, uint32_t classStartOffset,
|
||||
uint32_t classEndOffset, size_t numFieldsWithInitializers,
|
||||
ListNodeType& classMembers);
|
||||
|
||||
FunctionNodeType fieldInitializer(YieldHandling yieldHandling,
|
||||
HandleAtom atom);
|
||||
|
|
Загрузка…
Ссылка в новой задаче