author | Ashley Hauck <khyperia@mozilla.com> |
Thu, 25 Oct 2018 18:25:34 +0000 | |
changeset 443085 | d0b577458d53f59ecabc3556ce04a27a8e8eb6d0 |
parent 443084 | fba3ebc7b200042c5f3fd9114e78cce0896708cf |
child 443086 | f3d23a7bcbb6e30a5d7368882fdf1b04498d9bdf |
child 443101 | 4d10fab344663acad54d62e1527b8e679103cf1d |
push id | 71785 |
push user | aciure@mozilla.com |
push date | Fri, 26 Oct 2018 00:08:02 +0000 |
treeherder | autoland@d0b577458d53 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jorendorff |
bugs | 1499448 |
milestone | 65.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -544,17 +544,17 @@ class NodeBuilder HandleValue isDefault, TokenPos* pos, MutableHandleValue dst); MOZ_MUST_USE bool exportSpecifier(HandleValue bindingName, HandleValue exportName, TokenPos* pos, MutableHandleValue dst); MOZ_MUST_USE bool exportBatchSpecifier(TokenPos* pos, MutableHandleValue dst); 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); /* * expressions */ MOZ_MUST_USE bool binaryExpression(BinaryOperator op, HandleValue left, HandleValue right, @@ -1647,19 +1647,19 @@ NodeBuilder::classMethod(HandleValue nam "name", name, "body", body, "kind", kindName, "static", isStaticVal, dst); } bool -NodeBuilder::classMethods(NodeVector& methods, MutableHandleValue dst) +NodeBuilder::classMembers(NodeVector& members, MutableHandleValue dst) { - return newArray(methods, dst); + return newArray(members, dst); } bool NodeBuilder::classDefinition(bool expr, HandleValue name, HandleValue heritage, HandleValue block, TokenPos* pos, MutableHandleValue dst) { ASTType type = expr ? AST_CLASS_EXPR : AST_CLASS_STMT; RootedValue cb(cx, callbacks[type]); @@ -2387,17 +2387,17 @@ ASTSerializer::classDefinition(ClassNode if (ClassNames* names = pn->names()) { if (!identifier(names->innerBinding(), &className)) { return false; } } return optExpression(pn->heritage(), &heritage) && - statement(pn->methodList(), &classBody) && + statement(pn->memberList(), &classBody) && builder.classDefinition(expr, className, heritage, classBody, &pn->pn_pos, dst); } bool ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) { if (!CheckRecursionLimit(cx)) { return false; @@ -2615,36 +2615,41 @@ ASTSerializer::statement(ParseNode* pn, } case ParseNodeKind::Debugger: return builder.debuggerStatement(&pn->pn_pos, dst); case ParseNodeKind::Class: return classDefinition(&pn->as<ClassNode>(), false, dst); - case ParseNodeKind::ClassMethodList: + case ParseNodeKind::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()) { + for (ParseNode* item : memberList->contents()) { + if (item->is<ClassField>()) { + // TODO(khyperia): Implement private field access. + return false; + } + ClassMethod* method = &item->as<ClassMethod>(); - MOZ_ASSERT(methodList->pn_pos.encloses(method->pn_pos)); + MOZ_ASSERT(memberList->pn_pos.encloses(method->pn_pos)); RootedValue prop(cx); if (!classMethod(method, &prop)) { return false; } - methods.infallibleAppend(prop); + members.infallibleAppend(prop); } - return builder.classMethods(methods, dst); + return builder.classMembers(members, dst); } default: LOCAL_NOT_REACHED("unexpected statement type"); } } bool @@ -2955,16 +2960,17 @@ ASTSerializer::expression(ParseNode* pn, return node->isKind(ParseNodeKind::New) ? builder.newExpression(callee, args, &node->pn_pos, dst) : builder.callExpression(callee, args, &node->pn_pos, dst); } case ParseNodeKind::Dot: { PropertyAccess* prop = &pn->as<PropertyAccess>(); + // TODO(khyperia): Implement private field access. MOZ_ASSERT(prop->pn_pos.encloses(prop->expression().pn_pos)); RootedValue expr(cx); RootedValue propname(cx); RootedAtom pnAtom(cx, prop->key().atom()); if (prop->isSuper()) { if (!builder.super(&prop->expression().pn_pos, &expr)) {
--- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -116,24 +116,32 @@ CreateScriptSourceObject(JSContext* cx, * * This returns true even if str is a keyword like "if". * * Defined in TokenStream.cpp. */ 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); +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); /* Trace all GC things reachable from parser. Defined in Parser.cpp. */ void TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1038,16 +1038,17 @@ BytecodeEmitter::checkSideEffects(ParseN case ParseNodeKind::RawUndefined: case ParseNodeKind::Elision: case ParseNodeKind::Generator: MOZ_ASSERT(pn->is<NullaryNode>()); *answer = false; return true; case ParseNodeKind::ObjectPropertyName: + case ParseNodeKind::PrivateName: // no side effects, unlike ParseNodeKind::Name case ParseNodeKind::String: case ParseNodeKind::TemplateString: MOZ_ASSERT(pn->is<NameNode>()); *answer = false; return true; case ParseNodeKind::RegExp: MOZ_ASSERT(pn->is<RegExpLiteral>()); @@ -1481,27 +1482,28 @@ BytecodeEmitter::checkSideEffects(ParseN case ParseNodeKind::ParamsBody: *answer = true; return true; case ParseNodeKind::ForIn: // by ParseNodeKind::For case ParseNodeKind::ForOf: // by ParseNodeKind::For case ParseNodeKind::ForHead: // by ParseNodeKind::For case ParseNodeKind::ClassMethod: // by ParseNodeKind::Class + case ParseNodeKind::ClassField: // by ParseNodeKind::Class case ParseNodeKind::ClassNames: // by ParseNodeKind::Class - case ParseNodeKind::ClassMethodList: // by ParseNodeKind::Class - case ParseNodeKind::ImportSpecList: // by ParseNodeKind::Import + case ParseNodeKind::ClassMemberList: // by ParseNodeKind::Class + case ParseNodeKind::ImportSpecList: // by ParseNodeKind::Import case ParseNodeKind::ImportSpec: // by ParseNodeKind::Import - case ParseNodeKind::ExportBatchSpec:// by ParseNodeKind::Export - case ParseNodeKind::ExportSpecList: // by ParseNodeKind::Export + case ParseNodeKind::ExportBatchSpec: // by ParseNodeKind::Export + case ParseNodeKind::ExportSpecList: // by ParseNodeKind::Export case ParseNodeKind::ExportSpec: // by ParseNodeKind::Export - case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate - case ParseNodeKind::PosHolder: // by ParseNodeKind::NewTarget - case ParseNodeKind::SuperBase: // by ParseNodeKind::Elem and others - case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot + case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate + case ParseNodeKind::PosHolder: // by ParseNodeKind::NewTarget + case ParseNodeKind::SuperBase: // by ParseNodeKind::Elem and others + case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot MOZ_CRASH("handled by parent nodes"); case ParseNodeKind::Limit: // invalid sentinel value MOZ_CRASH("invalid node kind"); } MOZ_CRASH("invalid, unenumerated ParseNodeKind value encountered in " "BytecodeEmitter::checkSideEffects"); @@ -1784,16 +1786,18 @@ BytecodeEmitter::emitPropLHS(PropertyAcc } // pndown is a primary expression, not a dotted property reference. if (!emitTree(pndown)) { return false; } while (true) { + // TODO(khyperia): Implement private field access. + // Walk back up the list, emitting annotated name ops. if (!emitAtomOp(pndot->key().atom(), JSOP_GETPROP)) { return false; } // Reverse the pndot->expression() link again. pnup = pndot->maybeExpression(); pndot->setExpression(pndown); @@ -1805,16 +1809,17 @@ BytecodeEmitter::emitPropLHS(PropertyAcc } return true; } bool BytecodeEmitter::emitPropIncDec(UnaryNode* incDec) { PropertyAccess* prop = &incDec->kid()->as<PropertyAccess>(); + // TODO(khyperia): Implement private field access. bool isSuper = prop->isSuper(); ParseNodeKind kind = incDec->getKind(); PropOpEmitter poe(this, kind == ParseNodeKind::PostIncrement ? PropOpEmitter::Kind::PostIncrement : kind == ParseNodeKind::PreIncrement ? PropOpEmitter::Kind::PreIncrement : kind == ParseNodeKind::PostDecrement ? PropOpEmitter::Kind::PostDecrement : PropOpEmitter::Kind::PreDecrement, isSuper @@ -2619,16 +2624,17 @@ BytecodeEmitter::emitSetOrInitializeDest case ParseNodeKind::Dot: { // The reference is already pushed by emitDestructuringLHSRef. // // [Super] // // THIS SUPERBASE VAL // // [Other] // // OBJ VAL PropertyAccess* prop = &target->as<PropertyAccess>(); + // TODO(khyperia): Implement private field access. bool isSuper = prop->isSuper(); PropOpEmitter poe(this, PropOpEmitter::Kind::SimpleAssignment, isSuper ? PropOpEmitter::ObjKind::Super : PropOpEmitter::ObjKind::Other); if (!poe.skipObjAndRhs()) { return false; @@ -3988,16 +3994,17 @@ BytecodeEmitter::emitAssignment(ParseNod MOZ_ASSERT(0); } if (isCompound) { MOZ_ASSERT(rhs); switch (lhs->getKind()) { case ParseNodeKind::Dot: { PropertyAccess* prop = &lhs->as<PropertyAccess>(); + // TODO(khyperia): Implement private field access. if (!poe->emitGet(prop->key().atom())) { // [Super] // // THIS SUPERBASE PROP // // [Other] // // OBJ PROP return false; } break; } @@ -4061,16 +4068,17 @@ BytecodeEmitter::emitAssignment(ParseNod return false; } } /* Finally, emit the specialized assignment bytecode. */ switch (lhs->getKind()) { case ParseNodeKind::Dot: { PropertyAccess* prop = &lhs->as<PropertyAccess>(); + // TODO(khyperia): Implement private field access. if (!poe->emitAssignment(prop->key().atom())) { // VAL return false; } poe.reset(); break; } case ParseNodeKind::Call: @@ -6387,16 +6395,17 @@ BytecodeEmitter::emitDeleteName(UnaryNod } bool BytecodeEmitter::emitDeleteProperty(UnaryNode* deleteNode) { MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteProp)); PropertyAccess* propExpr = &deleteNode->kid()->as<PropertyAccess>(); + // TODO(khyperia): Implement private field access. PropOpEmitter poe(this, PropOpEmitter::Kind::Delete, propExpr->as<PropertyAccess>().isSuper() ? PropOpEmitter::ObjKind::Super : PropOpEmitter::ObjKind::Other); if (propExpr->isSuper()) { // The expression |delete super.foo;| has to evaluate |super.foo|, // which could throw if |this| hasn't yet been set by a |super(...)| @@ -6788,16 +6797,17 @@ BytecodeEmitter::emitCalleeAndThis(Parse case ParseNodeKind::Name: if (!cone.emitNameCallee(callee->as<NameNode>().name())) { return false; // CALLEE THIS } break; case ParseNodeKind::Dot: { MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting); PropertyAccess* prop = &callee->as<PropertyAccess>(); + // TODO(khyperia): Implement private field access. bool isSuper = prop->isSuper(); PropOpEmitter& poe = cone.prepareForPropCallee(isSuper); if (!poe.prepareForObj()) { return false; } if (isSuper) { UnaryNode* base = &prop->expression().as<UnaryNode>(); @@ -7013,17 +7023,19 @@ BytecodeEmitter::emitCallOrNew(BinaryNod // Check if this member is a simple chain of simple chain of // property accesses, e.g. x.y.z, this.x.y, super.x.y bool simpleDotChain = false; for (ParseNode* cur = calleeNode; cur->isKind(ParseNodeKind::Dot); cur = &cur->as<PropertyAccess>().expression()) { - ParseNode* left = &cur->as<PropertyAccess>().expression(); + PropertyAccess* prop = &cur->as<PropertyAccess>(); + ParseNode* left = &prop->expression(); + // TODO(khyperia): Implement private field access. if (left->isKind(ParseNodeKind::Name) || left->isKind(ParseNodeKind::This) || left->isKind(ParseNodeKind::SuperBase)) { simpleDotChain = true; } } if (!simpleDotChain) { @@ -7297,16 +7309,20 @@ BytecodeEmitter::emitConditionalExpressi return true; } bool BytecodeEmitter::emitPropertyList(ListNode* obj, MutableHandlePlainObject objp, PropListType type) { for (ParseNode* propdef : obj->contents()) { + if (propdef->is<ClassField>()) { + // TODO(khyperia): Implement private field access. + return false; + } if (!updateSourceCoordNotes(propdef->pn_pos.begin)) { return false; } // Handle __proto__: v specially because *only* this form, and no other // involving "__proto__", performs [[Prototype]] mutation. if (propdef->isKind(ParseNodeKind::MutateProto)) { MOZ_ASSERT(type == ObjectLiteral); @@ -8127,19 +8143,23 @@ BytecodeEmitter::emitLexicalInitializati // This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15 // (BindingClassDeclarationEvaluation). bool BytecodeEmitter::emitClass(ClassNode* classNode) { ClassNames* names = classNode->names(); ParseNode* heritageExpression = classNode->heritage(); - ListNode* classMethods = classNode->methodList(); + ListNode* classMembers = classNode->memberList(); CodeNode* constructor = nullptr; - for (ParseNode* mn : classMethods->contents()) { + for (ParseNode* mn : classMembers->contents()) { + if (mn->is<ClassField>()) { + // TODO(khyperia): Implement private field access. + return false; + } ClassMethod& method = mn->as<ClassMethod>(); ParseNode& methodName = method.name(); if (!method.isStatic() && (methodName.isKind(ParseNodeKind::ObjectPropertyName) || methodName.isKind(ParseNodeKind::String)) && methodName.as<NameNode>().atom() == cx->names().constructor) { constructor = &method.method(); @@ -8316,17 +8336,17 @@ BytecodeEmitter::emitClass(ClassNode* cl if (!emitAtomOp(cx->names().prototype, JSOP_INITLOCKEDPROP)) { // ... CONSTRUCTOR HOMEOBJ CONSTRUCTOR return false; } if (!emitAtomOp(cx->names().constructor, JSOP_INITHIDDENPROP)) { // ... CONSTRUCTOR HOMEOBJ return false; } RootedPlainObject obj(cx); - if (!emitPropertyList(classMethods, &obj, ClassBody)) { // ... CONSTRUCTOR HOMEOBJ + if (!emitPropertyList(classMembers, &obj, ClassBody)) { // ... CONSTRUCTOR HOMEOBJ return false; } if (!emit1(JSOP_POP)) { // ... CONSTRUCTOR return false; } if (names) { @@ -8688,16 +8708,17 @@ BytecodeEmitter::emitTree(ParseNode* pn, case ParseNodeKind::DeleteExpr: if (!emitDeleteExpression(&pn->as<UnaryNode>())) { return false; } break; case ParseNodeKind::Dot: { PropertyAccess* prop = &pn->as<PropertyAccess>(); + // TODO(khyperia): Implement private field access. bool isSuper = prop->isSuper(); PropOpEmitter poe(this, PropOpEmitter::Kind::Get, isSuper ? PropOpEmitter::ObjKind::Super : PropOpEmitter::ObjKind::Other); if (!poe.prepareForObj()) { return false;
--- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -360,16 +360,17 @@ ContainsHoistedDeclaration(JSContext* cx case ParseNodeKind::Array: case ParseNodeKind::Object: case ParseNodeKind::PropertyName: case ParseNodeKind::Dot: case ParseNodeKind::Elem: case ParseNodeKind::Arguments: case ParseNodeKind::Call: case ParseNodeKind::Name: + case ParseNodeKind::PrivateName: case ParseNodeKind::TemplateString: case ParseNodeKind::TemplateStringList: case ParseNodeKind::TaggedTemplate: case ParseNodeKind::CallSiteObj: case ParseNodeKind::String: case ParseNodeKind::RegExp: case ParseNodeKind::True: case ParseNodeKind::False: @@ -381,17 +382,18 @@ ContainsHoistedDeclaration(JSContext* cx case ParseNodeKind::New: case ParseNodeKind::Generator: case ParseNodeKind::ParamsBody: case ParseNodeKind::Catch: case ParseNodeKind::ForIn: case ParseNodeKind::ForOf: case ParseNodeKind::ForHead: case ParseNodeKind::ClassMethod: - case ParseNodeKind::ClassMethodList: + case ParseNodeKind::ClassField: + case ParseNodeKind::ClassMemberList: case ParseNodeKind::ClassNames: case ParseNodeKind::NewTarget: case ParseNodeKind::ImportMeta: case ParseNodeKind::PosHolder: case ParseNodeKind::SuperCall: case ParseNodeKind::SuperBase: case ParseNodeKind::SetThis: MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on " @@ -1626,16 +1628,17 @@ Fold(JSContext* cx, ParseNode** pnp, Per MOZ_ASSERT(pn->is<BreakStatement>()); return true; case ParseNodeKind::Continue: MOZ_ASSERT(pn->is<ContinueStatement>()); return true; case ParseNodeKind::ObjectPropertyName: + case ParseNodeKind::PrivateName: case ParseNodeKind::String: case ParseNodeKind::TemplateString: MOZ_ASSERT(pn->is<NameNode>()); return true; case ParseNodeKind::RegExp: MOZ_ASSERT(pn->is<RegExpLiteral>()); return true; @@ -1754,17 +1757,17 @@ Fold(JSContext* cx, ParseNode** pnp, Per case ParseNodeKind::Gt: case ParseNodeKind::Ge: case ParseNodeKind::InstanceOf: case ParseNodeKind::In: case ParseNodeKind::Comma: case ParseNodeKind::Array: case ParseNodeKind::Object: case ParseNodeKind::StatementList: - case ParseNodeKind::ClassMethodList: + case ParseNodeKind::ClassMemberList: case ParseNodeKind::TemplateStringList: case ParseNodeKind::Var: case ParseNodeKind::Const: case ParseNodeKind::Let: case ParseNodeKind::ParamsBody: case ParseNodeKind::CallSiteObj: case ParseNodeKind::ExportSpecList: case ParseNodeKind::ImportSpecList: @@ -1844,16 +1847,27 @@ Fold(JSContext* cx, ParseNode** pnp, Per case ParseNodeKind::ImportSpec: case ParseNodeKind::ExportSpec: case ParseNodeKind::SetThis: { BinaryNode* node = &pn->as<BinaryNode>(); return Fold(cx, node->unsafeLeftReference(), parser) && Fold(cx, node->unsafeRightReference(), parser); } + case ParseNodeKind::ClassField: { + ClassField* node = &pn->as<ClassField>(); + if (node->hasInitializer()) { + if (!Fold(cx, node->unsafeInitializerReference(), parser)) { + return false; + } + } + + return true; + } + case ParseNodeKind::NewTarget: case ParseNodeKind::ImportMeta: { #ifdef DEBUG BinaryNode* node = &pn->as<BinaryNode>(); MOZ_ASSERT(node->left()->isKind(ParseNodeKind::PosHolder)); MOZ_ASSERT(node->right()->isKind(ParseNodeKind::PosHolder)); #endif return true;
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -259,40 +259,40 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) MOZ_MUST_USE bool addElision(ListNodeType literal, const TokenPos& pos) { MOZ_ASSERT(literal->isKind(ParseNodeKind::Array)); NullaryNode* elision = new_<NullaryNode>(ParseNodeKind::Elision, pos); if (!elision) { return false; } - addList(/* list = */ literal, /* child = */ elision); + addList(/* list = */ literal, /* kid = */ elision); literal->setHasArrayHoleOrSpread(); literal->setHasNonConstInitializer(); return true; } MOZ_MUST_USE bool addSpreadElement(ListNodeType literal, uint32_t begin, Node inner) { MOZ_ASSERT(literal->isKind(ParseNodeKind::Array)); ParseNode* spread = newSpread(begin, inner); if (!spread) { return false; } - addList(/* list = */ literal, /* child = */ spread); + addList(/* list = */ literal, /* kid = */ spread); literal->setHasArrayHoleOrSpread(); literal->setHasNonConstInitializer(); return true; } void addArrayElement(ListNodeType literal, Node element) { if (!element->isConstant()) { literal->setHasNonConstInitializer(); } - addList(/* list = */ literal, /* child = */ element); + addList(/* list = */ literal, /* kid = */ element); } BinaryNodeType newCall(Node callee, Node args) { return new_<BinaryNode>(ParseNodeKind::Call, JSOP_CALL, callee, args); } ListNodeType newArguments(const TokenPos& pos) { return new_<ListNode>(ParseNodeKind::Arguments, JSOP_NOP, pos); @@ -305,21 +305,21 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) BinaryNodeType newTaggedTemplate(Node tag, Node args) { return new_<BinaryNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args); } ListNodeType newObjectLiteral(uint32_t begin) { return new_<ListNode>(ParseNodeKind::Object, TokenPos(begin, begin + 1)); } - 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>(ParseNodeKind::ClassMethodList, TokenPos(begin, begin + 1)); + ListNodeType newClassMemberList(uint32_t begin) { + return new_<ListNode>(ParseNodeKind::ClassMemberList, TokenPos(begin, begin + 1)); } ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) { return new_<ClassNames>(outer, inner, pos); } BinaryNodeType newNewTarget(NullaryNodeType newHolder, NullaryNodeType targetHolder) { return new_<BinaryNode>(ParseNodeKind::NewTarget, JSOP_NOP, newHolder, targetHolder); } NullaryNodeType newPosHolder(const TokenPos& pos) { @@ -334,17 +334,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) // Object literals with mutated [[Prototype]] are non-constant so that // singleton objects will have Object.prototype as their [[Prototype]]. literal->setHasNonConstInitializer(); UnaryNode* mutation = newUnary(ParseNodeKind::MutateProto, begin, expr); if (!mutation) { return false; } - addList(/* list = */ literal, /* child = */ mutation); + addList(/* list = */ literal, /* kid = */ mutation); return true; } BinaryNodeType newPropertyDefinition(Node key, Node val) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); checkAndSetIsDirectRHSAnonFunction(val); return newBinary(ParseNodeKind::Colon, key, val, JSOP_INITPROP); } @@ -352,17 +352,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) { MOZ_ASSERT(literal->isKind(ParseNodeKind::Object)); MOZ_ASSERT(propdef->isKind(ParseNodeKind::Colon)); if (!propdef->right()->isConstant()) { literal->setHasNonConstInitializer(); } - addList(/* list = */ literal, /* child = */ propdef); + addList(/* list = */ literal, /* kid = */ propdef); } MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node val) { BinaryNode* propdef = newPropertyDefinition(key, val); if (!propdef) { return false; } addPropertyDefinition(literal, propdef); @@ -375,63 +375,78 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) MOZ_ASSERT(expr->isKind(ParseNodeKind::Name)); MOZ_ASSERT(name->atom() == expr->atom()); literal->setHasNonConstInitializer(); BinaryNode* propdef = newBinary(ParseNodeKind::Shorthand, name, expr, JSOP_INITPROP); if (!propdef) { return false; } - addList(/* list = */ literal, /* child = */ propdef); + addList(/* list = */ literal, /* kid = */ propdef); return true; } MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { MOZ_ASSERT(literal->isKind(ParseNodeKind::Object)); literal->setHasNonConstInitializer(); ParseNode* spread = newSpread(begin, inner); if (!spread) { return false; } - addList(/* list = */ literal, /* child = */ spread); + addList(/* list = */ literal, /* kid = */ spread); return true; } MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key, CodeNodeType funNode, AccessorType atype) { literal->setHasNonConstInitializer(); checkAndSetIsDirectRHSAnonFunction(funNode); ParseNode* propdef = newObjectMethodOrPropertyDefinition(key, funNode, atype); if (!propdef) { return false; } - addList(/* list = */ literal, /* child = */ propdef); + addList(/* list = */ literal, /* kid = */ propdef); return true; } - MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key, + MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType memberList, Node key, CodeNodeType funNode, AccessorType atype, bool isStatic) { - MOZ_ASSERT(methodList->isKind(ParseNodeKind::ClassMethodList)); + MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList)); MOZ_ASSERT(isUsableAsObjectPropertyName(key)); checkAndSetIsDirectRHSAnonFunction(funNode); ClassMethod* classMethod = new_<ClassMethod>(key, funNode, AccessorTypeToJSOp(atype), isStatic); if (!classMethod) { return false; } - addList(/* list = */ methodList, /* child = */ classMethod); + addList(/* list = */ memberList, /* kid = */ classMethod); + return true; + } + + MOZ_MUST_USE bool addClassFieldDefinition(ListNodeType memberList, + Node name, Node initializer) + { + MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList)); + MOZ_ASSERT(isUsableAsObjectPropertyName(name)); + + ClassField* classField = new_<ClassField>(name, initializer); + + if (!classField) { + return false; + } + addList(/* list = */ memberList, /* kid = */ classField); return true; } UnaryNodeType newInitialYieldExpression(uint32_t begin, Node gen) { TokenPos pos(begin, begin + 1); return new_<UnaryNode>(ParseNodeKind::InitialYield, pos, gen); } @@ -461,34 +476,34 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) stmt = stmt->as<LabeledStatement>().statement(); } return stmt->isKind(ParseNodeKind::Function); } void addStatementToList(ListNodeType list, Node stmt) { MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList)); - addList(/* list = */ list, /* child = */ stmt); + addList(/* list = */ list, /* kid = */ stmt); if (isFunctionStmt(stmt)) { // Notify the emitter that the block contains body-level function // definitions that should be processed before the rest of nodes. list->setHasTopLevelFunctionDeclarations(); } } void setListEndPosition(ListNodeType list, const TokenPos& pos) { MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList)); list->pn_pos.end = pos.end; } void addCaseStatementToList(ListNodeType list, CaseClauseType caseClause) { MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList)); - addList(/* list = */ list, /* child = */ caseClause); + addList(/* list = */ list, /* kid = */ caseClause); if (caseClause->statementList()->hasTopLevelFunctionDeclarations()) { list->setHasTopLevelFunctionDeclarations(); } } MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) { MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList)); @@ -730,21 +745,21 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) funNode->setBody(paramsBody); } void setFunctionBox(CodeNodeType funNode, FunctionBox* funbox) { MOZ_ASSERT(funNode->isKind(ParseNodeKind::Function)); funNode->setFunbox(funbox); funbox->functionNode = funNode; } void addFunctionFormalParameter(CodeNodeType funNode, Node argpn) { - addList(/* list = */ funNode->body(), /* child = */ argpn); + addList(/* list = */ funNode->body(), /* kid = */ argpn); } void setFunctionBody(CodeNodeType funNode, LexicalScopeNodeType body) { MOZ_ASSERT(funNode->body()->isKind(ParseNodeKind::ParamsBody)); - addList(/* list = */ funNode->body(), /* child = */ body); + addList(/* list = */ funNode->body(), /* kid = */ body); } CodeNodeType newModule(const TokenPos& pos) { return new_<CodeNode>(ParseNodeKind::Module, JSOP_NOP, pos); } LexicalScopeNodeType newLexicalScope(LexicalScope::Data* bindings, Node body) { return new_<LexicalScopeNode>(bindings, body);
--- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -85,16 +85,17 @@ class NameResolver } if (!*foundName) { return true; } return appendPropertyReference(prop->right()->as<NameNode>().atom()); } case ParseNodeKind::Name: + case ParseNodeKind::PrivateName: *foundName = true; return buf->append(n->as<NameNode>().atom()); case ParseNodeKind::This: *foundName = true; return buf->append("this"); case ParseNodeKind::Elem: { @@ -143,16 +144,17 @@ class NameResolver for (int pos = nparents - 1; pos >= 0; pos--) { ParseNode* cur = parents[pos]; if (cur->is<AssignmentNode>()) { return cur; } switch (cur->getKind()) { + case ParseNodeKind::PrivateName: case ParseNodeKind::Name: return cur; /* found the initialized declaration */ case ParseNodeKind::This: return cur; /* Setting a property of 'this'. */ case ParseNodeKind::Function: return nullptr; /* won't find an assignment or declaration */ case ParseNodeKind::Return: /* * Normally the relevant parent of a node is its direct parent, but * sometimes with code like: @@ -442,16 +444,17 @@ class NameResolver MOZ_ASSERT(cur->is<BreakStatement>()); break; case ParseNodeKind::Continue: MOZ_ASSERT(cur->is<ContinueStatement>()); break; case ParseNodeKind::ObjectPropertyName: + case ParseNodeKind::PrivateName: // TODO(khyperia): Implement private field access. case ParseNodeKind::String: case ParseNodeKind::TemplateString: MOZ_ASSERT(cur->is<NameNode>()); break; case ParseNodeKind::RegExp: MOZ_ASSERT(cur->is<RegExpLiteral>()); break; @@ -535,16 +538,31 @@ class NameResolver return false; } if (!resolve(node->right(), prefix)) { return false; } break; } + case ParseNodeKind::ClassField: { + ClassField* node = &cur->as<ClassField>(); + if (!resolve(&node->name(), prefix)) { + return false; + } + + if (node->hasInitializer()) { + if (!resolve(&node->initializer(), prefix)) { + return false; + } + } + + break; + } + case ParseNodeKind::Elem: { PropertyByValue* elem = &cur->as<PropertyByValue>(); if (!elem->isSuper() && !resolve(&elem->expression(), prefix)) { return false; } if (!resolve(&elem->key(), prefix)) { return false; } @@ -696,17 +714,17 @@ class NameResolver MOZ_ASSERT(!innerBinding->initializer()); } #endif if (ParseNode* heritage = classNode->heritage()) { if (!resolve(heritage, prefix)) { return false; } } - if (!resolve(classNode->methodList(), prefix)) { + if (!resolve(classNode->memberList(), prefix)) { return false; } break; } // The condition and consequent are non-optional, but the alternative // might be omitted. case ParseNodeKind::If: { @@ -804,17 +822,17 @@ class NameResolver for (ParseNode* element : cur->as<ListNode>().contents()) { if (!resolve(element, prefix)) { return false; } } break; case ParseNodeKind::Object: - case ParseNodeKind::ClassMethodList: + case ParseNodeKind::ClassMemberList: for (ParseNode* element : cur->as<ListNode>().contents()) { if (!resolve(element, prefix)) { return false; } } break; // A template string list's contents alternate raw template string
--- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -169,16 +169,19 @@ ParseNode::dump(GenericPrinter& out, int as<CodeNode>().dump(out, indent); return; case PN_LIST: as<ListNode>().dump(out, indent); return; case PN_NAME: as<NameNode>().dump(out, indent); return; + case PN_FIELD: + as<ClassField>().dump(out, indent); + return; case PN_NUMBER: as<NumericLiteral>().dump(out, indent); return; case PN_REGEXP: as<RegExpLiteral>().dump(out, indent); return; case PN_LOOP: as<LoopControlStatement>().dump(out, indent); @@ -342,16 +345,17 @@ NameNode::dump(GenericPrinter& out, int switch (getKind()) { case ParseNodeKind::String: case ParseNodeKind::TemplateString: case ParseNodeKind::ObjectPropertyName: atom()->dumpCharsNoNewline(out); return; case ParseNodeKind::Name: + case ParseNodeKind::PrivateName: // atom() already includes the '#', no need to specially include it. case ParseNodeKind::PropertyName: if (!atom()) { out.put("#<null name>"); } else if (getOp() == JSOP_GETARG && atom()->length() == 0) { // Dump destructuring parameter. static const char ZeroLengthPrefix[] = "(#<zero-length name> "; constexpr size_t ZeroLengthPrefixLength = ArrayLength(ZeroLengthPrefix) - 1; out.put(ZeroLengthPrefix); @@ -384,16 +388,31 @@ NameNode::dump(GenericPrinter& out, int DumpParseTree(initializer(), out, indent); out.printf(")"); return; } } } void +ClassField::dump(GenericPrinter& out, int indent) +{ + out.printf("("); + if (hasInitializer()) { + indent += 2; + } + DumpParseTree(&name(), out, indent); + if (hasInitializer()) { + IndentNewLine(out, indent); + DumpParseTree(&initializer(), out, indent); + } + out.printf(")"); +} + +void LexicalScopeNode::dump(GenericPrinter& out, int indent) { const char* name = parseNodeNames[size_t(getKind())]; out.printf("(%s [", name); int nameIndent = indent + strlen(name) + 3; if (!isEmptyScope()) { LexicalScope::Data* bindings = scopeBindings(); for (uint32_t i = 0; i < bindings->length; i++) {
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -63,16 +63,17 @@ class ObjectBox; F(Elision, PN_NULLARY) \ F(StatementList, PN_LIST) \ F(Label, PN_NAME) \ F(Object, PN_LIST) \ F(Call, PN_BINARY) \ F(Arguments, PN_LIST) \ F(Name, PN_NAME) \ F(ObjectPropertyName, PN_NAME) \ + F(PrivateName, PN_NAME) \ F(ComputedName, PN_UNARY) \ F(Number, PN_NUMBER) \ F(String, PN_NAME) \ F(TemplateStringList, PN_LIST) \ F(TemplateString, PN_NAME) \ F(TaggedTemplate, PN_BINARY) \ F(CallSiteObj, PN_LIST) \ F(RegExp, PN_REGEXP) \ @@ -123,17 +124,18 @@ class ObjectBox; F(ForIn, PN_TERNARY) \ F(ForOf, PN_TERNARY) \ F(ForHead, PN_TERNARY) \ F(ParamsBody, PN_LIST) \ F(Spread, PN_UNARY) \ F(MutateProto, PN_UNARY) \ F(Class, PN_TERNARY) \ F(ClassMethod, PN_BINARY) \ - F(ClassMethodList, PN_LIST) \ + F(ClassField, PN_FIELD) \ + F(ClassMemberList, PN_LIST) \ F(ClassNames, PN_BINARY) \ F(NewTarget, PN_BINARY) \ F(PosHolder, PN_NULLARY) \ F(SuperBase, PN_UNARY) \ F(SuperCall, PN_BINARY) \ F(SetThis, PN_BINARY) \ F(ImportMeta, PN_BINARY) \ F(CallImport, PN_BINARY) \ @@ -248,25 +250,25 @@ IsTypeofKind(ParseNodeKind kind) * * Return for expression closure * count: number of formal parameters + 1 * Spread (UnaryNode) * kid: expression being spread * Class (ClassNode) * kid1: ClassNames for class name. can be null for anonymous class. * kid2: expression after `extends`. null if no expression * kid3: either of - * * ClassMethodList, if anonymous class - * * LexicalScopeNode which contains ClassMethodList as scopeBody, + * * ClassMemberList, if anonymous class + * * LexicalScopeNode which contains ClassMemberList as scopeBody, * if named class * 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 - * ClassMethodList (ListNode) - * head: list of N ClassMethod nodes + * ClassMemberList (ListNode) + * head: list of N ClassMethod or ClassField nodes * count: N >= 0 * ClassMethod (ClassMethod) * name: propertyName * method: methodDefinition * Module (CodeNode) * funbox: ? * body: ? * @@ -515,28 +517,30 @@ enum ParseNodeArity { PN_NULLARY, /* 0 kids */ PN_UNARY, /* one kid, plus a couple of scalars */ PN_BINARY, /* two kids, plus a couple of scalars */ PN_TERNARY, /* three kids */ PN_CODE, /* module or function definition node */ PN_LIST, /* generic singly linked list */ PN_NAME, /* name, label, string */ + PN_FIELD, /* field name, optional initializer */ PN_NUMBER, /* numeric literal */ PN_REGEXP, /* regexp literal */ PN_LOOP, /* loop control (break/continue) */ PN_SCOPE /* lexical scope */ }; // FIXME: Remove `*Type` (bug 1489008) #define FOR_EACH_PARSENODE_SUBCLASS(macro) \ macro(BinaryNode, BinaryNodeType, asBinary) \ 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) \ macro(PropertyByValue, PropertyByValueType, asPropertyByValue) \ macro(SwitchStatement, SwitchStatementType, asSwitchStatement) \ \ macro(CodeNode, CodeNodeType, asCode) \ \ @@ -710,16 +714,22 @@ class ParseNode private: friend class NameNode; JSAtom* atom; /* lexical name or label atom */ 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; ObjectBox* objbox; } regexp; struct { private: friend class CodeNode; FunctionBox* funbox; /* function object */ ParseNode* body; /* module or function body */ @@ -967,17 +977,17 @@ class BinaryNode : public ParseNode return pn_u.binary.left; } ParseNode* right() const { return pn_u.binary.right; } // Methods used by FoldConstants.cpp. - // caller are responsible for keeping the list consistent. + // callers are responsible for keeping the list consistent. ParseNode** unsafeLeftReference() { return &pn_u.binary.left; } ParseNode** unsafeRightReference() { return &pn_u.binary.right; } }; @@ -1174,34 +1184,34 @@ class ListNode : public ParseNode MOZ_MUST_USE bool hasArrayHoleOrSpread() const { MOZ_ASSERT(isKind(ParseNodeKind::Array)); return pn_u.list.xflags & hasArrayHoleOrSpreadBit; } MOZ_MUST_USE bool hasNonConstInitializer() const { MOZ_ASSERT(isKind(ParseNodeKind::Array) || isKind(ParseNodeKind::Object) || - isKind(ParseNodeKind::ClassMethodList)); + isKind(ParseNodeKind::ClassMemberList)); return pn_u.list.xflags & hasNonConstInitializerBit; } void setHasTopLevelFunctionDeclarations() { MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); pn_u.list.xflags |= hasTopLevelFunctionDeclarationsBit; } void setHasArrayHoleOrSpread() { MOZ_ASSERT(isKind(ParseNodeKind::Array)); pn_u.list.xflags |= hasArrayHoleOrSpreadBit; } void setHasNonConstInitializer() { MOZ_ASSERT(isKind(ParseNodeKind::Array) || isKind(ParseNodeKind::Object) || - isKind(ParseNodeKind::ClassMethodList)); + isKind(ParseNodeKind::ClassMemberList)); pn_u.list.xflags |= hasNonConstInitializerBit; } /* * Compute a pointer to the last element in a singly-linked list. NB: list * must be non-empty -- this is asserted! */ ParseNode* last() const { @@ -1955,16 +1965,58 @@ class ClassMethod : public BinaryNode return right()->as<CodeNode>(); } bool isStatic() const { return pn_u.binary.isStatic; } }; +class ClassField : public ParseNode +{ + public: + ClassField(ParseNode* name, ParseNode* initializer) + : ParseNode(ParseNodeKind::ClassField, JSOP_NOP, PN_FIELD, + initializer == nullptr ? name->pn_pos : TokenPos::box(name->pn_pos, initializer->pn_pos)) + { + pn_u.field.name = name; + pn_u.field.initializer = initializer; + } + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::ClassField); + } + + ParseNode& name() const { + return *pn_u.field.name; + } + + bool hasInitializer() const { + return pn_u.field.initializer != nullptr; + } + + ParseNode& initializer() const { + return *pn_u.field.initializer; + } + +#ifdef DEBUG + void dump(GenericPrinter& out, int indent); +#endif + + // Methods used by FoldConstants.cpp. + // callers are responsible for keeping the list consistent. + ParseNode** unsafeNameReference() { + return &pn_u.field.name; + } + + ParseNode** unsafeInitializerReference() { + return &pn_u.field.initializer; + } +}; + class SwitchStatement : public BinaryNode { public: SwitchStatement(uint32_t begin, ParseNode* discriminant, LexicalScopeNode* lexicalForCaseList, bool hasDefault) : BinaryNode(ParseNodeKind::Switch, JSOP_NOP, TokenPos(begin, lexicalForCaseList->pn_pos.end), discriminant, lexicalForCaseList) @@ -2041,45 +2093,45 @@ class ClassNames : public BinaryNode NameNode* innerBinding() const { return &right()->as<NameNode>(); } }; class ClassNode : public TernaryNode { public: - ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock, + ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* membersOrBlock, const TokenPos& pos) - : TernaryNode(ParseNodeKind::Class, names, heritage, methodsOrBlock, pos) + : TernaryNode(ParseNodeKind::Class, names, heritage, membersOrBlock, pos) { MOZ_ASSERT_IF(names, names->is<ClassNames>()); - MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() || - methodsOrBlock->isKind(ParseNodeKind::ClassMethodList)); + MOZ_ASSERT(membersOrBlock->is<LexicalScopeNode>() || + membersOrBlock->isKind(ParseNodeKind::ClassMemberList)); } static bool test(const ParseNode& node) { bool match = node.isKind(ParseNodeKind::Class); MOZ_ASSERT_IF(match, node.is<TernaryNode>()); return match; } ClassNames* names() const { return kid1() ? &kid1()->as<ClassNames>() : nullptr; } ParseNode* heritage() const { return kid2(); } - ListNode* methodList() const { - ParseNode* methodsOrBlock = kid3(); - if (methodsOrBlock->isKind(ParseNodeKind::ClassMethodList)) { - return &methodsOrBlock->as<ListNode>(); + ListNode* memberList() const { + ParseNode* membersOrBlock = kid3(); + if (membersOrBlock->isKind(ParseNodeKind::ClassMemberList)) { + return &membersOrBlock->as<ListNode>(); } - ListNode* list = &methodsOrBlock->as<LexicalScopeNode>().scopeBody()->as<ListNode>(); - MOZ_ASSERT(list->isKind(ParseNodeKind::ClassMethodList)); + ListNode* list = &membersOrBlock->as<LexicalScopeNode>().scopeBody()->as<ListNode>(); + MOZ_ASSERT(list->isKind(ParseNodeKind::ClassMemberList)); return list; } Handle<LexicalScope::Data*> scopeBindings() const { ParseNode* scope = kid3(); return scope->as<LexicalScopeNode>().scopeBindings(); } };
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -5012,17 +5012,17 @@ GeneralParser<ParseHandler, Unit>::objec if (!handler.addSpreadProperty(literal, begin, inner)) { return null(); } } else { TokenPos namePos = anyChars.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) { // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. if (!tokenStream.getToken(&tt, TokenStream::Operand)) { @@ -8022,18 +8022,18 @@ GeneralParser<ParseHandler, Unit>::class classHeritage = memberExpr(yieldHandling, TripledotProhibited, tt); if (!classHeritage) { return null(); } } MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CLASS); - ListNodeType classMethods = handler.newClassMethodList(pos().begin); - if (!classMethods) { + ListNodeType classMembers = handler.newClassMemberList(pos().begin); + if (!classMembers) { return null(); } Maybe<DeclarationKind> declKind = Nothing(); for (;;) { TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); @@ -8067,21 +8067,54 @@ GeneralParser<ParseHandler, Unit>::class } uint32_t nameOffset; if (!tokenStream.peekOffset(&nameOffset)) { return null(); } PropertyType propType; - Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom); + Node propName = propertyName(yieldHandling, PropertyNameInClass, declKind, classMembers, &propType, &propAtom); if (!propName) { return null(); } + if (propType == PropertyType::Field) { + if (isStatic) { + errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); + return null(); + } + if (!tokenStream.getToken(&tt)) { + return null(); + } + Node initializer = null(); + if (tt == TokenKind::Assign) { + initializer = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!tokenStream.getToken(&tt)) { + return null(); + } + } + + // TODO(khyperia): Implement ASI + if (tt != TokenKind::Semi) { + error(JSMSG_MISSING_SEMI_FIELD); + return null(); + } + + if (!handler.addClassFieldDefinition(classMembers, propName, initializer)) { + return null(); + } + + // TODO(khyperia): Change the below to `continue;` once fields are + // fully supported in the backend. We can't fail in BytecodeCompiler + // because of lazy parsing. + errorAt(nameOffset, JSMSG_FIELDS_NOT_SUPPORTED); + return null(); + } + if (propType != PropertyType::Getter && propType != PropertyType::Setter && propType != PropertyType::Method && propType != PropertyType::GeneratorMethod && propType != PropertyType::AsyncMethod && propType != PropertyType::AsyncGeneratorMethod) { errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } @@ -8128,50 +8161,50 @@ GeneralParser<ParseHandler, Unit>::class // parsing and will be amended when class parsing finishes below. CodeNodeType funNode = methodDefinition(isConstructor ? classStartOffset : nameOffset, propType, funName); if (!funNode) { return null(); } AccessorType atype = ToAccessorType(propType); - if (!handler.addClassMethodDefinition(classMethods, propName, funNode, atype, isStatic)) { + if (!handler.addClassMethodDefinition(classMembers, propName, funNode, atype, 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; } Node nameNode = null(); - Node methodsOrBlock = classMethods; + Node membersOrBlock = classMembers; if (name) { // The inner name is immutable. if (!noteDeclaredName(name, DeclarationKind::Const, namePos)) { return null(); } NameNodeType innerName = newName(name, namePos); if (!innerName) { return null(); } - Node classBlock = finishLexicalScope(*innerScope, classMethods); + Node classBlock = finishLexicalScope(*innerScope, classMembers); if (!classBlock) { return null(); } - methodsOrBlock = classBlock; + membersOrBlock = classBlock; // Pop the inner scope. innerScope.reset(); innerScopeStmt.reset(); NameNodeType outerName = null(); if (classContext == ClassStatement) { // The outer name is mutable. @@ -8188,17 +8221,17 @@ GeneralParser<ParseHandler, Unit>::class nameNode = handler.newClassNames(outerName, innerName, namePos); if (!nameNode) { return null(); } } MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness)); - return handler.newClass(nameNode, classHeritage, methodsOrBlock, + return handler.newClass(nameNode, classHeritage, membersOrBlock, TokenPos(classStartOffset, classEndOffset)); } bool ParserBase::nextTokenContinuesLetDeclaration(TokenKind next) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Let)); MOZ_ASSERT(anyChars.nextToken().type == next); @@ -9599,16 +9632,17 @@ GeneralParser<ParseHandler, Unit>::membe break; } Node nextMember; if (tt == TokenKind::Dot) { if (!tokenStream.getToken(&tt)) { return null(); } + if (TokenKindIsPossibleIdentifierName(tt)) { PropertyName* field = anyChars.currentName(); if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) { error(JSMSG_BAD_SUPERPROP, "property"); return null(); } NameNodeType name = handler.newPropertyName(field, pos()); @@ -9812,17 +9846,17 @@ GeneralParser<ParseHandler, Unit>::check TokenKind tt; if (hint == TokenKind::Limit) { tt = ReservedWordTokenKind(ident); } else { MOZ_ASSERT(hint == ReservedWordTokenKind(ident), "hint doesn't match actual token kind"); tt = hint; } - if (tt == TokenKind::Name) { + if (tt == TokenKind::Name || tt == TokenKind::PrivateName) { return true; } if (TokenKindIsContextualKeyword(tt)) { if (tt == TokenKind::Yield) { if (yieldHandling == YieldIsKeyword) { errorAt(offset, JSMSG_RESERVED_ID, "yield"); return false; } @@ -10270,20 +10304,21 @@ GeneralParser<ParseHandler, Unit>::array handler.setEndPosition(literal, pos().end); return literal; } template <class ParseHandler, typename Unit> typename ParseHandler::Node GeneralParser<ParseHandler, Unit>::propertyName(YieldHandling yieldHandling, - const Maybe<DeclarationKind>& maybeDecl, - ListNodeType propList, - PropertyType* propType, - MutableHandleAtom propAtom) + PropertyNameContext propertyNameContext, + const Maybe<DeclarationKind>& maybeDecl, + ListNodeType propList, + PropertyType* propType, + MutableHandleAtom propAtom) { TokenKind ltok; if (!tokenStream.getToken(<ok)) { return null(); } MOZ_ASSERT(ltok != TokenKind::RightCurly, "caller should have handled TokenKind::RightCurly"); @@ -10444,16 +10479,26 @@ GeneralParser<ParseHandler, Unit>::prope if (isGenerator || isAsync) { error(JSMSG_BAD_PROP_ID); return null(); } *propType = PropertyType::Normal; return propName; } + if (propertyNameContext == PropertyNameInClass && (tt == TokenKind::Semi || tt == TokenKind::Assign)) { + if (isGenerator || isAsync) { + error(JSMSG_BAD_PROP_ID); + return null(); + } + anyChars.ungetToken(); + *propType = PropertyType::Field; + return propName; + } + if (TokenKindIsPossibleIdentifierName(ltok) && (tt == TokenKind::Comma || tt == TokenKind::RightCurly || tt == TokenKind::Assign)) { if (isGenerator || isAsync) { error(JSMSG_BAD_PROP_ID); return null(); } @@ -10562,17 +10607,17 @@ GeneralParser<ParseHandler, Unit>::objec } if (!handler.addSpreadProperty(literal, begin, inner)) { return null(); } } else { TokenPos namePos = anyChars.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(); } if (propType == PropertyType::Normal) { TokenPos exprPos; if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand)) { return null();
--- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -235,17 +235,18 @@ enum class PropertyType { CoverInitializedName, Getter, Setter, Method, GeneratorMethod, AsyncMethod, AsyncGeneratorMethod, Constructor, - DerivedConstructor + DerivedConstructor, + Field, }; enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword }; template <class ParseHandler, typename Unit> class AutoAwaitIsKeyword; template <class ParseHandler, typename Unit> @@ -1238,17 +1239,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE uint32_t prevPos); bool notePositionalFormalParameter(CodeNodeType funNode, HandlePropertyName name, uint32_t beginPos, bool disallowDuplicateParams, bool* duplicatedParam); bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt, DeclarationKind kind, TokenPos pos); + 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, const mozilla::Maybe<DeclarationKind>& maybeDecl, ListNodeType literal); ListNodeType arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError); inline RegExpLiteralType newRegExp();
--- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -274,17 +274,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) ListNodeType newArguments(const TokenPos& pos) { return NodeGeneric; } BinaryNodeType newCall(Node callee, Node args) { return NodeFunctionCall; } BinaryNodeType newSuperCall(Node callee, Node args) { return NodeGeneric; } BinaryNodeType newTaggedTemplate(Node tag, 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; } LexicalScopeNodeType newLexicalScope(Node body) { return NodeLexicalDeclaration; } @@ -300,21 +300,25 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {} MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node expr) { return true; } 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 key, CodeNodeType funNode, AccessorType atype) { return true; } - MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key, + MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType memberList, Node key, CodeNodeType funNode, AccessorType atype, bool isStatic) { return true; } + MOZ_MUST_USE bool addClassFieldDefinition(ListNodeType memberList, + Node name, Node 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; } // Statements ListNodeType newStatementList(const TokenPos& pos) { return NodeGeneric; } void addStatementToList(ListNodeType list, Node stmt) {}
--- a/js/src/frontend/TokenKind.h +++ b/js/src/frontend/TokenKind.h @@ -68,16 +68,17 @@ macro(TripleDot, "'...'") /* rest arguments and spread operator */ \ macro(LeftBracket, "'['") \ macro(RightBracket, "']'") \ macro(LeftCurly, "'{'") \ macro(RightCurly, "'}'") \ macro(LeftParen, "'('") \ macro(RightParen, "')'") \ macro(Name, "identifier") \ + macro(PrivateName, "private identifier") \ macro(Number, "numeric literal") \ macro(String, "string literal") \ \ /* start of template literal with substitutions */ \ macro(TemplateHead, "'${'") \ /* template literal without substitutions */ \ macro(NoSubsTemplate, "template literal") \ \ @@ -317,16 +318,17 @@ TokenKindIsReservedWord(TokenKind tt) TokenKindIsFutureReservedWord(tt) || TokenKindIsReservedWordLiteral(tt); } inline MOZ_MUST_USE bool TokenKindIsPossibleIdentifier(TokenKind tt) { return tt == TokenKind::Name || + tt == TokenKind::PrivateName || TokenKindIsContextualKeyword(tt) || TokenKindIsStrictReservedWord(tt); } inline MOZ_MUST_USE bool TokenKindIsPossibleIdentifierName(TokenKind tt) { return TokenKindIsPossibleIdentifier(tt) ||
--- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -114,46 +114,38 @@ FindReservedWord(const CharT* s, size_t template <> MOZ_ALWAYS_INLINE const ReservedWordInfo* FindReservedWord<Utf8Unit>(const Utf8Unit* units, size_t length) { return FindReservedWord(Utf8AsUnsignedChars(units), 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()); -} - -template <typename CharT> -static bool -IsIdentifierImpl(const CharT* chars, size_t length) -{ - using namespace js; - - if (length == 0) { - return false; + 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); } - if (!unicode::IsIdentifierStart(char16_t(*chars))) { - return false; + const char16_t* chars = str->twoByteChars(nogc); + size_t length = str->length(); + if (length > 0 && chars[0] == '#') { + *visibility = js::frontend::NameVisibility::Private; + return nullptr; } - - const CharT* end = chars + length; - while (++chars != end) { - if (!unicode::IsIdentifierPart(char16_t(*chars))) { - return false; - } - } - - return true; + *visibility = js::frontend::NameVisibility::Public; + return FindReservedWord(chars, length); } static uint32_t GetSingleCodePoint(const char16_t** p, const char16_t* end) { using namespace js; uint32_t codePoint; @@ -166,25 +158,81 @@ GetSingleCodePoint(const char16_t** p, c } } codePoint = **p; (*p)++; return codePoint; } -static bool -IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length) +namespace js { + +namespace frontend { + +bool +IsIdentifier(JSLinearString* str) +{ + JS::AutoCheckCannotGC nogc; + MOZ_ASSERT(str); + if (str->hasLatin1Chars()) { + return IsIdentifier(str->latin1Chars(nogc), str->length()); + } + return IsIdentifier(str->twoByteChars(nogc), str->length()); +} + +bool +IsIdentifierNameOrPrivateName(JSLinearString* str) +{ + JS::AutoCheckCannotGC nogc; + MOZ_ASSERT(str); + if (str->hasLatin1Chars()) { + return IsIdentifierNameOrPrivateName(str->latin1Chars(nogc), str->length()); + } + return IsIdentifierNameOrPrivateName(str->twoByteChars(nogc), str->length()); +} + +bool +IsIdentifier(const Latin1Char* chars, size_t length) { - using namespace js; - - if (IsIdentifierImpl(chars, length)) { - return true; + if (length == 0) { + return false; + } + + if (!unicode::IsIdentifierStart(char16_t(*chars))) { + return false; + } + + const Latin1Char* end = chars + length; + while (++chars != end) { + if (!unicode::IsIdentifierPart(char16_t(*chars))) { + return false; + } } + return true; +} + +bool +IsIdentifierNameOrPrivateName(const Latin1Char* chars, size_t length) +{ + if (length == 0) { + return false; + } + + if (char16_t(*chars) == '#') { + ++chars; + --length; + } + + return IsIdentifier(chars, length); +} + +bool +IsIdentifier(const char16_t* chars, size_t length) +{ if (length == 0) { return false; } const char16_t* p = chars; const char16_t* end = chars + length; uint32_t codePoint; @@ -198,67 +246,77 @@ IsIdentifierMaybeNonBMP(const char16_t* if (!unicode::IsIdentifierPart(codePoint)) { return false; } } return true; } -namespace js { - -namespace frontend { - bool -IsIdentifier(JSLinearString* str) +IsIdentifierNameOrPrivateName(const char16_t* chars, size_t length) { - JS::AutoCheckCannotGC nogc; - MOZ_ASSERT(str); - if (str->hasLatin1Chars()) { - return ::IsIdentifierImpl(str->latin1Chars(nogc), str->length()); + if (length == 0) { + return false; } - return ::IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length()); -} - -bool -IsIdentifier(const char* chars, size_t length) -{ - return ::IsIdentifierImpl(chars, length); -} - -bool -IsIdentifier(const char16_t* chars, size_t length) -{ - return ::IsIdentifierImpl(chars, length); + + const char16_t* p = chars; + const char16_t* end = chars + length; + uint32_t codePoint; + + codePoint = GetSingleCodePoint(&p, end); + if (codePoint == '#') { + if (length == 1) { + return false; + } + + codePoint = GetSingleCodePoint(&p, end); + } + + if (!unicode::IsIdentifierStart(codePoint)) { + return false; + } + + while (p < end) { + codePoint = GetSingleCodePoint(&p, end); + if (!unicode::IsIdentifierPart(codePoint)) { + return false; + } + } + + return true; } bool 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 ReservedWordTokenKind(PropertyName* str) { - if (const ReservedWordInfo* rw = FindReservedWord(str)) { + NameVisibility visibility; + if (const ReservedWordInfo* rw = FindReservedWord(str, &visibility)) { return rw->tokentype; } - return TokenKind::Name; + return visibility == NameVisibility::Private ? TokenKind::PrivateName : TokenKind::Name; } const char* 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* ReservedWordToCharZ(TokenKind tt) @@ -1656,16 +1714,52 @@ GeneralTokenStreamChars<Unit, AnyCharsAc this->sourceUnits.unskipCodeUnits(length); } MOZ_ASSERT(this->sourceUnits.previousCodeUnit() == Unit('\\')); return false; } template<typename Unit, class AnyCharsAccess> +MOZ_MUST_USE bool +TokenStreamSpecific<Unit, AnyCharsAccess>::matchIdentifierStart(IdentifierEscapes* sawEscape) +{ + int32_t unit = getCodeUnit(); + if (unicode::IsIdentifierStart(char16_t(unit))) { + *sawEscape = IdentifierEscapes::None; + return true; + } + + if (unit == '\\') { + *sawEscape = IdentifierEscapes::SawUnicodeEscape; + + uint32_t codePoint; + uint32_t escapeLength = matchUnicodeEscapeIdStart(&codePoint); + if (escapeLength != 0) { + return true; + } + + // We could point "into" a mistyped escape, e.g. for "\u{41H}" we + // could point at the 'H'. But we don't do that now, so the code + // unit after the '\' isn't necessarily bad, so just point at the + // start of the actually-invalid escape. + ungetCodeUnit('\\'); + error(JSMSG_BAD_ESCAPE); + return false; + } + + *sawEscape = IdentifierEscapes::None; + + // NOTE: |unit| may be EOF here. + ungetCodeUnit(unit); + error(JSMSG_MISSING_PRIVATE_NAME); + return false; +} + +template<typename Unit, class AnyCharsAccess> bool TokenStreamSpecific<Unit, AnyCharsAccess>::getDirectives(bool isMultiline, bool shouldWarnDeprecated) { // Match directive comments used in debugging, such as "//# sourceURL" and // "//# sourceMappingURL". Use of "//@" instead of "//#" is deprecated. // // To avoid a crashing bug in IE, several JavaScript transpilers wrap single @@ -1926,19 +2020,21 @@ TokenStreamSpecific<Unit, AnyCharsAccess } while (true); return true; } template<typename Unit, class AnyCharsAccess> MOZ_MUST_USE bool TokenStreamSpecific<Unit, AnyCharsAccess>::identifierName(TokenStart start, - const Unit* identStart, - IdentifierEscapes escaping, - Modifier modifier, TokenKind* out) + const Unit* identStart, + IdentifierEscapes escaping, + Modifier modifier, + NameVisibility visibility, + TokenKind* out) { // Run the bad-token code for every path out of this function except the // two success-cases. auto noteBadToken = MakeScopeExit([this]() { this->badToken(); }); // We've already consumed an initial code point in the identifer, to *know* @@ -1990,31 +2086,43 @@ TokenStreamSpecific<Unit, AnyCharsAccess } atom = drainCharBufferIntoAtom(anyCharsAccess().cx); } else { // Escape-free identifiers can be created directly from sourceUnits. const Unit* chars = identStart; size_t length = this->sourceUnits.addressOfNextCodeUnit() - identStart; - // Represent reserved words lacking escapes as reserved word tokens. - if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) { - noteBadToken.release(); - newSimpleToken(rw->tokentype, start, modifier, out); - return true; + // Private identifiers start with a '#', and so cannot be reserved words. + if (visibility == NameVisibility::Public) { + // Represent reserved words lacking escapes as reserved word tokens. + if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) { + noteBadToken.release(); + newSimpleToken(rw->tokentype, start, modifier, out); + return true; + } } atom = atomizeSourceChars(anyCharsAccess().cx, MakeSpan(chars, length)); } if (!atom) { return false; } noteBadToken.release(); - newNameToken(atom->asPropertyName(), start, modifier, out); + if (visibility == NameVisibility::Private) { + MOZ_ASSERT(identStart[0] == static_cast<Unit>('#'), "Private identifier starts with #"); + newPrivateNameToken(atom->asPropertyName(), start, modifier, out); + + // TODO(khypera): Delete the below once private names are supported. + errorAt(start.offset(), JSMSG_FIELDS_NOT_SUPPORTED); + return false; + } else { + newNameToken(atom->asPropertyName(), start, modifier, out); + } return true; } enum FirstCharKind { // A char16_t has the 'OneChar' kind if it, by itself, constitutes a valid // token that cannot also be a prefix of a longer token. E.g. ';' has the // OneChar kind, but '+' does not, because '++' and '+=' are valid longer tokens // that begin with '+'. @@ -2435,17 +2543,17 @@ TokenStreamSpecific<Unit, AnyCharsAccess if (MOZ_LIKELY(unicode::IsUnicodeIDStart(cp))) { this->sourceUnits.consumeKnownCodePoint(peeked); MOZ_ASSERT(!IsLineTerminator(cp), "IdentifierStart must guarantee !IsLineTerminator " "or else we'll fail to maintain line-info/flags " "for EOL here"); - return identifierName(start, identStart, IdentifierEscapes::None, modifier, ttp); + return identifierName(start, identStart, IdentifierEscapes::None, modifier, NameVisibility::Public, ttp); } error(JSMSG_ILLEGAL_CHARACTER); return badToken(); } // !isAsciiCodePoint(unit) consumeKnownCodeUnit(unit); @@ -2484,17 +2592,18 @@ TokenStreamSpecific<Unit, AnyCharsAccess continue; } // Look for an identifier. // if (c1kind == Ident) { TokenStart start(this->sourceUnits, -1); return identifierName(start, this->sourceUnits.addressOfNextCodeUnit() - 1, - IdentifierEscapes::None, modifier, ttp); + IdentifierEscapes::None, modifier, + NameVisibility::Public, ttp); } // Look for a decimal number. // if (c1kind == Dec) { TokenStart start(this->sourceUnits, -1); const Unit* numStart = this->sourceUnits.addressOfNextCodeUnit() - 1; return decimalNumber(unit, start, numStart, modifier, ttp); @@ -2677,16 +2786,26 @@ TokenStreamSpecific<Unit, AnyCharsAccess // NOTE: |unit| may be EOF here. A stray '.' at EOF would be an // error, but subsequent code will handle it. ungetCodeUnit(unit); simpleKind = TokenKind::Dot; break; + case '#': { + TokenStart start(this->sourceUnits, -1); + const Unit* identStart = this->sourceUnits.addressOfNextCodeUnit() - 1; + IdentifierEscapes sawEscape; + if (!matchIdentifierStart(&sawEscape)) { + return badToken(); + } + return identifierName(start, identStart, sawEscape, modifier, NameVisibility::Private, ttp); + } + case '=': if (matchCodeUnit('=')) { simpleKind = matchCodeUnit('=') ? TokenKind::StrictEq : TokenKind::Eq; } else if (matchCodeUnit('>')) { simpleKind = TokenKind::Arrow; } else { simpleKind = TokenKind::Assign; } @@ -2700,17 +2819,17 @@ TokenStreamSpecific<Unit, AnyCharsAccess } break; case '\\': { uint32_t codePoint; if (uint32_t escapeLength = matchUnicodeEscapeIdStart(&codePoint)) { return identifierName(start, this->sourceUnits.addressOfNextCodeUnit() - escapeLength - 1, - IdentifierEscapes::SawUnicodeEscape, modifier, ttp); + IdentifierEscapes::SawUnicodeEscape, modifier, NameVisibility::Public, ttp); } // We could point "into" a mistyped escape, e.g. for "\u{41H}" we // could point at the 'H'. But we don't do that now, so the code // unit after the '\' isn't necessarily bad, so just point at the // start of the actually-invalid escape. ungetCodeUnit('\\'); error(JSMSG_BAD_ESCAPE);
--- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -284,16 +284,18 @@ enum class InvalidEscapeType { UnicodeOverflow, // An octal escape in a template token. Octal }; // The only escapes found in IdentifierName are of the Unicode flavor. enum class IdentifierEscapes { None, SawUnicodeEscape }; +enum class NameVisibility { Public, Private }; + class TokenStreamShared; struct Token { private: // Sometimes the parser needs to inform the tokenizer to interpret // subsequent text in a particular manner: for example, to tokenize a // keyword as an identifier, not as the actual keyword, on the right-hand @@ -401,17 +403,17 @@ struct Token * situations. */ ModifierException modifierException; #endif // Mutators void setName(PropertyName* name) { - MOZ_ASSERT(type == TokenKind::Name); + MOZ_ASSERT(type == TokenKind::Name || type == TokenKind::PrivateName); u.name = name; } void setAtom(JSAtom* atom) { MOZ_ASSERT(type == TokenKind::String || type == TokenKind::TemplateHead || type == TokenKind::NoSubsTemplate); u.atom = atom; @@ -427,17 +429,17 @@ struct Token MOZ_ASSERT(type == TokenKind::Number); u.number.value = n; u.number.decimalPoint = decimalPoint; } // Type-safe accessors PropertyName* name() const { - MOZ_ASSERT(type == TokenKind::Name); + MOZ_ASSERT(type == TokenKind::Name || type == TokenKind::PrivateName); return u.name->JSAtom::asPropertyName(); // poor-man's type verification } JSAtom* atom() const { MOZ_ASSERT(type == TokenKind::String || type == TokenKind::TemplateHead || type == TokenKind::NoSubsTemplate); return u.atom; @@ -622,26 +624,26 @@ class TokenStreamAnyChars MOZ_MUST_USE bool checkOptions(); private: PropertyName* reservedWordToPropertyName(TokenKind tt) const; public: PropertyName* currentName() const { - if (isCurrentTokenType(TokenKind::Name)) { + if (isCurrentTokenType(TokenKind::Name) || isCurrentTokenType(TokenKind::PrivateName)) { return currentToken().name(); } MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type)); return reservedWordToPropertyName(currentToken().type); } bool currentNameHasEscapes() const { - if (isCurrentTokenType(TokenKind::Name)) { + if (isCurrentTokenType(TokenKind::Name) || isCurrentTokenType(TokenKind::PrivateName)) { TokenPos pos = currentToken().pos; return (pos.end - pos.begin) != currentToken().name()->length(); } MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type)); return false; } @@ -1922,16 +1924,25 @@ class GeneralTokenStreamChars void newNameToken(PropertyName* name, TokenStart start, TokenStreamShared::Modifier modifier, TokenKind* out) { Token* token = newToken(TokenKind::Name, start, modifier, out); token->setName(name); } + void newPrivateNameToken(PropertyName* name, + TokenStart start, + TokenStreamShared::Modifier modifier, + TokenKind* out) + { + Token* token = newToken(TokenKind::PrivateName, start, modifier, out); + token->setName(name); + } + void newRegExpToken(RegExpFlag reflags, TokenStart start, TokenKind* out) { Token* token = newToken(TokenKind::RegExp, start, TokenStreamShared::Operand, out); token->setRegExpFlags(reflags); } MOZ_COLD bool badToken(); @@ -1995,16 +2006,17 @@ class GeneralTokenStreamChars } MOZ_MUST_USE MOZ_ALWAYS_INLINE bool updateLineInfoForEOL() { return anyCharsAccess().internalUpdateLineInfoForEOL(this->sourceUnits.offset()); } uint32_t matchUnicodeEscapeIdStart(uint32_t* codePoint); bool matchUnicodeEscapeIdent(uint32_t* codePoint); + bool matchIdentifierStart(); /** * If possible, compute a line of context for an otherwise-filled-in |err| * at the given offset in this token stream. * * This function is very-internal: almost certainly you should use one of * its callers instead. It basically exists only to make those callers * more readable. @@ -2330,16 +2342,17 @@ class MOZ_STACK_CLASS TokenStreamSpecifi using GeneralCharsBase::internalComputeLineOfContext; using TokenStreamCharsShared::isAsciiCodePoint; using CharsBase::matchCodeUnit; using CharsBase::matchLineTerminator; using GeneralCharsBase::matchUnicodeEscapeIdent; using GeneralCharsBase::matchUnicodeEscapeIdStart; using GeneralCharsBase::newAtomToken; using GeneralCharsBase::newNameToken; + using GeneralCharsBase::newPrivateNameToken; using GeneralCharsBase::newNumberToken; using GeneralCharsBase::newRegExpToken; using GeneralCharsBase::newSimpleToken; using CharsBase::peekCodeUnit; // Deliberately don't |using| |sourceUnits| because of bug 1472569. :-( using CharsBase::toUnit; using GeneralCharsBase::ungetCodeUnit; using GeneralCharsBase::updateLineInfoForEOL; @@ -2665,17 +2678,19 @@ class MOZ_STACK_CLASS TokenStreamSpecifi } const Unit* rawLimit() const { return this->sourceUnits.limit(); } MOZ_MUST_USE bool identifierName(TokenStart start, const Unit* identStart, IdentifierEscapes escaping, Modifier modifier, - TokenKind* out); + NameVisibility visibility, TokenKind* out); + + MOZ_MUST_USE bool matchIdentifierStart(IdentifierEscapes* sawEscape); MOZ_MUST_USE bool getTokenInternal(TokenKind* const ttp, const Modifier modifier); MOZ_MUST_USE bool getStringOrTemplateToken(char untilChar, Modifier modifier, TokenKind* out); MOZ_MUST_USE bool getDirectives(bool isMultiline, bool shouldWarnDeprecated); MOZ_MUST_USE bool getDirective(bool isMultiline, bool shouldWarnDeprecated, const char* directive, uint8_t directiveLength,
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -260,16 +260,17 @@ MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0 MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "finally without try") MSG_DEF(JSMSG_FORBIDDEN_AS_STATEMENT, 1, JSEXN_SYNTAXERR, "{0} can't appear in single-statement context") MSG_DEF(JSMSG_FOR_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "for await (... of ...) is only valid in async functions and async generators") MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause") 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_BAD_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid escape sequence") +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") MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL, 0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer") MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer") MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers") MSG_DEF(JSMSG_INVALID_ID, 1, JSEXN_SYNTAXERR, "{0} is an invalid identifier") MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found") @@ -350,16 +351,18 @@ MSG_DEF(JSMSG_VAR_HIDES_ARG, 1 MSG_DEF(JSMSG_WHILE_AFTER_DO, 0, JSEXN_SYNTAXERR, "missing while after do-loop body") MSG_DEF(JSMSG_YIELD_IN_PARAMETER, 0, JSEXN_SYNTAXERR, "yield expression can't be used in parameter") MSG_DEF(JSMSG_YIELD_OUTSIDE_GENERATOR, 0, JSEXN_SYNTAXERR, "yield expression is only valid in generators") MSG_DEF(JSMSG_BAD_COLUMN_NUMBER, 0, JSEXN_RANGEERR, "column number out of range") MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property names aren't supported in this destructuring declaration") MSG_DEF(JSMSG_DEFAULT_IN_PATTERN, 0, JSEXN_SYNTAXERR, "destructuring defaults aren't supported in this destructuring declaration") MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allowed within functions") 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") // UTF-8 source text encoding errors MSG_DEF(JSMSG_BAD_LEADING_UTF8_UNIT, 1, JSEXN_SYNTAXERR, "{0} byte doesn't begin a valid UTF-8 code point") MSG_DEF(JSMSG_NOT_ENOUGH_CODE_UNITS, 4, JSEXN_SYNTAXERR, "{0} byte in UTF-8 must be followed by {1} bytes, but {2} byte{3} present") MSG_DEF(JSMSG_BAD_TRAILING_UTF8_UNIT, 1, JSEXN_SYNTAXERR, "bad trailing UTF-8 byte {0} doesn't match the pattern 0b10xxxxxx") MSG_DEF(JSMSG_FORBIDDEN_UTF8_CODE_POINT,2,JSEXN_SYNTAXERR, "{0} isn't a valid code point because {1}") MSG_DEF(JSMSG_BAD_CODE_UNITS, 1, JSEXN_NOTE, "the code units comprising this invalid code point were: {0}")
--- a/js/src/jsast.tbl +++ b/js/src/jsast.tbl @@ -73,9 +73,10 @@ ASTDEF(AST_OBJECT_PATT, "Objec ASTDEF(AST_PROP_PATT, "Property", "propertyPattern") ASTDEF(AST_TEMPLATE_LITERAL, "TemplateLiteral", "templateLiteral") ASTDEF(AST_TAGGED_TEMPLATE, "TaggedTemplate", "taggedTemplate") ASTDEF(AST_CALL_SITE_OBJ, "CallSiteObject", "callSiteObject") ASTDEF(AST_COMPUTED_NAME, "ComputedName", "computedName") ASTDEF(AST_CLASS_STMT, "ClassStatement", "classStatement") ASTDEF(AST_CLASS_METHOD, "ClassMethod", "classMethod") +ASTDEF(AST_CLASS_FIELD, "ClassField", "classField") /* AST_LIMIT = last + 1 */
--- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -456,16 +456,24 @@ skip script test262/intl402/RelativeTime # https://bugzilla.mozilla.org/show_bug.cgi?id=1483548 skip script test262/intl402/RelativeTimeFormat/prototype/format/value-non-finite.js # https://bugzilla.mozilla.org/show_bug.cgi?id=1499933 skip script test262/intl402/DateTimeFormat/prototype/resolvedOptions/order.js skip script test262/intl402/PluralRules/prototype/resolvedOptions/order.js skip script test262/intl402/NumberFormat/prototype/resolvedOptions/order.js +# Fields are not fully implemented yet +skip script non262/fields/access.js +skip script non262/fields/basic.js +skip script non262/fields/error.js +skip script non262/fields/field_types.js +skip script non262/fields/literal.js +skip script non262/fields/mixed_methods.js +skip script non262/fields/quirks.js ########################################################### # Tests disabled due to issues in test262 importer script # ########################################################### # test262 importer merges all includes in a per directory shell.js file, breaking this harness test case. skip script test262/harness/detachArrayBuffer.js
new file mode 100644 --- /dev/null +++ b/js/src/tests/non262/fields/access.js @@ -0,0 +1,19 @@ +// * * * THIS TEST IS DISABLED - Fields are not fully implemented yet + +class C { + x = 5; +} + +c = new C(); + +reportCompare(c.x, undefined); // TODO +//reportCompare(c.x, 5); + +class D { + #y = 5; +} + +d = new D(); + +reportCompare(d.#y, undefined); // TODO +//reportCompare(d.#y, 5);
new file mode 100644 --- /dev/null +++ b/js/src/tests/non262/fields/basic.js @@ -0,0 +1,14 @@ +// * * * THIS TEST IS DISABLED - Fields are not fully implemented yet + +class C { + x; + y = 2; +} + +class D { + #x; + #y = 2; +} + +if (typeof reportCompare === "function") + reportCompare(true, true);
new file mode 100644 --- /dev/null +++ b/js/src/tests/non262/fields/error.js @@ -0,0 +1,45 @@ +// * * * THIS TEST IS DISABLED - Fields are not fully implemented yet + +let source = `class C { + x +}`; +assertThrowsInstanceOf(() => Function(source), SyntaxError); + +source = `class C { + -2; + -2 = 2; +}`; +assertThrowsInstanceOf(() => Function(source), SyntaxError); + +source = `class C { + x += 2; +}`; +assertThrowsInstanceOf(() => Function(source), SyntaxError); + +source = `class C { + #2; +}`; +assertThrowsInstanceOf(() => Function(source), SyntaxError); + +source = `class C { + #["h" + "i"]; +}`; +assertThrowsInstanceOf(() => Function(source), SyntaxError); + +source = `class C { + #"hi"; +}`; +assertThrowsInstanceOf(() => Function(source), SyntaxError); + +source = `function f() { +class C { + #"should still throw error during lazy parse"; +} +}`; +assertThrowsInstanceOf(() => Function(source), SyntaxError); + +source = `#outside;`; +assertThrowsInstanceOf(() => eval(source), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true);
new file mode 100644 --- /dev/null +++ b/js/src/tests/non262/fields/field_types.js @@ -0,0 +1,25 @@ +// * * * THIS TEST IS DISABLED - Fields are not fully implemented yet + +class C { + [Math.sqrt(4)]; + [Math.sqrt(8)] = 5 + 2; + "hi"; + "bye" = {}; + 2 = 2; + 0x101 = 2; + 0o101 = 2; + 0b101 = 2; + NaN = 0; // actually the field called "NaN", not the number + Infinity = 50; // actually the field called "Infinity", not the number + // all the keywords below are proper fields (?!?) + with = 0; + //static = 0; // doesn't work yet + async = 0; + get = 0; + set = 0; + export = 0; + function = 0; +} + +if (typeof reportCompare === "function") + reportCompare(true, true);
new file mode 100644 --- /dev/null +++ b/js/src/tests/non262/fields/literal.js @@ -0,0 +1,46 @@ +// * * * THIS TEST IS DISABLED - Fields are not fully implemented yet + +source = `var y = { + x; +}`; +assertThrowsInstanceOf(() => eval(source), SyntaxError); + +// This is legal, and is equivalent to `var y = { x: x };` +// source = `var y = { +// x +// }`; +// assertThrowsInstanceOf(() => eval(source), SyntaxError); + +source = `var y = { + #x; +}`; +assertThrowsInstanceOf(() => eval(source), SyntaxError); + +// Temporarily disabled due to the same reason above. +// source = `var y = { +// #x +// }`; +// assertThrowsInstanceOf(() => eval(source), SyntaxError); + +source = `var y = { + x = 2; +}`; +assertThrowsInstanceOf(() => eval(source), SyntaxError); + +source = `var y = { + x = 2 +}`; +assertThrowsInstanceOf(() => eval(source), SyntaxError); + +source = `var y = { + #x = 2; +}`; +assertThrowsInstanceOf(() => eval(source), SyntaxError); + +source = `var y = { + #x = 2 +}`; +assertThrowsInstanceOf(() => eval(source), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true);
new file mode 100644 --- /dev/null +++ b/js/src/tests/non262/fields/mixed_methods.js @@ -0,0 +1,11 @@ +// * * * THIS TEST IS DISABLED - Fields are not fully implemented yet + +class C { + x; + y(){} + z = 2; + w(){}; +} + +if (typeof reportCompare === "function") + reportCompare(true, true);
new file mode 100644 --- /dev/null +++ b/js/src/tests/non262/fields/quirks.js @@ -0,0 +1,16 @@ +// * * * THIS TEST IS DISABLED - Fields are not fully implemented yet + +class C { + x;;;; + y + ; +} + +class D { + x = 5; + y = (x += 1); + // TODO: Assert values of x and y +} + +if (typeof reportCompare === "function") + reportCompare(true, true);
--- a/js/src/vm/CompilationAndEvaluation.cpp +++ b/js/src/vm/CompilationAndEvaluation.cpp @@ -320,17 +320,17 @@ JS::CompileFunction(JSContext* cx, AutoO if (name) { nameLen = strlen(name); nameAtom = Atomize(cx, name, nameLen); if (!nameAtom) { 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; } } uint32_t parameterListEnd; StringBuffer funStr(cx); if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf, &funStr, ¶meterListEnd))
--- a/js/src/vm/StringType.cpp +++ b/js/src/vm/StringType.cpp @@ -2205,17 +2205,17 @@ js::EncodeAscii(JSContext* cx, JSString* } UniqueChars js::IdToPrintableUTF8(JSContext* cx, HandleId id, IdToPrintableBehavior behavior) { // ToString(<symbol>) throws a TypeError, therefore require that callers // request source representation when |id| is a property key. MOZ_ASSERT_IF(behavior == IdToPrintableBehavior::IdIsIdentifier, - JSID_IS_ATOM(id) && frontend::IsIdentifier(JSID_TO_ATOM(id))); + JSID_IS_ATOM(id) && frontend::IsIdentifierNameOrPrivateName(JSID_TO_ATOM(id))); RootedValue v(cx, IdToValue(id)); JSString* str; if (behavior == IdToPrintableBehavior::IdIsPropertyKey) { str = ValueToSource(cx, v); } else { str = ToString<CanGC>(cx, v); }