Issue #2142 - Implement syntax for public/private fields and computed field names

This state still has the initializers scoped on .initializers local variable, which will be changed later.

Based-on: m-c 1499448, 1530084, 1530832, 1529448 (partial), 1532921, 1528039, 1528038, 1535166, 1550628,
              1535166, 1550628, 1541641, 1547133, 1540787, 1535804/9
This commit is contained in:
Martok 2023-04-06 03:10:50 +02:00 committed by roytam1
commit 51db22ff2f
24 changed files with 1383 additions and 290 deletions

View file

@ -539,9 +539,11 @@ class NodeBuilder
MOZ_MUST_USE bool classDefinition(bool expr, HandleValue name, HandleValue heritage,
HandleValue block, TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool classMethods(NodeVector& methods, MutableHandleValue dst);
MOZ_MUST_USE bool classMembers(NodeVector& members, MutableHandleValue dst);
MOZ_MUST_USE bool classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic,
TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool classField(HandleValue name, HandleValue initializer,
TokenPos* pos, MutableHandleValue dst);
/*
* expressions
@ -1721,9 +1723,23 @@ NodeBuilder::classMethod(HandleValue name, HandleValue body, PropKind kind, bool
}
bool
NodeBuilder::classMethods(NodeVector& methods, MutableHandleValue dst)
NodeBuilder::classField(HandleValue name, HandleValue initializer,
TokenPos* pos, MutableHandleValue dst)
{
return newArray(methods, dst);
RootedValue cb(cx, callbacks[AST_CLASS_FIELD]);
if (!cb.isNull())
return callback(cb, name, initializer, pos, dst);
return newNode(AST_CLASS_FIELD, pos,
"name", name,
"init", initializer,
dst);
}
bool
NodeBuilder::classMembers(NodeVector& members, MutableHandleValue dst)
{
return newArray(members, dst);
}
bool
@ -1853,6 +1869,7 @@ class ASTSerializer
bool property(ParseNode* pn, MutableHandleValue dst);
bool classMethod(ClassMethod* classMethod, MutableHandleValue dst);
bool classField(ClassField* classField, MutableHandleValue dst);
bool optIdentifier(HandleAtom atom, TokenPos* pos, MutableHandleValue dst) {
if (!atom) {
@ -2457,7 +2474,7 @@ ASTSerializer::classDefinition(ClassNode* pn, bool expr, MutableHandleValue dst)
}
return optExpression(pn->heritage(), &heritage) &&
statement(pn->methodList(), &classBody) &&
statement(pn->memberList(), &classBody) &&
builder.classDefinition(expr, className, heritage, classBody, &pn->pn_pos, dst);
}
@ -2676,24 +2693,34 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
case PNK_CLASS:
return classDefinition(&pn->as<ClassNode>(), false, dst);
case PNK_CLASSMETHODLIST:
case PNK_CLASSMEMBERLIST:
{
ListNode* methodList = &pn->as<ListNode>();
NodeVector methods(cx);
if (!methods.reserve(methodList->count()))
ListNode* memberList = &pn->as<ListNode>();
NodeVector members(cx);
if (!members.reserve(memberList->count()))
return false;
for (ParseNode* item : methodList->contents()) {
ClassMethod* method = &item->as<ClassMethod>();
MOZ_ASSERT(methodList->pn_pos.encloses(method->pn_pos));
for (ParseNode* item : memberList->contents()) {
if (item->is<ClassField>()) {
ClassField* field = &item->as<ClassField>();
MOZ_ASSERT(memberList->pn_pos.encloses(field->pn_pos));
RootedValue prop(cx);
if (!classMethod(method, &prop))
return false;
methods.infallibleAppend(prop);
RootedValue prop(cx);
if (!classField(field, &prop))
return false;
members.infallibleAppend(prop);
} else {
ClassMethod* method = &item->as<ClassMethod>();
MOZ_ASSERT(memberList->pn_pos.encloses(method->pn_pos));
RootedValue prop(cx);
if (!classMethod(method, &prop))
return false;
members.infallibleAppend(prop);
}
}
return builder.classMethods(methods, dst);
return builder.classMembers(members, dst);
}
case PNK_NOP:
@ -2732,6 +2759,34 @@ ASTSerializer::classMethod(ClassMethod* classMethod, MutableHandleValue dst)
builder.classMethod(key, val, kind, isStatic, &classMethod->pn_pos, dst);
}
bool
ASTSerializer::classField(ClassField* classField, MutableHandleValue dst)
{
RootedValue key(cx), val(cx);
// Dig through the lambda and get to the actual expression
if (classField->initializer()) {
ParseNode* value = classField->initializer()
->body()
->head()->as<LexicalScopeNode>()
.scopeBody()->as<ListNode>()
.head()->as<UnaryNode>()
.kid()->as<AssignmentNode>()
.right();
// RawUndefinedExpr is the node we use for "there is no initializer". If one
// writes, literally, `x = undefined;`, it will not be a RawUndefinedExpr
// node, but rather a variable reference.
// Behavior for "there is no initializer" should be { ..., "init": null }
if (value->getKind() != PNK_RAW_UNDEFINED) {
if (!expression(value, &val))
return false;
} else {
val.setNull();
}
}
return propertyName(&classField->name(), &key) &&
builder.classField(key, val, &classField->pn_pos, dst);
}
bool
ASTSerializer::leftAssociate(ListNode* node, MutableHandleValue dst)
{

View file

@ -110,14 +110,22 @@ CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& opt
bool
IsIdentifier(JSLinearString* str);
bool
IsIdentifierNameOrPrivateName(JSLinearString* str);
/*
* As above, but taking chars + length.
*/
bool
IsIdentifier(const char* chars, size_t length);
IsIdentifier(const Latin1Char* chars, size_t length);
bool
IsIdentifier(const char16_t* chars, size_t length);
static bool
IsIdentifierNameOrPrivateName(const Latin1Char* chars, size_t length);
bool
IsIdentifierNameOrPrivateName(const char16_t* chars, size_t length);
/* True if str is a keyword. Defined in TokenStream.cpp. */
bool
IsKeyword(JSLinearString* str);

View file

@ -174,6 +174,10 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
innermostNestableControl(nullptr),
innermostEmitterScope_(nullptr),
innermostTDZCheckCache(nullptr),
fieldInitializers_(parent
? parent->fieldInitializers_
: lazyScript ? lazyScript->getFieldInitializers()
: FieldInitializers::Invalid()),
#ifdef DEBUG
unstableEmitterScope(false),
#endif
@ -1058,6 +1062,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
return true;
case PNK_OBJECT_PROPERTY_NAME:
case PNK_PRIVATE_NAME: // no side effects, unlike PNK_NAME
case PNK_STRING:
case PNK_TEMPLATE_STRING:
MOZ_ASSERT(pn->is<NameNode>());
@ -1515,8 +1520,9 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
case PNK_FOROF: // by PNK_FOR/PNK_COMPREHENSIONFOR
case PNK_FORHEAD: // by PNK_FOR/PNK_COMPREHENSIONFOR
case PNK_CLASSMETHOD: // by PNK_CLASS
case PNK_CLASSFIELD: // by PNK_CLASS
case PNK_CLASSNAMES: // by PNK_CLASS
case PNK_CLASSMETHODLIST: // by PNK_CLASS
case PNK_CLASSMEMBERLIST: // by PNK_CLASS
case PNK_IMPORT_SPEC_LIST: // by PNK_IMPORT
case PNK_IMPORT_SPEC: // by PNK_IMPORT
case PNK_EXPORT_BATCH_SPEC:// by PNK_EXPORT
@ -2381,6 +2387,64 @@ BytecodeEmitter::emitScript(ParseNode* body)
return true;
}
bool BytecodeEmitter::emitInitializeInstanceFields()
{
MOZ_ASSERT(fieldInitializers_.valid);
size_t numFields = fieldInitializers_.numFieldInitializers;
if (numFields == 0) {
return true;
}
if (!emitGetName(cx->names().dotInitializers)) {
// [stack] ARRAY
return false;
}
for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
if (fieldIndex < numFields - 1) {
// We DUP to keep the array around (it is consumed in the bytecode below)
// for next iterations of this loop, except for the last iteration, which
// avoids an extra POP at the end of the loop.
if (!emit1(JSOP_DUP)) {
// [stack] ARRAY ARRAY
return false;
}
}
if (!emitNumberOp(fieldIndex)) {
// [stack] ARRAY? ARRAY INDEX
return false;
}
// Don't use CALLELEM here, because the receiver of the call != the receiver
// of this getelem. (Specifically, the call receiver is `this`, and the
// receiver of this getelem is `.initializers`)
if (!emit1(JSOP_GETELEM)) {
// [stack] ARRAY? FUNC
return false;
}
// This is guaranteed to run after super(), so we don't need TDZ checks.
if (!emitGetName(cx->names().dotThis)) {
// [stack] ARRAY? FUNC THIS
return false;
}
if (!emitCall(JSOP_CALL_IGNORES_RV, 0)) {
// [stack] ARRAY? RVAL
return false;
}
if (!emit1(JSOP_POP)) {
// [stack] ARRAY?
return false;
}
}
return true;
}
bool
BytecodeEmitter::emitFunctionScript(FunctionNode* funNode)
{
@ -7718,6 +7782,12 @@ bool
BytecodeEmitter::emitPropertyList(ListNode* obj, MutableHandlePlainObject objp, PropListType type)
{
for (ParseNode* propdef : obj->contents()) {
if (propdef->is<ClassField>()) {
// Skip over class fields and emit them at the end. This is needed
// because they're all emitted into a single array, which is then stored
// into a local variable
continue;
}
if (!updateSourceCoordNotes(propdef->pn_pos.begin))
return false;
@ -7877,9 +7947,186 @@ BytecodeEmitter::emitPropertyList(ListNode* obj, MutableHandlePlainObject objp,
return false;
}
}
if (obj->getKind() == PNK_CLASSMEMBERLIST) {
if (!emitCreateFieldKeys(obj))
return false;
if (!emitCreateFieldInitializers(obj))
return false;
}
return true;
}
FieldInitializers
BytecodeEmitter::setupFieldInitializers(ListNode* classMembers)
{
size_t numFields = 0;
for (ParseNode* propdef : classMembers->contents()) {
if (propdef->is<ClassField>()) {
FunctionNode* initializer = propdef->as<ClassField>().initializer();
// Don't include fields without initializers.
if (initializer != nullptr) {
numFields++;
}
continue;
}
}
return FieldInitializers(numFields);
}
// Purpose of .fieldKeys:
// Computed field names (`["x"] = 2;`) must be ran at class-evaluation time, not
// object construction time. The transformation to do so is roughly as follows:
//
// class C {
// [keyExpr] = valueExpr;
// }
// -->
// let .fieldKeys = [keyExpr];
// let .initializers = [
// () => {
// this[.fieldKeys[0]] = valueExpr;
// }
// ];
// class C {
// constructor() {
// .initializers[0]();
// }
// }
//
// BytecodeEmitter::emitCreateFieldKeys does `let .fieldKeys = [keyExpr, ...];`
// See Parser::fieldInitializer for the `this[.fieldKeys[0]]` part.
bool
BytecodeEmitter::emitCreateFieldKeys(ListNode* obj)
{
size_t numFieldKeys = 0;
for (ParseNode* propdef : obj->contents()) {
if (propdef->is<ClassField>()) {
ClassField* field = &propdef->as<ClassField>();
if (field->name().getKind() == PNK_COMPUTED_NAME) {
numFieldKeys++;
}
}
}
if (numFieldKeys == 0)
return true;
NameOpEmitter noe(this, cx->names().dotFieldKeys,
NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs())
return false;
if (!emitUint32Operand(JSOP_NEWARRAY, numFieldKeys)) {
// [stack] ARRAY
return false;
}
size_t curFieldKeyIndex = 0;
for (ParseNode* propdef : obj->contents()) {
if (propdef->is<ClassField>()) {
ClassField* field = &propdef->as<ClassField>();
if (field->name().getKind() == PNK_COMPUTED_NAME) {
ParseNode* nameExpr = field->name().as<UnaryNode>().kid();
if (!emitTree(nameExpr)) {
// [stack] ARRAY KEY
return false;
}
if (!emit1(JSOP_TOID)) {
// [stack] ARRAY KEY
return false;
}
if (!emitUint32Operand(JSOP_INITELEM_ARRAY, curFieldKeyIndex)) {
// [stack] ARRAY
return false;
}
curFieldKeyIndex++;
}
}
}
MOZ_ASSERT(curFieldKeyIndex == numFieldKeys);
if (!noe.emitAssignment()) {
// [stack] ARRAY
return false;
}
if (!emit1(JSOP_POP)) {
// [stack]
return false;
}
return true;
}
bool
BytecodeEmitter::emitCreateFieldInitializers(ListNode* obj)
{
const FieldInitializers& fieldInitializers = fieldInitializers_;
MOZ_ASSERT(fieldInitializers.valid);
size_t numFields = fieldInitializers.numFieldInitializers;
if (numFields == 0)
return true;
// .initializers is a variable that stores an array of lambdas containing
// code (the initializer) for each field. Upon an object's construction,
// these lambdas will be called, defining the values.
NameOpEmitter noe(this, cx->names().dotInitializers,
NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
return false;
}
if (!emitUint32Operand(JSOP_NEWARRAY, numFields)) {
// [stack] CTOR? OBJ ARRAY
return false;
}
size_t curFieldIndex = 0;
for (ParseNode* propdef : obj->contents()) {
if (propdef->is<ClassField>()) {
FunctionNode* initializer = propdef->as<ClassField>().initializer();
if (initializer == nullptr) {
continue;
}
if (!emitTree(initializer)) {
// [stack] CTOR? OBJ ARRAY LAMBDA
return false;
}
if (!emitUint32Operand(JSOP_INITELEM_ARRAY, curFieldIndex)) {
// [stack] CTOR? OBJ ARRAY
return false;
}
curFieldIndex++;
}
}
if (!noe.emitAssignment()) {
// [stack] CTOR? OBJ ARRAY
return false;
}
if (!emit1(JSOP_POP)) {
// [stack] CTOR? OBJ
return false;
}
return true;
}
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
// the comment on emitSwitch.
MOZ_NEVER_INLINE bool
@ -8414,6 +8661,11 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody)
{
FunctionBox* funbox = sc->asFunctionBox();
if (funbox->function()->kind() == JSFunction::FunctionKind::ClassConstructor) {
if (!emitInitializeInstanceFields())
return false;
}
if (!emitTree(funBody))
return false;
@ -8484,6 +8736,21 @@ BytecodeEmitter::emitLexicalInitialization(ParseNode* pn)
return true;
}
class AutoResetFieldInitializers
{
BytecodeEmitter* bce;
FieldInitializers oldFieldInfo;
public:
AutoResetFieldInitializers(BytecodeEmitter* bce, FieldInitializers newFieldInfo)
: bce(bce), oldFieldInfo(bce->fieldInitializers_)
{
bce->fieldInitializers_ = newFieldInfo;
}
~AutoResetFieldInitializers() { bce->fieldInitializers_ = oldFieldInfo; }
};
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
// (BindingClassDeclarationEvaluation).
bool
@ -8491,21 +8758,26 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
{
ClassNames* names = classNode->names();
ParseNode* heritageExpression = classNode->heritage();
ListNode* classMethods = classNode->methodList();
ListNode* classMembers = classNode->memberList();
FunctionNode* constructor = nullptr;
for (ParseNode* mn : classMethods->contents()) {
ClassMethod& method = mn->as<ClassMethod>();
ParseNode& methodName = method.name();
if (!method.isStatic() &&
(methodName.isKind(PNK_OBJECT_PROPERTY_NAME) || methodName.isKind(PNK_STRING)) &&
methodName.as<NameNode>().atom() == cx->names().constructor)
{
constructor = &method.method();
break;
for (ParseNode* mn : classMembers->contents()) {
if (mn->is<ClassMethod>()) {
ClassMethod& method = mn->as<ClassMethod>();
ParseNode& methodName = method.name();
if (!method.isStatic() &&
(methodName.isKind(PNK_OBJECT_PROPERTY_NAME) || methodName.isKind(PNK_STRING)) &&
methodName.as<NameNode>().atom() == cx->names().constructor)
{
constructor = &method.method();
break;
}
}
}
// set this->fieldInitializers_
AutoResetFieldInitializers _innermostClassAutoReset(this, setupFieldInitializers(classMembers));
bool savedStrictness = sc->setLocalStrictMode(true);
Maybe<TDZCheckCache> tdzCache;
@ -8577,7 +8849,7 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
return false;
RootedPlainObject obj(cx);
if (!emitPropertyList(classMethods, &obj, ClassBody))
if (!emitPropertyList(classMembers, &obj, ClassBody))
return false;
if (!emit1(JSOP_POP))

View file

@ -177,6 +177,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
EmitterScope* innermostEmitterScope_;
TDZCheckCache* innermostTDZCheckCache;
/* field info for enclosing class */
FieldInitializers fieldInitializers_;
#ifdef DEBUG
bool unstableEmitterScope;
@ -415,6 +418,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// encompasses the entire source.
MOZ_MUST_USE bool emitScript(ParseNode* body);
MOZ_MUST_USE bool emitInitializeInstanceFields();
// Emit function code for the tree rooted at body.
MOZ_MUST_USE bool emitFunctionScript(FunctionNode* funNode);
@ -514,6 +519,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitPropertyList(ListNode* obj, MutableHandlePlainObject objp,
PropListType type);
FieldInitializers setupFieldInitializers(ListNode* classMembers);
MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj);
MOZ_MUST_USE bool emitCreateFieldInitializers(ListNode* obj);
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
// not used to unconditionally emit JSOP_GETLOCAL. Variable access should
// instead be emitted using EmitVarOp. In special cases, when the caller

View file

@ -377,6 +377,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_OPTELEM:
case PNK_OPTCALL:
case PNK_NAME:
case PNK_PRIVATE_NAME:
case PNK_TEMPLATE_STRING:
case PNK_TEMPLATE_STRING_LIST:
case PNK_TAGGED_TEMPLATE:
@ -400,8 +401,8 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_FORIN:
case PNK_FOROF:
case PNK_FORHEAD:
case PNK_CLASSMETHOD:
case PNK_CLASSMETHODLIST:
case PNK_CLASSFIELD:
case PNK_CLASSMEMBERLIST:
case PNK_CLASSNAMES:
case PNK_NEWTARGET:
case PNK_IMPORT_META:
@ -1679,6 +1680,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
return true;
case PNK_OBJECT_PROPERTY_NAME:
case PNK_PRIVATE_NAME:
case PNK_STRING:
case PNK_TEMPLATE_STRING:
MOZ_ASSERT(pn->is<NameNode>());
@ -1810,7 +1812,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
case PNK_OBJECT:
case PNK_ARRAYCOMP:
case PNK_STATEMENTLIST:
case PNK_CLASSMETHODLIST:
case PNK_CLASSMEMBERLIST:
case PNK_CATCHLIST:
case PNK_TEMPLATE_STRING_LIST:
case PNK_VAR:
@ -1902,6 +1904,16 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
Fold(cx, node->unsafeRightReference(), parser, inGenexpLambda);
}
case PNK_CLASSFIELD: {
ClassField* node = &pn->as<ClassField>();
if (node->initializer()) {
if (!Fold(cx, node->unsafeRightReference(), parser, inGenexpLambda)) {
return false;
}
}
return true;
}
case PNK_NEWTARGET:
case PNK_IMPORT_META:{
#ifdef DEBUG

View file

@ -367,11 +367,11 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return literal;
}
ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) {
return new_<ClassNode>(name, heritage, methodBlock, pos);
ClassNodeType newClass(Node name, Node heritage, Node memberBlock, const TokenPos& pos) {
return new_<ClassNode>(name, heritage, memberBlock, pos);
}
ListNodeType newClassMethodList(uint32_t begin) {
return new_<ListNode>(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1));
ListNodeType newClassMemberList(uint32_t begin) {
return new_<ListNode>(PNK_CLASSMEMBERLIST, TokenPos(begin, begin + 1));
}
ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
return new_<ClassNames>(outer, inner, pos);
@ -457,19 +457,28 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return true;
}
MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key, FunctionNodeType funNode,
MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType memberList, Node key, FunctionNodeType funNode,
JSOp op, bool isStatic)
{
MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST));
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
key->isKind(PNK_STRING) ||
key->isKind(PNK_COMPUTED_NAME));
MOZ_ASSERT(memberList->isKind(PNK_CLASSMEMBERLIST));
MOZ_ASSERT(isUsableAsObjectPropertyName(key));
ClassMethod* classMethod = new_<ClassMethod>(key, funNode, op, isStatic);
if (!classMethod)
return false;
methodList->append(classMethod);
memberList->append(classMethod);
return true;
}
MOZ_MUST_USE bool addClassFieldDefinition(ListNodeType memberList, Node name, FunctionNodeType initializer)
{
MOZ_ASSERT(memberList->isKind(PNK_CLASSMEMBERLIST));
MOZ_ASSERT(isUsableAsObjectPropertyName(name));
ParseNode* classField = new_<ClassField>(name, initializer);
if (!classField)
return false;
memberList->append(classField);
return true;
}
@ -732,8 +741,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
pn->setDirectRHSAnonFunction(true);
}
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind) {
return new_<FunctionNode>(syntaxKind, pos());
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind, const TokenPos& pos) {
return new_<FunctionNode>(syntaxKind, pos);
}
bool setComprehensionLambdaBody(FunctionNodeType funNode, ListNodeType body) {
@ -819,6 +828,13 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return node->isKind(PNK_SUPERBASE);
}
bool isUsableAsObjectPropertyName(ParseNode* node) {
return node->isKind(PNK_NUMBER) ||
node->isKind(PNK_OBJECT_PROPERTY_NAME) ||
node->isKind(PNK_STRING) ||
node->isKind(PNK_COMPUTED_NAME);
}
inline MOZ_MUST_USE bool finishInitializerAssignment(NameNodeType nameNode, Node init);
void setBeginPosition(Node pn, Node oth) {
@ -853,9 +869,9 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return new_<ListNode>(kind, op, pos());
}
ListNodeType newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) {
ListNodeType newList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) {
MOZ_ASSERT(!isDeclarationKind(kind));
return new_<ListNode>(kind, op, TokenPos(begin, begin + 1));
return new_<ListNode>(kind, op, pos);
}
ListNodeType newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {

View file

@ -83,6 +83,7 @@ class NameResolver
}
case PNK_NAME:
case PNK_PRIVATE_NAME:
*foundName = true;
return buf->append(n->as<NameNode>().atom());
@ -136,6 +137,7 @@ class NameResolver
return cur;
switch (cur->getKind()) {
case PNK_PRIVATE_NAME:
case PNK_NAME: return cur; /* found the initialized declaration */
case PNK_THIS: return cur; /* Setting a property of 'this'. */
case PNK_FUNCTION: return nullptr; /* won't find an assignment or declaration */
@ -404,6 +406,7 @@ class NameResolver
break;
case PNK_OBJECT_PROPERTY_NAME:
case PNK_PRIVATE_NAME:
case PNK_STRING:
case PNK_TEMPLATE_STRING:
MOZ_ASSERT(cur->is<NameNode>());
@ -498,6 +501,19 @@ class NameResolver
break;
}
case PNK_CLASSFIELD: {
ClassField* node = &cur->as<ClassField>();
if (!resolve(&node->name(), prefix)) {
return false;
}
if (ParseNode* init = node->initializer()) {
if (!resolve(init, prefix)) {
return false;
}
}
break;
}
case PNK_ELEM: {
PropertyByValue* elem = &cur->as<PropertyByValue>();
if (!elem->isSuper() && !resolve(&elem->expression(), prefix))
@ -643,7 +659,7 @@ class NameResolver
if (!resolve(heritage, prefix))
return false;
}
if (!resolve(classNode->methodList(), prefix))
if (!resolve(classNode->memberList(), prefix))
return false;
break;
}
@ -757,7 +773,7 @@ class NameResolver
}
case PNK_OBJECT:
case PNK_CLASSMETHODLIST:
case PNK_CLASSMEMBERLIST:
for (ParseNode* element : cur->as<ListNode>().contents()) {
if (!resolve(element, prefix))
return false;

View file

@ -370,6 +370,14 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
return PushResult::Recyclable;
}
case PNK_CLASSFIELD: {
BinaryNode* bn = &pn->as<BinaryNode>();
stack->push(bn->left());
if (bn->right())
stack->push(bn->right());
return PushResult::Recyclable;
}
// Ternary nodes with all children non-null.
case PNK_CONDITIONAL: {
TernaryNode* tn = &pn->as<TernaryNode>();
@ -494,7 +502,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
case PNK_IMPORT_SPEC_LIST:
case PNK_EXPORT_SPEC_LIST:
case PNK_PARAMSBODY:
case PNK_CLASSMETHODLIST:
case PNK_CLASSMEMBERLIST:
return PushListNodeChildren(&pn->as<ListNode>(), stack);
// Array comprehension nodes are lists with a single child:
@ -881,6 +889,7 @@ NameNode::dump(int indent)
}
case PNK_NAME:
case PNK_PRIVATE_NAME: // atom() already includes the '#', no need to specially include it.
case PNK_PROPERTYNAME: {
if (!atom()) {
fprintf(stderr, "#<null name>");

View file

@ -49,6 +49,7 @@ class ObjectBox;
F(CALL) \
F(ARGUMENTS) \
F(NAME) \
F(PRIVATE_NAME) \
F(OBJECT_PROPERTY_NAME) \
F(COMPUTED_NAME) \
F(NUMBER) \
@ -116,7 +117,8 @@ class ObjectBox;
F(MUTATEPROTO) \
F(CLASS) \
F(CLASSMETHOD) \
F(CLASSMETHODLIST) \
F(CLASSFIELD) \
F(CLASSMEMBERLIST) \
F(CLASSNAMES) \
F(NEWTARGET) \
F(POSHOLDER) \
@ -251,19 +253,22 @@ IsTypeofKind(ParseNodeKind kind)
* kid1: PNK_CLASSNAMES for class name. can be null for anonymous class.
* kid2: expression after `extends`. null if no expression
* kid3: either of
* * PNK_CLASSMETHODLIST, if anonymous class
* * PNK_LEXICALSCOPE which contains PNK_CLASSMETHODLIST as scopeBody,
* * PNK_CLASSMEMBERLIST, if anonymous class
* * PNK_LEXICALSCOPE which contains PNK_CLASSMEMBERLIST as scopeBody,
* if named class
* PNK_CLASSNAMES (ClassNames)
* left: Name node for outer binding, or null if the class is an expression
* that doesn't create an outer binding
* right: Name node for inner binding
* PNK_CLASSMETHODLIST (ListNode)
* head: list of N PNK_CLASSMETHOD nodes
* PNK_CLASSMEMBERLIST (ListNode)
* head: list of N PNK_CLASSMETHOD or PNK_CLASSFIELD nodes
* count: N >= 0
* PNK_CLASSMETHOD (ClassMethod)
* name: propertyName
* method: methodDefinition
* PNK_CLASSFIELD (ClassField)
* name: fieldName
* initializer: field initializer or null
* PNK_MODULE (ModuleNode)
* body: statement list of the module
*
@ -567,6 +572,7 @@ enum ParseNodeArity
macro(AssignmentNode, AssignmentNodeType, asAssignment) \
macro(CaseClause, CaseClauseType, asCaseClause) \
macro(ClassMethod, ClassMethodType, asClassMethod) \
macro(ClassField, ClassFieldType, asClassField) \
macro(ClassNames, ClassNamesType, asClassNames) \
macro(ForNode, ForNodeType, asFor) \
macro(PropertyAccess, PropertyAccessType, asPropertyAccess) \
@ -778,6 +784,12 @@ class ParseNode
ParseNode* initOrStmt; /* var initializer, argument default,
* or label statement target */
} name;
struct {
private:
friend class ClassField;
ParseNode* name;
ParseNode* initializer; /* field initializer - optional */
} field;
struct {
private:
friend class RegExpLiteral;
@ -1233,7 +1245,7 @@ class ListNode : public ParseNode
MOZ_MUST_USE bool hasNonConstInitializer() const {
MOZ_ASSERT(isKind(PNK_ARRAY) ||
isKind(PNK_OBJECT) ||
isKind(PNK_CLASSMETHODLIST));
isKind(PNK_CLASSMEMBERLIST));
return pn_u.list.xflags & hasNonConstInitializerBit;
}
@ -1250,7 +1262,7 @@ class ListNode : public ParseNode
void setHasNonConstInitializer() {
MOZ_ASSERT(isKind(PNK_ARRAY) ||
isKind(PNK_OBJECT) ||
isKind(PNK_CLASSMETHODLIST));
isKind(PNK_CLASSMEMBERLIST));
pn_u.list.xflags |= hasNonConstInitializerBit;
}
@ -1874,9 +1886,9 @@ class NullLiteral : public NullaryNode
}
};
// This is only used internally, currently just for tagged templates.
// It represents the value 'undefined' (aka `void 0`), like NullLiteral
// represents the value 'null'.
// This is only used internally, currently just for tagged templates and the
// initial value of fields without initializers. It represents the value
// 'undefined' (aka `void 0`), like NullLiteral represents the value 'null'.
class RawUndefinedLiteral : public NullaryNode
{
public:
@ -2124,6 +2136,30 @@ class ClassMethod : public BinaryNode
}
};
class ClassField : public BinaryNode
{
public:
ClassField(ParseNode* name, ParseNode* initializer)
: BinaryNode(PNK_CLASSFIELD, JSOP_NOP,
initializer == nullptr ? name->pn_pos : TokenPos::box(name->pn_pos, initializer->pn_pos),
name, initializer)
{
}
static bool test(const ParseNode& node) {
bool match = node.isKind(PNK_CLASSFIELD);
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
return match;
}
ParseNode& name() const { return *left(); }
FunctionNode* initializer() const {
return right() ? &right()->as<FunctionNode>() : nullptr;
}
};
class SwitchStatement : public BinaryNode
{
public:
@ -2207,13 +2243,13 @@ class ClassNames : public BinaryNode
class ClassNode : public TernaryNode
{
public:
ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock,
ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* membersOrBlock,
const TokenPos& pos)
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock, pos)
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, membersOrBlock, pos)
{
MOZ_ASSERT_IF(names, names->is<ClassNames>());
MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() ||
methodsOrBlock->isKind(PNK_CLASSMETHODLIST));
MOZ_ASSERT(membersOrBlock->is<LexicalScopeNode>() ||
membersOrBlock->isKind(PNK_CLASSMEMBERLIST));
}
static bool test(const ParseNode& node) {
@ -2228,13 +2264,13 @@ class ClassNode : public TernaryNode
ParseNode* heritage() const {
return kid2();
}
ListNode* methodList() const {
ParseNode* methodsOrBlock = kid3();
if (methodsOrBlock->isKind(PNK_CLASSMETHODLIST))
return &methodsOrBlock->as<ListNode>();
ListNode* memberList() const {
ParseNode* membersOrBlock = kid3();
if (membersOrBlock->isKind(PNK_CLASSMEMBERLIST))
return &membersOrBlock->as<ListNode>();
ListNode* list = &methodsOrBlock->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
MOZ_ASSERT(list->isKind(PNK_CLASSMETHODLIST));
ListNode* list = &membersOrBlock->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
MOZ_ASSERT(list->isKind(PNK_CLASSMEMBERLIST));
return list;
}
Handle<LexicalScope::Data*> scopeBindings() const {

View file

@ -514,6 +514,7 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt
allowNewTarget_ = sc->allowNewTarget();
allowSuperProperty_ = sc->allowSuperProperty();
allowSuperCall_ = sc->allowSuperCall();
allowArguments_ = sc->allowArguments();
needsThisTDZChecks_ = sc->needsThisTDZChecks();
thisBinding_ = sc->thisBinding();
} else {
@ -549,6 +550,16 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt
}
}
void
FunctionBox::initFieldInitializer(ParseContext* enclosing, bool hasHeritage)
{
this->initWithEnclosingParseContext(enclosing, FunctionSyntaxKind::Expression);
allowSuperProperty_ = false;
allowSuperCall_ = false;
allowArguments_ = false;
needsThisTDZChecks_ = hasHeritage;
}
void
FunctionBox::initWithEnclosingScope(Scope* enclosingScope)
{
@ -2294,7 +2305,7 @@ Parser<ParseHandler>::hasUsedFunctionSpecialName(HandlePropertyName name)
template <typename ParseHandler>
bool
Parser<ParseHandler>::declareFunctionThis()
Parser<ParseHandler>::declareFunctionThis(bool canSkipLazyClosedOverBindings)
{
// The asm.js validator does all its own symbol-table management so, as an
// optimization, avoid doing any work here.
@ -2307,10 +2318,11 @@ Parser<ParseHandler>::declareFunctionThis()
HandlePropertyName dotThis = context->names().dotThis;
bool declareThis;
if (handler.canSkipLazyClosedOverBindings())
if (canSkipLazyClosedOverBindings)
declareThis = funbox->function()->lazyScript()->hasThisBinding();
else
declareThis = hasUsedFunctionSpecialName(dotThis) || funbox->isDerivedClassConstructor();
declareThis = hasUsedFunctionSpecialName(dotThis) ||
funbox->function()->kind() == JSFunction::FunctionKind::ClassConstructor;
if (declareThis) {
ParseContext::Scope& funScope = pc->functionScope();
@ -2539,7 +2551,7 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
tokenStream.ungetToken();
}
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Statement);
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Statement, pos());
if (!funNode)
return null();
@ -2585,7 +2597,7 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
template <typename ParseHandler>
bool
Parser<ParseHandler>::declareFunctionArgumentsObject()
Parser<ParseHandler>::declareFunctionArgumentsObject(bool canSkipLazyClosedOverBindings)
{
FunctionBox* funbox = pc->functionBox();
ParseContext::Scope& funScope = pc->functionScope();
@ -2597,7 +2609,7 @@ Parser<ParseHandler>::declareFunctionArgumentsObject()
HandlePropertyName argumentsName = context->names().arguments;
bool tryDeclareArguments;
if (handler.canSkipLazyClosedOverBindings())
if (canSkipLazyClosedOverBindings)
tryDeclareArguments = funbox->function()->lazyScript()->shouldDeclareArguments();
else
tryDeclareArguments = hasUsedFunctionSpecialName(argumentsName);
@ -2672,6 +2684,10 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
uint32_t startYieldOffset = pc->lastYieldOffset;
#endif
// One might expect noteUsedName(".initializers") here when parsing a
// constructor. See Parser<ParseHandler>::classDefinition on why
// it's not here.
Node body;
if (type == StatementListBody) {
bool inheritedStrict = pc->sc()->strict();
@ -2756,9 +2772,10 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
// finishing up the scope so these special bindings get marked as closed
// over if necessary. Arrow functions don't have these bindings.
if (kind != FunctionSyntaxKind::Arrow) {
if (!declareFunctionArgumentsObject())
bool canSkipLazyClosedOverBindings = handler.canSkipLazyClosedOverBindings();
if (!declareFunctionArgumentsObject(canSkipLazyClosedOverBindings))
return null();
if (!declareFunctionThis())
if (!declareFunctionThis(canSkipLazyClosedOverBindings))
return null();
}
@ -2769,7 +2786,7 @@ template <typename ParseHandler>
JSFunction*
Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
HandleObject proto)
HandleObject proto /* = nullptr */)
{
MOZ_ASSERT_IF(kind == FunctionSyntaxKind::Statement, atom != nullptr);
@ -3594,7 +3611,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
syntaxKind = FunctionSyntaxKind::Arrow;
}
FunctionNodeType funNode = handler.newFunction(syntaxKind);
FunctionNodeType funNode = handler.newFunction(syntaxKind, pos());
if (!funNode)
return null();
@ -3878,7 +3895,7 @@ Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHa
return null();
}
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Statement);
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Statement, pos());
if (!funNode)
return null();
@ -3918,7 +3935,7 @@ Parser<ParseHandler>::functionExpr(uint32_t toStringStart, InvokedPrediction inv
tokenStream.ungetToken();
}
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Expression);
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Expression, pos());
if (!funNode)
return null();
@ -4442,7 +4459,7 @@ Parser<ParseHandler>::objectBindingPattern(DeclarationKind kind, YieldHandling y
TokenPos namePos = tokenStream.nextToken().pos;
PropertyType propType;
Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
Node propName = propertyName(yieldHandling, PropertyNameInPattern, declKind, literal, &propType, &propAtom);
if (!propName)
return null();
if (propType == PropertyType::Normal) {
@ -7377,6 +7394,224 @@ JSOpFromPropertyType(PropertyType propType)
}
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::classMember(YieldHandling yieldHandling, DefaultHandling defaultHandling,
const ParseContext::ClassStatement& classStmt,
HandlePropertyName className,
uint32_t classStartOffset, bool hasHeritage,
size_t& numFields, size_t& numFieldKeys,
ListNodeType& classMembers, bool* done)
{
*done = false;
TokenKind tt;
if (!tokenStream.getToken(&tt))
return false;
if (tt == TOK_RC) {
*done = true;
return true;
}
if (tt == TOK_SEMI)
return true;
bool isStatic = false;
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_LP) {
isStatic = true;
} else {
tokenStream.ungetToken();
}
} else {
tokenStream.ungetToken();
}
uint32_t propNameOffset;
if (!tokenStream.peekOffset(&propNameOffset))
return false;
RootedAtom propAtom(context);
PropertyType propType;
Node propName = propertyName(yieldHandling, PropertyNameInClass, /* maybeDecl = */ Nothing(),
classMembers, &propType, &propAtom);
if (!propName)
return false;
if (propType == PropertyType::Field) {
if (!options().fieldsEnabledOption) {
errorAt(propNameOffset, JSMSG_FIELDS_NOT_SUPPORTED);
return false;
}
if (isStatic) {
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
return false;
}
if (propAtom == context->names().constructor) {
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
return false;
}
if (!abortIfSyntaxParser())
return false;
numFields++;
FunctionNodeType initializer = fieldInitializerOpt(yieldHandling, hasHeritage, propName,
propAtom, numFieldKeys);
if (!initializer)
return false;
if (!tokenStream.getToken(&tt)) {
return false;
}
// TODO(khyperia): Implement ASI
if (tt != TOK_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 &&
propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor)
{
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
return false;
}
if (propType == PropertyType::Getter)
propType = PropertyType::GetterNoExpressionClosure;
if (propType == PropertyType::Setter)
propType = PropertyType::SetterNoExpressionClosure;
bool isConstructor = !isStatic && propAtom == context->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 == context->names().prototype) {
errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF);
return false;
}
RootedAtom funName(context);
switch (propType) {
case PropertyType::GetterNoExpressionClosure:
case PropertyType::SetterNoExpressionClosure:
if (!tokenStream.isCurrentTokenType(TOK_RB)) {
funName = prefixAccessorName(propType, propAtom);
if (!funName)
return false;
}
break;
case PropertyType::Constructor:
case PropertyType::DerivedConstructor:
funName = className;
break;
default:
if (!tokenStream.isCurrentTokenType(TOK_RB))
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;
handler.checkAndSetIsDirectRHSAnonFunction(funNode);
JSOp op = JSOpFromPropertyType(propType);
return handler.addClassMethodDefinition(classMembers, propName, funNode, op, isStatic);
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::finishClassConstructor(const ParseContext::ClassStatement& classStmt,
HandlePropertyName className, uint32_t classStartOffset,
uint32_t classEndOffset, size_t numFields,
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 && numFields > 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(context->names().constructor, pos());
if (!constructorNameNode) {
return false;
}
if (!handler.addClassMethodDefinition(classMembers, constructorNameNode,
synthesizedCtor, JSOP_INITPROP,
/* 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 (numFields > 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 (numFields > 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(numFields);
ctorbox->function()->lazyScript()->setFieldInitializers(fieldInfo);
}
}
return true;
}
template <typename ParseHandler>
typename ParseHandler::ClassNodeType
Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
@ -7392,14 +7627,14 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!tokenStream.getToken(&tt))
return null();
RootedPropertyName name(context);
RootedPropertyName className(context);
if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name)
className = bindingIdentifier(yieldHandling);
if (!className)
return null();
} else if (classContext == ClassStatement) {
if (defaultHandling == AllowDefaultName) {
name = context->names().starDefaultStar;
className = context->names().starDefaultStar;
tokenStream.ungetToken();
} else {
// Class statements must have a bound name
@ -7411,188 +7646,122 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
tokenStream.ungetToken();
}
// Push a ParseContext::ClassStatement to keep track of the constructor
// funbox.
ParseContext::ClassStatement classStmt(pc);
RootedAtom propAtom(context);
// 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 (name) {
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();
Node classHeritage = null();
bool hasHeritage;
if (!tokenStream.matchToken(&hasHeritage, TOK_EXTENDS))
return null();
if (hasHeritage) {
if (!tokenStream.getToken(&tt))
return null();
classHeritage = optionalExpr(yieldHandling, TripledotProhibited, tt);
if (!classHeritage)
return null();
}
if (!mustMatchToken(TOK_LC, JSMSG_CURLY_BEFORE_CLASS)) {
return null();
}
ListNodeType classMethods = handler.newClassMethodList(pos().begin);
if (!classMethods)
return null();
Maybe<DeclarationKind> declKind = Nothing();
for (;;) {
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_RC)
break;
if (tt == TOK_SEMI)
continue;
bool isStatic = false;
if (tt == TOK_STATIC) {
if (!tokenStream.peekToken(&tt))
return null();
if (tt == TOK_RC) {
tokenStream.consumeKnownToken(tt);
error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
return null();
}
if (tt != TOK_LP) {
isStatic = true;
} else {
tokenStream.ungetToken();
}
} else {
tokenStream.ungetToken();
}
uint32_t nameOffset;
if (!tokenStream.peekOffset(&nameOffset))
return null();
PropertyType propType;
Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom);
if (!propName)
return null();
if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
propType != PropertyType::Method && propType != PropertyType::GeneratorMethod &&
propType != PropertyType::AsyncMethod && propType != PropertyType::AsyncGeneratorMethod &&
propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor)
{
errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
return null();
}
if (propType == PropertyType::Getter)
propType = PropertyType::GetterNoExpressionClosure;
if (propType == PropertyType::Setter)
propType = PropertyType::SetterNoExpressionClosure;
bool isConstructor = !isStatic && propAtom == context->names().constructor;
if (isConstructor) {
if (propType != PropertyType::Method) {
errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
return null();
}
if (classStmt.constructorBox) {
errorAt(nameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor");
return null();
}
propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor;
} else if (isStatic && propAtom == context->names().prototype) {
errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
return null();
}
RootedAtom funName(context);
switch (propType) {
case PropertyType::GetterNoExpressionClosure:
case PropertyType::SetterNoExpressionClosure:
if (!tokenStream.isCurrentTokenType(TOK_RB)) {
funName = prefixAccessorName(propType, propAtom);
if (!funName)
return null();
}
break;
case PropertyType::Constructor:
case PropertyType::DerivedConstructor:
funName = name;
break;
default:
if (!tokenStream.isCurrentTokenType(TOK_RB))
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 : nameOffset,
propType, funName);
if (!funNode)
return null();
handler.checkAndSetIsDirectRHSAnonFunction(funNode);
JSOp op = JSOpFromPropertyType(propType);
if (!handler.addClassMethodDefinition(classMethods, propName, funNode, op, isStatic))
return null();
}
// Amend the toStringEnd offset for the constructor now that we've
// finished parsing the class.
uint32_t classEndOffset = pos().end;
if (FunctionBox* ctorbox = classStmt.constructorBox) {
if (ctorbox->function()->isInterpretedLazy())
ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset);
ctorbox->toStringEnd = classEndOffset;
}
// Push a ParseContext::ClassStatement to keep track of the constructor
// funbox.
ParseContext::ClassStatement classStmt(pc);
NameNodeType innerName;
Node nameNode = null();
Node methodsOrBlock = classMethods;
if (name) {
// The inner name is immutable.
if (!noteDeclaredName(name, DeclarationKind::Const, namePos))
Node classHeritage = null();
LexicalScopeNodeType 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();
NameNodeType innerName = newName(name, namePos);
if (!innerName)
bool hasHeritage;
if (!tokenStream.matchToken(&hasHeritage, TOK_EXTENDS))
return null();
if (hasHeritage) {
if (!tokenStream.getToken(&tt))
return null();
classHeritage = optionalExpr(yieldHandling, TripledotProhibited, tt);
if (!classHeritage)
return null();
}
if (!mustMatchToken(TOK_LC, JSMSG_CURLY_BEFORE_CLASS)) {
return null();
}
ListNodeType classMembers = handler.newClassMemberList(pos().begin);
if (!classMembers)
return null();
LexicalScopeNodeType classBlock = finishLexicalScope(*innerScope, classMethods);
size_t numFields = 0;
size_t numFieldKeys = 0;
for (;;) {
bool done;
if (!classMember(yieldHandling, defaultHandling, classStmt, className,
classStartOffset, hasHeritage, numFields, numFieldKeys,
classMembers, &done))
return null();
if (done)
break;
}
if (numFields > 0) {
// .initializers is always closed over by the constructor when there are
// fields with initializers. However, there's some strange circumstances
// which prevents us from using the normal noteUsedName() system. We
// cannot call noteUsedName(".initializers") when parsing the constructor,
// because .initializers should be marked as used *only if* there are
// fields with initializers. Even if we haven't seen any fields yet,
// there may be fields after the constructor.
// Consider the following class:
//
// class C {
// constructor() {
// // do we noteUsedName(".initializers") here?
// }
// // ... because there might be some fields down here.
// }
//
// So, instead, at the end of class parsing (where we are now), we do some
// tricks to pretend that noteUsedName(".initializers") was called in the
// constructor.
if (!usedNames.markAsAlwaysClosedOver(context, context->names().dotInitializers,
pc->scriptId(),
pc->innermostScope()->id()))
return null();
if (!noteDeclaredName(context->names().dotInitializers,
DeclarationKind::Var, namePos))
return null();
}
if (numFieldKeys > 0) {
if (!noteDeclaredName(context->names().dotFieldKeys, DeclarationKind::Let, namePos))
return null();
}
classEndOffset = pos().end;
if (!finishClassConstructor(classStmt, className, classStartOffset,
classEndOffset, numFields, classMembers))
return null();
if (className) {
// The inner name is immutable.
if (!noteDeclaredName(className, DeclarationKind::Const, namePos))
return null();
innerName = newName(className, namePos);
if (!innerName)
return null();
}
classBlock = finishLexicalScope(innerScope, classMembers);
if (!classBlock)
return null();
methodsOrBlock = classBlock;
// Pop the inner scope.
innerScope.reset();
innerScopeStmt.reset();
}
if (className) {
NameNodeType outerName = null();
if (classContext == ClassStatement) {
// The outer name is mutable.
if (!noteDeclaredName(name, DeclarationKind::Let, namePos))
if (!noteDeclaredName(className, DeclarationKind::Let, namePos))
return null();
outerName = newName(name, namePos);
outerName = newName(className, namePos);
if (!outerName)
return null();
}
@ -7604,10 +7773,264 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
return handler.newClass(nameNode, classHeritage, methodsOrBlock,
return handler.newClass(nameNode, classHeritage, classBlock,
TokenPos(classStartOffset, classEndOffset));
}
template <class ParseHandler>
typename ParseHandler::FunctionNodeType
Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t classNameOffset)
{
FunctionSyntaxKind functionSyntaxKind = FunctionSyntaxKind::ClassConstructor;
// Create the function object.
RootedFunction fun(context, newFunction(className, functionSyntaxKind,
GeneratorKind::NotGenerator,
FunctionAsyncKind::SyncFunction));
if (!fun)
return null();
// Create the top-level field initializer node.
FunctionNodeType funNode = handler.newFunction(functionSyntaxKind, pos());
if (!funNode)
return null();
// Create the FunctionBox and link it to the function object.
Directives directives(true);
FunctionBox* funbox = newFunctionBox(funNode, fun, classNameOffset,
directives, GeneratorKind::NotGenerator,
FunctionAsyncKind::SyncFunction, false);
if (!funbox)
return null();
funbox->initWithEnclosingParseContext(pc, functionSyntaxKind);
handler.setFunctionBox(funNode, funbox);
funbox->setEnd(pos().end);
// Push a ParseContext on to the stack.
ParseContext funpc(this, funbox, /* newDirectives = */ nullptr);
if (!funpc.init())
return null();
TokenPos synthesizedBodyPos = TokenPos(classNameOffset, classNameOffset + 1);
// Create a ListNode for the parameters + body (there are no parameters).
ListNodeType argsbody = handler.newList(PNK_PARAMSBODY, synthesizedBodyPos);
if (!argsbody)
return null();
handler.setFunctionFormalParametersAndBody(funNode, argsbody);
funbox->function()->setArgCount(0);
funbox->setStart(tokenStream);
// Push a LexicalScope on to the stack.
ParseContext::Scope lexicalScope(this);
if (!lexicalScope.init(pc))
return null();
auto stmtList = handler.newStatementList(synthesizedBodyPos);
if (!stmtList)
return null();
if (!noteUsedName(context->names().dotThis))
return null();
// One might expect a noteUsedName(".initializers") here. See comment in
// GeneralParser<ParseHandler, Unit>::classDefinition on why it's not here.
bool canSkipLazyClosedOverBindings = handler.canSkipLazyClosedOverBindings();
if (!declareFunctionThis(canSkipLazyClosedOverBindings))
return null();
auto initializerBody = finishLexicalScope(lexicalScope, stmtList);
if (!initializerBody)
return null();
handler.setBeginPosition(initializerBody, stmtList);
handler.setEndPosition(initializerBody, stmtList);
handler.setFunctionBody(funNode, initializerBody);
if (!finishFunction())
return null();
// This function is asserted to set classStmt->constructorBox - however, it's
// not directly set in this function, but rather in
// initWithEnclosingParseContext.
return funNode;
}
template <class ParseHandler>
typename ParseHandler::FunctionNodeType
Parser<ParseHandler>::fieldInitializerOpt(YieldHandling yieldHandling, bool hasHeritage,
Node propName, HandleAtom propAtom, size_t& numFieldKeys)
{
bool hasInitializer = false;
if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
return null();
TokenPos firstTokenPos;
if (hasInitializer) {
firstTokenPos = pos();
} else {
// the location of the "initializer" should be a zero-width span:
// class C {
// x /* here */ ;
// }
uint32_t endPos = pos().end;
firstTokenPos = TokenPos(endPos, endPos);
}
// Create the function object.
RootedFunction fun(context,
newFunction(propAtom, FunctionSyntaxKind::Expression,
GeneratorKind::NotGenerator,
FunctionAsyncKind::SyncFunction));
if (!fun)
return null();
// Create the top-level field initializer node.
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Expression, 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->initFieldInitializer(pc, hasHeritage);
handler.setFunctionBox(funNode, funbox);
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();
// Push a VarScope on to the stack.
ParseContext::VarScope varScope(this);
if (!varScope.init(pc))
return null();
// Push a LexicalScope on to the stack.
ParseContext::Scope lexicalScope(this);
if (!lexicalScope.init(pc))
return null();
Node initializerExpr;
TokenPos wholeInitializerPos;
if (hasInitializer) {
// Parse the expression for the field initializer.
{
AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, AwaitIsName);
initializerExpr = assignExpr(InAllowed, YieldIsName, TripledotProhibited);
if (!initializerExpr)
return null();
}
wholeInitializerPos = pos();
wholeInitializerPos.begin = firstTokenPos.begin;
} else {
initializerExpr = handler.newRawUndefinedLiteral(firstTokenPos);
if (!initializerExpr)
return null();
wholeInitializerPos = firstTokenPos;
}
// Update the end position of the parse node.
handler.setEndPosition(funNode, wholeInitializerPos.end);
funbox->setEnd(pos().end);
// Create a ListNode for the parameters + body (there are no parameters)
ListNodeType argsbody = handler.newList(PNK_PARAMSBODY, wholeInitializerPos);
if (!argsbody)
return null();
handler.setFunctionFormalParametersAndBody(funNode, argsbody);
funbox->function()->setArgCount(0);
funbox->usesThis = true;
NameNodeType thisName = newThisName();
if (!thisName)
return null();
// Build `this.field` expression.
ThisLiteralType propAssignThis = handler.newThisLiteral(wholeInitializerPos, thisName);
if (!propAssignThis)
return null();
Node propAssignFieldAccess;
uint32_t indexValue;
if (!propAtom) {
// See BytecodeEmitter::emitCreateFieldKeys for an explanation of what
// .fieldKeys means and its purpose.
Node dotFieldKeys = newInternalDotName(context->names().dotFieldKeys);
if (!dotFieldKeys)
return null();
double fieldKeyIndex = numFieldKeys;
numFieldKeys++;
Node fieldKeyIndexNode = handler.newNumber(fieldKeyIndex, DecimalPoint::NoDecimal, wholeInitializerPos);
if (!fieldKeyIndexNode)
return null();
Node fieldKeyValue = handler.newPropertyByValue(dotFieldKeys, fieldKeyIndexNode, wholeInitializerPos.end);
if (!fieldKeyValue)
return null();
propAssignFieldAccess = handler.newPropertyByValue(propAssignThis, fieldKeyValue, wholeInitializerPos.end);
if (!propAssignFieldAccess)
return null();
} else if (propAtom->isIndex(&indexValue)) {
propAssignFieldAccess = handler.newPropertyByValue(propAssignThis, propName, wholeInitializerPos.end);
if (!propAssignFieldAccess)
return null();
} else {
NameNodeType propAssignName = handler.newPropertyName(propAtom->asPropertyName(), wholeInitializerPos);
if (!propAssignName)
return null();
propAssignFieldAccess = handler.newPropertyAccess(propAssignThis, propAssignName);
if (!propAssignFieldAccess)
return null();
}
// Synthesize an assignment expression for the property.
AssignmentNodeType initializerAssignment = handler.newAssignment(PNK_ASSIGN,
propAssignFieldAccess, initializerExpr,
JSOP_NOP);
if (!initializerAssignment)
return null();
bool canSkipLazyClosedOverBindings = handler.canSkipLazyClosedOverBindings();
if (!declareFunctionThis(canSkipLazyClosedOverBindings))
return null();
UnaryNodeType exprStatement = handler.newExprStatement(initializerAssignment, wholeInitializerPos.end);
if (!exprStatement)
return null();
ListNodeType statementList = handler.newStatementList(wholeInitializerPos);
if (!statementList)
return null();
handler.addStatementToList(statementList, exprStatement);
// Set the function's body to the field assignment.
LexicalScopeNodeType initializerBody = finishLexicalScope(lexicalScope, statementList);
if (!initializerBody) {
return null();
}
handler.setFunctionBody(funNode, initializerBody);
if (!finishFunction())
return null();
if (!leaveInnerFunction(outerpc))
return null();
return funNode;
}
template <class ParseHandler>
bool
Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
@ -8543,7 +8966,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
}
}
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Arrow);
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Arrow, pos());
if (!funNode)
return null();
@ -8940,7 +9363,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
{
FunctionNodeType genfn = handler.newFunction(FunctionSyntaxKind::Expression);
FunctionNodeType genfn = handler.newFunction(FunctionSyntaxKind::Expression, pos());
if (!genfn)
return null();
@ -9698,7 +10121,12 @@ Parser<ParseHandler>::checkLabelOrIdentifierReference(PropertyName* ident,
tt = hint;
}
if (tt == TOK_NAME)
if (!pc->sc()->allowArguments() && ident == context->names().arguments) {
error(JSMSG_BAD_ARGUMENTS);
return false;
}
if (tt == TOK_NAME || tt == TOK_PRIVATE_NAME)
return true;
if (TokenKindIsContextualKeyword(tt)) {
if (tt == TOK_YIELD) {
@ -10068,6 +10496,7 @@ DoubleToAtom(ExclusiveContext* cx, double value)
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::propertyName(YieldHandling yieldHandling,
PropertyNameContext propertyNameContext,
const Maybe<DeclarationKind>& maybeDecl, ListNodeType propList,
PropertyType* propType, MutableHandleAtom propAtom)
{
@ -10226,6 +10655,16 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling,
return propName;
}
if (propertyNameContext == PropertyNameInClass && (tt == TOK_SEMI || tt == TOK_ASSIGN)) {
if (isGenerator || isAsync) {
error(JSMSG_BAD_PROP_ID);
return null();
}
tokenStream.ungetToken();
*propType = PropertyType::Field;
return propName;
}
if (TokenKindIsPossibleIdentifierName(ltok) &&
(tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN))
{
@ -10326,7 +10765,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
TokenPos namePos = tokenStream.nextToken().pos;
PropertyType propType;
Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
Node propName = propertyName(yieldHandling, PropertyNameInLiteral, declKind, literal, &propType, &propAtom);
if (!propName)
return null();
@ -10557,7 +10996,7 @@ Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType prop
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
FunctionNodeType funNode = handler.newFunction(syntaxKind);
FunctionNodeType funNode = handler.newFunction(syntaxKind, pos());
if (!funNode)
return null();

View file

@ -583,7 +583,8 @@ enum class PropertyType {
AsyncMethod,
AsyncGeneratorMethod,
Constructor,
DerivedConstructor
DerivedConstructor,
Field,
};
// Specify a value for an ES6 grammar parametrization. We have no enum for
@ -720,6 +721,15 @@ class UsedNameTracker
MOZ_MUST_USE bool noteUse(ExclusiveContext* cx, JSAtom* name,
uint32_t scriptId, uint32_t scopeId);
MOZ_MUST_USE bool markAsAlwaysClosedOver(ExclusiveContext* cx, JSAtom* name,
uint32_t scriptId, uint32_t scopeId) {
// This marks a variable as always closed over:
// UsedNameInfo::noteBoundInScope only checks if scriptId and scopeId are
// greater than the current scriptId/scopeId, so do a simple increment to
// make that so.
return noteUse(cx, name, scriptId + 1, scopeId + 1);
}
struct RewindToken
{
private:
@ -1171,7 +1181,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
*/
JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
HandleObject proto);
HandleObject proto = nullptr);
void trace(JSTracer* trc);
@ -1478,6 +1488,24 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
enum ClassContext { ClassStatement, ClassExpression };
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& numFields,
size_t& numFieldKeys,
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 fieldInitializerOpt(YieldHandling yieldHandling, bool hasHeritage,
Node name, HandleAtom atom, size_t& numFieldKeys);
FunctionNodeType synthesizeConstructor(HandleAtom className,
uint32_t classNameOffset);
bool checkLabelOrIdentifierReference(PropertyName* ident,
uint32_t offset,
@ -1522,8 +1550,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
bool matchInOrOf(bool* isForInp, bool* isForOfp);
bool hasUsedFunctionSpecialName(HandlePropertyName name);
bool declareFunctionArgumentsObject();
bool declareFunctionThis();
bool declareFunctionArgumentsObject(bool canSkipLazyClosedOverBindings);
bool declareFunctionThis(bool canSkipLazyClosedOverBindings);
NameNodeType newInternalDotName(HandlePropertyName name);
NameNodeType newThisName();
NameNodeType newDotGeneratorName();
@ -1596,7 +1624,9 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope);
LexicalScopeNodeType finishLexicalScope(ParseContext::Scope& scope, Node body);
enum PropertyNameContext { PropertyNameInLiteral, PropertyNameInPattern, PropertyNameInClass };
Node propertyName(YieldHandling yieldHandling,
PropertyNameContext propertyNameContext,
const mozilla::Maybe<DeclarationKind>& maybeDecl, ListNodeType propList,
PropertyType* propType, MutableHandleAtom propAtom);
UnaryNodeType computedPropertyName(YieldHandling yieldHandling,

View file

@ -243,6 +243,7 @@ class SharedContext
bool allowNewTarget_;
bool allowSuperProperty_;
bool allowSuperCall_;
bool allowArguments_;
bool inWith_;
bool needsThisTDZChecks_;
@ -262,6 +263,7 @@ class SharedContext
allowNewTarget_(false),
allowSuperProperty_(false),
allowSuperCall_(false),
allowArguments_(true),
inWith_(false),
needsThisTDZChecks_(false)
{ }
@ -286,6 +288,7 @@ class SharedContext
bool allowNewTarget() const { return allowNewTarget_; }
bool allowSuperProperty() const { return allowSuperProperty_; }
bool allowSuperCall() const { return allowSuperCall_; }
bool allowArguments() const { return allowArguments_; }
bool inWith() const { return inWith_; }
bool needsThisTDZChecks() const { return needsThisTDZChecks_; }
@ -452,6 +455,7 @@ class FunctionBox : public ObjectBox, public SharedContext
void initFromLazyFunction();
void initStandaloneFunction(Scope* enclosingScope);
void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind);
void initFieldInitializer(ParseContext* enclosing, bool hasHeritage);
ObjectBox* toObjectBox() override { return this; }
JSFunction* function() const { return &object->as<JSFunction>(); }
@ -563,7 +567,11 @@ class FunctionBox : public ObjectBox, public SharedContext
}
void setStart(const TokenStream& tokenStream) {
bufStart = tokenStream.currentToken().pos.begin;
setStart(tokenStream, tokenStream.currentToken().pos);
}
void setStart(const TokenStream& tokenStream, const TokenPos& tokenPos) {
bufStart = tokenPos.begin;
tokenStream.srcCoords.lineNumAndColumnIndex(bufStart, &startLine, &startColumn);
}

View file

@ -312,7 +312,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
Node newGenExp(Node callee, Node args) { return NodeGeneric; }
ListNodeType newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
ListNodeType newClassMethodList(uint32_t begin) { return NodeGeneric; }
ListNodeType newClassMemberList(uint32_t begin) { return NodeGeneric; }
ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; }
ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
@ -331,7 +331,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
MOZ_MUST_USE bool addShorthand(ListNodeType literal, NameNodeType name, NameNodeType expr) { return true; }
MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { return true; }
MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node name, FunctionNodeType funNode, JSOp op) { return true; }
MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType literal, Node name, FunctionNodeType funNode, JSOp op, bool isStatic) { return true; }
MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType memberList, Node key, FunctionNodeType funNode, JSOp op, bool isStatic) { return true; }
MOZ_MUST_USE bool addClassFieldDefinition(ListNodeType memberList, Node name, FunctionNodeType initializer) { return true; }
UnaryNodeType newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
UnaryNodeType newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
@ -417,7 +418,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
void checkAndSetIsDirectRHSAnonFunction(Node pn) {}
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind) { return NodeFunctionDefinition; }
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind, const TokenPos& pos) { return NodeFunctionDefinition; }
bool setComprehensionLambdaBody(FunctionNodeType funNode, ListNodeType body) { return true; }
void setFunctionFormalParametersAndBody(FunctionNodeType funNode, ListNodeType paramsBody) {}
@ -468,7 +469,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
MOZ_ASSERT(kind != PNK_CONST);
return NodeGeneric;
}
ListNodeType newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) {
ListNodeType newList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) {
return newList(kind, op);
}
ListNodeType newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {

View file

@ -71,6 +71,7 @@
macro(LP, "'('") \
macro(RP, "')'") \
macro(NAME, "identifier") \
macro(PRIVATE_NAME, "private identifier") \
macro(NUMBER, "numeric literal") \
macro(STRING, "string literal") \
\
@ -322,6 +323,7 @@ inline MOZ_MUST_USE bool
TokenKindIsPossibleIdentifier(TokenKind tt)
{
return tt == TOK_NAME ||
tt == TOK_PRIVATE_NAME ||
TokenKindIsContextualKeyword(tt) ||
TokenKindIsStrictReservedWord(tt);
}

View file

@ -93,18 +93,35 @@ FindReservedWord(const CharT* s, size_t length)
}
static const ReservedWordInfo*
FindReservedWord(JSLinearString* str)
FindReservedWord(JSLinearString* str, js::frontend::NameVisibility* visibility)
{
JS::AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
? FindReservedWord(str->latin1Chars(nogc), str->length())
: FindReservedWord(str->twoByteChars(nogc), str->length());
if (str->hasLatin1Chars()) {
const JS::Latin1Char* chars = str->latin1Chars(nogc);
size_t length = str->length();
if (length > 0 && chars[0] == '#') {
*visibility = js::frontend::NameVisibility::Private;
return nullptr;
}
*visibility = js::frontend::NameVisibility::Public;
return FindReservedWord(chars, length);
}
const char16_t* chars = str->twoByteChars(nogc);
size_t length = str->length();
if (length > 0 && chars[0] == '#') {
*visibility = js::frontend::NameVisibility::Private;
return nullptr;
}
*visibility = js::frontend::NameVisibility::Public;
return FindReservedWord(chars, length);
}
template <typename CharT>
static bool
IsIdentifier(const CharT* chars, size_t length)
{
// Generic version for latin1 in char* and UCS-2 in char16_t*
if (length == 0)
return false;
@ -138,14 +155,52 @@ GetSingleCodePoint(const char16_t** p, const char16_t* end)
return codePoint;
}
namespace js {
namespace frontend {
// Latin1 Variants
bool
IsIdentifier(const Latin1Char* chars, size_t length)
{
return ::IsIdentifier(chars, length);
}
static bool
IsIdentifierNameOrPrivateName(const Latin1Char* chars, size_t length)
{
if (length == 0)
return false;
if (char16_t(*chars) == '#') {
++chars;
--length;
}
return IsIdentifier(chars, length);
}
// UTF-16 Versions
bool
IsIdentifier(const char16_t* chars, size_t length)
{
return ::IsIdentifier(chars, length);
}
static bool
IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length)
{
if (IsIdentifier(chars, length))
return true;
if (length == 0)
return false;
// XXX Revisit if this is still faster.
// Assumption is that iterating the string twice in the rare worst case (not a valid UCS-2
// identifier, but valid in UTF-16) is on average better than parsing UTF-16 code points
// individually for every input.
if (IsIdentifier(chars, length)) {
return true;
}
const char16_t* p = chars;
const char16_t* end = chars + length;
@ -164,56 +219,75 @@ IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length)
return true;
}
static bool
IsIdentifierNameOrPrivateNameMaybeNonBMP(const char16_t* chars, size_t length)
{
if (length == 0)
return false;
// '#' is always just one character in either UCS-2 or UTF-16, so compare it directly.
if (char16_t(*chars) == '#') {
++chars;
--length;
}
return IsIdentifierMaybeNonBMP(chars, length);
}
bool
frontend::IsIdentifier(JSLinearString* str)
IsIdentifier(JSLinearString* str)
{
JS::AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
? ::IsIdentifier(str->latin1Chars(nogc), str->length())
: ::IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length());
if (str->hasLatin1Chars()) {
return IsIdentifier(str->latin1Chars(nogc), str->length());
}
return IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length());
}
bool
frontend::IsIdentifier(const char* chars, size_t length)
IsIdentifierNameOrPrivateName(JSLinearString* str)
{
return ::IsIdentifier(chars, length);
JS::AutoCheckCannotGC nogc;
if (str->hasLatin1Chars()) {
return IsIdentifierNameOrPrivateName(str->latin1Chars(nogc), str->length());
}
return IsIdentifierNameOrPrivateNameMaybeNonBMP(str->twoByteChars(nogc), str->length());
}
bool
frontend::IsIdentifier(const char16_t* chars, size_t length)
IsKeyword(JSLinearString* str)
{
return ::IsIdentifier(chars, length);
}
bool
frontend::IsKeyword(JSLinearString* str)
{
if (const ReservedWordInfo* rw = FindReservedWord(str))
NameVisibility visibility;
if (const ReservedWordInfo* rw = FindReservedWord(str, &visibility))
return TokenKindIsKeyword(rw->tokentype);
return false;
}
TokenKind
frontend::ReservedWordTokenKind(PropertyName* str)
ReservedWordTokenKind(PropertyName* str)
{
if (const ReservedWordInfo* rw = FindReservedWord(str))
NameVisibility visibility;
if (const ReservedWordInfo* rw = FindReservedWord(str, &visibility))
return rw->tokentype;
return TOK_NAME;
return visibility == NameVisibility::Private ? TOK_PRIVATE_NAME : TOK_NAME;
}
const char*
frontend::ReservedWordToCharZ(PropertyName* str)
ReservedWordToCharZ(PropertyName* str)
{
if (const ReservedWordInfo* rw = FindReservedWord(str))
NameVisibility visibility;
if (const ReservedWordInfo* rw = FindReservedWord(str, &visibility))
return ReservedWordToCharZ(rw->tokentype);
return nullptr;
}
const char*
frontend::ReservedWordToCharZ(TokenKind tt)
ReservedWordToCharZ(TokenKind tt)
{
MOZ_ASSERT(tt != TOK_NAME);
switch (tt) {
@ -226,6 +300,10 @@ frontend::ReservedWordToCharZ(TokenKind tt)
return nullptr;
}
} // namespace frontend
} // namespace js
PropertyName*
TokenStream::reservedWordToPropertyName(TokenKind tt) const
{
@ -592,7 +670,7 @@ TokenStream::TokenBuf::findEOLMax(size_t start, size_t max)
if (n >= max)
break;
n++;
// This stops at U+2028 LINE SEPARATOR or U+2029 PARAGRAPH SEPARATOR in
// string and template literals. These code points do affect line and
// column coordinates, even as they encode their literal values.
@ -1302,6 +1380,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
bool hasExp;
DecimalPoint decimalPoint;
const char16_t* identStart;
NameVisibility identVisibility;
bool hadUnicodeEscape;
// Check if in the middle of a template string. Have to get this out of
@ -1346,6 +1425,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
if (unicode::IsUnicodeIDStart(char16_t(c))) {
identStart = userbuf.addressOfNextRawChar() - 1;
hadUnicodeEscape = false;
identVisibility = NameVisibility::Public;
goto identifier;
}
@ -1356,6 +1436,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
{
identStart = userbuf.addressOfNextRawChar() - 2;
hadUnicodeEscape = false;
identVisibility = NameVisibility::Public;
goto identifier;
}
}
@ -1404,6 +1485,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
tp = newToken(-1);
identStart = userbuf.addressOfNextRawChar() - 1;
hadUnicodeEscape = false;
identVisibility = NameVisibility::Public;
identifier:
for (;;) {
@ -1445,11 +1527,14 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
length = userbuf.addressOfNextRawChar() - identStart;
}
// Represent reserved words as reserved word tokens.
if (!hadUnicodeEscape) {
if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) {
tp->type = rw->tokentype;
goto out;
// Private identifiers start with a '#', and so cannot be reserved words.
if (identVisibility == NameVisibility::Public) {
// Represent reserved words as reserved word tokens.
if (!hadUnicodeEscape) {
if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) {
tp->type = rw->tokentype;
goto out;
}
}
}
@ -1457,7 +1542,17 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
if (!atom) {
goto error;
}
tp->type = TOK_NAME;
if (identVisibility == NameVisibility::Private) {
MOZ_ASSERT(identStart[0] == '#', "Private identifier starts with #");
tp->type = TOK_PRIVATE_NAME;
if (!options().fieldsEnabledOption) {
reportError(JSMSG_FIELDS_NOT_SUPPORTED);
goto error;
}
} else {
tp->type = TOK_NAME;
}
tp->setName(atom->asPropertyName());
goto out;
}
@ -1762,11 +1857,28 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
if (escapeLength > 0) {
identStart = userbuf.addressOfNextRawChar() - escapeLength - 1;
hadUnicodeEscape = true;
identVisibility = NameVisibility::Public;
goto identifier;
}
goto badchar;
}
case '#': {
// TODO: This does not handle escaped private property names due to being extremely difficult
// in the current state of the tokenizer. If #1351107 is ported, it becomes straightforward.
c = getCharIgnoreEOL();
// '$' and '_' are not in IsUnicodeIDStart
c1kind = FirstCharKind(firstCharKinds[c]);
if (c1kind == Ident || unicode::IsUnicodeIDStart(char16_t(c))) {
identStart = userbuf.addressOfNextRawChar() - 2;
hadUnicodeEscape = false;
identVisibility = NameVisibility::Private;
goto identifier;
}
ungetCharIgnoreEOL(c);
goto badchar;
}
case '|':
if (matchChar('|'))
tp->type = TOK_OR;
@ -2235,7 +2347,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
updateFlagsForEOL();
} else if (c == LINE_SEPARATOR || c == PARA_SEPARATOR) {
// U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR encode
// their literal values in template literals and (as of the
// their literal values in template literals and (as of the
// JSON superset proposal) string literals, but they still count
// as line terminators when computing line/column coordinates.
updateLineInfoForEOL();

View file

@ -92,6 +92,8 @@ enum class InvalidEscapeType {
Octal
};
enum class NameVisibility { Public, Private };
class TokenStream;
struct Token
@ -185,7 +187,7 @@ struct Token
// Mutators
void setName(PropertyName* name) {
MOZ_ASSERT(type == TOK_NAME);
MOZ_ASSERT(type == TOK_NAME || type == TOK_PRIVATE_NAME);
u.name = name;
}
@ -211,7 +213,7 @@ struct Token
// Type-safe accessors
PropertyName* name() const {
MOZ_ASSERT(type == TOK_NAME);
MOZ_ASSERT(type == TOK_NAME || type == TOK_PRIVATE_NAME);
return u.name->JSAtom::asPropertyName(); // poor-man's type verification
}
@ -344,7 +346,7 @@ class MOZ_STACK_CLASS TokenStream
public:
PropertyName* currentName() const {
if (isCurrentTokenType(TOK_NAME)) {
if (isCurrentTokenType(TOK_NAME) || isCurrentTokenType(TOK_PRIVATE_NAME)) {
return currentToken().name();
}
@ -353,7 +355,7 @@ class MOZ_STACK_CLASS TokenStream
}
bool currentNameHasEscapes() const {
if (isCurrentTokenType(TOK_NAME)) {
if (isCurrentTokenType(TOK_NAME) || isCurrentTokenType(TOK_PRIVATE_NAME)) {
TokenPos pos = currentToken().pos;
return (pos.end - pos.begin) != currentToken().name()->length();
}

View file

@ -216,6 +216,7 @@ MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch state
MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
MSG_DEF(JSMSG_BAD_SUPERCALL, 0, JSEXN_SYNTAXERR, "super() is only valid in derived class constructors")
MSG_DEF(JSMSG_BAD_ARGUMENTS, 0, JSEXN_SYNTAXERR, "arguments is not valid in fields")
MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list")
MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
@ -264,6 +265,7 @@ MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'fr
MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
MSG_DEF(JSMSG_MISSING_PRIVATE_NAME, 0, JSEXN_SYNTAXERR, "'#' not followed by identifier")
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character")
MSG_DEF(JSMSG_IMPORT_META_OUTSIDE_MODULE, 0, JSEXN_SYNTAXERR, "import.meta may only appear in a module")
MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
@ -360,6 +362,8 @@ MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allo
MSG_DEF(JSMSG_BAD_NEW_OPTIONAL, 0, JSEXN_SYNTAXERR, "new keyword cannot be used with an optional chain")
MSG_DEF(JSMSG_BAD_OPTIONAL_TEMPLATE, 0, JSEXN_SYNTAXERR, "tagged template cannot be used with optional chain")
MSG_DEF(JSMSG_ESCAPED_KEYWORD, 0, JSEXN_SYNTAXERR, "keywords must be written literally, without embedded escapes")
MSG_DEF(JSMSG_MISSING_SEMI_FIELD, 0, JSEXN_SYNTAXERR, "missing ; after field definition")
MSG_DEF(JSMSG_FIELDS_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "fields are not currently supported")
// asm.js
MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}")

View file

@ -4430,7 +4430,7 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain,
return false;
// If name is not valid identifier
if (!js::frontend::IsIdentifier(name, nameLen))
if (!js::frontend::IsIdentifier(reinterpret_cast<const Latin1Char*>(name), nameLen))
isInvalidName = true;
}

View file

@ -86,4 +86,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")
/* AST_LIMIT = last + 1 */

View file

@ -238,6 +238,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
uint32_t toStringEnd = script->toStringEnd();
uint32_t lineno = script->lineno();
uint32_t column = script->column();
uint32_t numFieldInitializers;
if (mode == XDR_ENCODE) {
packedFields = lazy->packedFields();
@ -251,10 +252,17 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
// relazify scripts with inner functions. See
// JSFunction::createScriptForLazilyInterpretedFunction.
MOZ_ASSERT(lazy->numInnerFunctions() == 0);
if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
numFieldInitializers = (uint32_t)lazy->getFieldInitializers().numFieldInitializers;
} else {
numFieldInitializers = UINT32_MAX;
}
}
if (!xdr->codeUint64(&packedFields))
return false;
if (!xdr->codeUint32(&numFieldInitializers))
return false;
if (mode == XDR_DECODE) {
RootedScriptSource sourceObject(cx, &script->scriptSourceUnwrap());
@ -265,6 +273,9 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
return false;
lazy->setToStringEnd(toStringEnd);
if (numFieldInitializers != UINT32_MAX) {
lazy->setFieldInitializers(FieldInitializers((size_t)numFieldInitializers));
}
// As opposed to XDRLazyScript, we need to restore the runtime bits
// of the script, as we are trying to match the fact this function
@ -975,6 +986,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
uint32_t lineno;
uint32_t column;
uint64_t packedFields;
uint32_t numFieldInitializers;
if (mode == XDR_ENCODE) {
// Note: it's possible the LazyScript has a non-null script_ pointer
@ -990,13 +1002,19 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
lineno = lazy->lineno();
column = lazy->column();
packedFields = lazy->packedFields();
if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
numFieldInitializers = (uint32_t)lazy->getFieldInitializers().numFieldInitializers;
} else {
numFieldInitializers = UINT32_MAX;
}
}
if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
!xdr->codeUint32(&toStringStart) ||
!xdr->codeUint32(&toStringEnd) ||
!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
!xdr->codeUint64(&packedFields))
!xdr->codeUint64(&packedFields) ||
!xdr->codeUint32(&numFieldInitializers))
{
return false;
}
@ -1007,6 +1025,9 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
if (!lazy)
return false;
lazy->setToStringEnd(toStringEnd);
if (numFieldInitializers != UINT32_MAX) {
lazy->setFieldInitializers(FieldInitializers((size_t)numFieldInitializers));
}
fun->initLazyScript(lazy);
}
}
@ -4110,6 +4131,7 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
sourceObject_(nullptr),
table_(table),
packedFields_(packedFields),
fieldInitializers_(FieldInitializers::Invalid()),
begin_(begin),
end_(end),
toStringStart_(toStringStart),

View file

@ -1999,11 +1999,39 @@ static_assert(sizeof(JSScript) % js::gc::CellSize == 0,
namespace js {
struct FieldInitializers
{
#ifdef DEBUG
bool valid;
#endif
// This struct will eventually have a vector of constant values for optimizing
// field initializers.
size_t numFieldInitializers;
explicit FieldInitializers(size_t numFieldInitializers)
:
#ifdef DEBUG
valid(true),
#endif
numFieldInitializers(numFieldInitializers) {
}
static FieldInitializers Invalid() { return FieldInitializers(); }
private:
FieldInitializers()
:
#ifdef DEBUG
valid(false),
#endif
numFieldInitializers(0) {
}
};
// Information about a script which may be (or has been) lazily compiled to
// bytecode from its source.
class LazyScript : public gc::TenuredCell
{
private:
// If non-nullptr, the script has been compiled and this is a forwarding
// pointer to the result. This is a weak pointer: after relazification, we
// can collect the script if there are no other pointers to it.
@ -2030,7 +2058,6 @@ class LazyScript : public gc::TenuredCell
uint32_t padding;
#endif
private:
static const uint32_t NumClosedOverBindingsBits = 20;
static const uint32_t NumInnerFunctionsBits = 20;
@ -2072,6 +2099,8 @@ class LazyScript : public gc::TenuredCell
uint64_t packedFields_;
};
FieldInitializers fieldInitializers_;
// Source location for the script.
// See the comment in JSScript for the details.
uint32_t begin_;
@ -2297,6 +2326,12 @@ class LazyScript : public gc::TenuredCell
p_.hasThisBinding = true;
}
void setFieldInitializers(FieldInitializers fieldInitializers) {
fieldInitializers_ = fieldInitializers;
}
FieldInitializers getFieldInitializers() const { return fieldInitializers_; }
const char* filename() const {
return scriptSource()->filename();
}

View file

@ -102,6 +102,8 @@
macro(dotAll, dotAll, "dotAll") \
macro(dotGenerator, dotGenerator, ".generator") \
macro(dotThis, dotThis, ".this") \
macro(dotInitializers, dotInitializers, ".initializers") \
macro(dotFieldKeys, dotFieldKeys, ".fieldKeys") \
macro(each, each, "each") \
macro(elementType, elementType, "elementType") \
macro(else, else_, "else") \

View file

@ -1890,6 +1890,7 @@
macro(JSOP_UNPICK, 183,"unpick", NULL, 2, 0, 0, JOF_UINT8) \
/*
* Pops the top of stack value, pushes property of it onto the stack.
* Requires the value under 'obj' to be the receiver of the following call.
*
* Like JSOP_GETPROP but for call context.
* Category: Literals
@ -1974,7 +1975,8 @@
\
/*
* Pops the top two values on the stack as 'propval' and 'obj', pushes
* 'propval' property of 'obj' onto the stack.
* 'propval' property of 'obj' onto the stack. Requires the value under
* 'obj' to be the receiver of the following call.
*
* Like JSOP_GETELEM but for call context.
* Category: Literals

View file

@ -7045,7 +7045,7 @@ ParseFunction(ModuleValidator& m, FunctionNode** funNodeOut, unsigned* line)
return false;
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
FunctionNode* funNode = m.parser().handler.newFunction(syntaxKind);
FunctionNode* funNode = m.parser().handler.newFunction(syntaxKind, m.parser().pos());
if (!funNode)
return false;