mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-15 08:53:07 +09:00
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:
parent
afa7bddc96
commit
51db22ff2f
24 changed files with 1383 additions and 290 deletions
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>");
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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") \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue