Bug 1499448 - Implement syntax for public and private fields. r=jorendorff

Differential Revision: https://phabricator.services.mozilla.com/D8887

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ashley Hauck 2018-10-25 18:25:34 +00:00
parent 7dd4276f45
commit b0f51cc3b0
26 changed files with 687 additions and 158 deletions

View File

@ -549,7 +549,7 @@ 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);
@ -1652,9 +1652,9 @@ NodeBuilder::classMethod(HandleValue name, HandleValue body, PropKind kind, bool
}
bool
NodeBuilder::classMethods(NodeVector& methods, MutableHandleValue dst)
NodeBuilder::classMembers(NodeVector& members, MutableHandleValue dst)
{
return newArray(methods, dst);
return newArray(members, dst);
}
bool
@ -2392,7 +2392,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);
}
@ -2620,26 +2620,31 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue 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:
@ -2960,6 +2965,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue 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);

View File

@ -121,14 +121,22 @@ CreateScriptSourceObject(JSContext* cx, const JS::ReadOnlyCompileOptions& option
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);

View File

@ -1043,6 +1043,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
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>());
@ -1486,17 +1487,18 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
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
@ -1789,6 +1791,8 @@ BytecodeEmitter::emitPropLHS(PropertyAccess* prop)
}
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;
@ -1810,6 +1814,7 @@ 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,
@ -2624,6 +2629,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri
// // [Other]
// // OBJ VAL
PropertyAccess* prop = &target->as<PropertyAccess>();
// TODO(khyperia): Implement private field access.
bool isSuper = prop->isSuper();
PropOpEmitter poe(this,
PropOpEmitter::Kind::SimpleAssignment,
@ -3993,6 +3999,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp compoundOp, ParseNode* 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]
@ -4066,6 +4073,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp compoundOp, ParseNode* rhs)
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;
}
@ -6392,6 +6400,7 @@ 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()
@ -6793,6 +6802,7 @@ BytecodeEmitter::emitCalleeAndThis(ParseNode* callee, ParseNode* call, CallOrNew
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);
@ -7018,7 +7028,9 @@ BytecodeEmitter::emitCallOrNew(BinaryNode* callNode,
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))
{
@ -7302,6 +7314,10 @@ 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;
}
@ -8132,9 +8148,13 @@ 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() &&
@ -8321,7 +8341,7 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
}
RootedPlainObject obj(cx);
if (!emitPropertyList(classMethods, &obj, ClassBody)) { // ... CONSTRUCTOR HOMEOBJ
if (!emitPropertyList(classMembers, &obj, ClassBody)) { // ... CONSTRUCTOR HOMEOBJ
return false;
}
@ -8693,6 +8713,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
case ParseNodeKind::Dot: {
PropertyAccess* prop = &pn->as<PropertyAccess>();
// TODO(khyperia): Implement private field access.
bool isSuper = prop->isSuper();
PropOpEmitter poe(this,
PropOpEmitter::Kind::Get,

View File

@ -365,6 +365,7 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
case ParseNodeKind::Arguments:
case ParseNodeKind::Call:
case ParseNodeKind::Name:
case ParseNodeKind::PrivateName:
case ParseNodeKind::TemplateString:
case ParseNodeKind::TemplateStringList:
case ParseNodeKind::TaggedTemplate:
@ -386,7 +387,8 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
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:
@ -1631,6 +1633,7 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
return true;
case ParseNodeKind::ObjectPropertyName:
case ParseNodeKind::PrivateName:
case ParseNodeKind::String:
case ParseNodeKind::TemplateString:
MOZ_ASSERT(pn->is<NameNode>());
@ -1759,7 +1762,7 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
case ParseNodeKind::Array:
case ParseNodeKind::Object:
case ParseNodeKind::StatementList:
case ParseNodeKind::ClassMethodList:
case ParseNodeKind::ClassMemberList:
case ParseNodeKind::TemplateStringList:
case ParseNodeKind::Var:
case ParseNodeKind::Const:
@ -1849,6 +1852,17 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& 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

View File

@ -264,7 +264,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
if (!elision) {
return false;
}
addList(/* list = */ literal, /* child = */ elision);
addList(/* list = */ literal, /* kid = */ elision);
literal->setHasArrayHoleOrSpread();
literal->setHasNonConstInitializer();
return true;
@ -277,7 +277,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
if (!spread) {
return false;
}
addList(/* list = */ literal, /* child = */ spread);
addList(/* list = */ literal, /* kid = */ spread);
literal->setHasArrayHoleOrSpread();
literal->setHasNonConstInitializer();
return true;
@ -287,7 +287,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
if (!element->isConstant()) {
literal->setHasNonConstInitializer();
}
addList(/* list = */ literal, /* child = */ element);
addList(/* list = */ literal, /* kid = */ element);
}
BinaryNodeType newCall(Node callee, Node args) {
@ -310,11 +310,11 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
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);
@ -339,7 +339,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
if (!mutation) {
return false;
}
addList(/* list = */ literal, /* child = */ mutation);
addList(/* list = */ literal, /* kid = */ mutation);
return true;
}
@ -357,7 +357,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
literal->setHasNonConstInitializer();
}
addList(/* list = */ literal, /* child = */ propdef);
addList(/* list = */ literal, /* kid = */ propdef);
}
MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node val) {
@ -380,7 +380,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
if (!propdef) {
return false;
}
addList(/* list = */ literal, /* child = */ propdef);
addList(/* list = */ literal, /* kid = */ propdef);
return true;
}
@ -392,7 +392,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
if (!spread) {
return false;
}
addList(/* list = */ literal, /* child = */ spread);
addList(/* list = */ literal, /* kid = */ spread);
return true;
}
@ -408,15 +408,15 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
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);
@ -426,7 +426,22 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
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;
}
@ -466,7 +481,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
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
@ -483,7 +498,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
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();
@ -735,11 +750,11 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
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) {

View File

@ -90,6 +90,7 @@ class NameResolver
}
case ParseNodeKind::Name:
case ParseNodeKind::PrivateName:
*foundName = true;
return buf->append(n->as<NameNode>().atom());
@ -148,6 +149,7 @@ class NameResolver
}
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 */
@ -447,6 +449,7 @@ class NameResolver
break;
case ParseNodeKind::ObjectPropertyName:
case ParseNodeKind::PrivateName: // TODO(khyperia): Implement private field access.
case ParseNodeKind::String:
case ParseNodeKind::TemplateString:
MOZ_ASSERT(cur->is<NameNode>());
@ -540,6 +543,21 @@ class NameResolver
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)) {
@ -701,7 +719,7 @@ class NameResolver
return false;
}
}
if (!resolve(classNode->methodList(), prefix)) {
if (!resolve(classNode->memberList(), prefix)) {
return false;
}
break;
@ -809,7 +827,7 @@ class NameResolver
break;
case ParseNodeKind::Object:
case ParseNodeKind::ClassMethodList:
case ParseNodeKind::ClassMemberList:
for (ParseNode* element : cur->as<ListNode>().contents()) {
if (!resolve(element, prefix)) {
return false;

View File

@ -174,6 +174,9 @@ ParseNode::dump(GenericPrinter& out, int indent)
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;
@ -347,6 +350,7 @@ NameNode::dump(GenericPrinter& out, int indent)
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>");
@ -388,6 +392,21 @@ NameNode::dump(GenericPrinter& out, int indent)
}
}
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)
{

View File

@ -68,6 +68,7 @@ class ObjectBox;
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) \
@ -128,7 +129,8 @@ class ObjectBox;
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) \
@ -253,15 +255,15 @@ IsTypeofKind(ParseNodeKind kind)
* 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
@ -520,6 +522,7 @@ enum ParseNodeArity
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) */
@ -532,6 +535,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) \
@ -713,6 +717,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;
@ -972,7 +982,7 @@ class BinaryNode : public ParseNode
}
// 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;
}
@ -1179,7 +1189,7 @@ class ListNode : public ParseNode
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;
}
@ -1196,7 +1206,7 @@ class ListNode : public ParseNode
void setHasNonConstInitializer() {
MOZ_ASSERT(isKind(ParseNodeKind::Array) ||
isKind(ParseNodeKind::Object) ||
isKind(ParseNodeKind::ClassMethodList));
isKind(ParseNodeKind::ClassMemberList));
pn_u.list.xflags |= hasNonConstInitializerBit;
}
@ -1960,6 +1970,48 @@ class ClassMethod : public BinaryNode
}
};
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:
@ -2046,13 +2098,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(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) {
@ -2067,14 +2119,14 @@ class ClassNode : public TernaryNode
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 {

View File

@ -5017,7 +5017,7 @@ GeneralParser<ParseHandler, Unit>::objectBindingPattern(DeclarationKind kind,
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();
}
@ -8027,8 +8027,8 @@ GeneralParser<ParseHandler, Unit>::classDefinition(YieldHandling yieldHandling,
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();
}
@ -8072,11 +8072,44 @@ GeneralParser<ParseHandler, Unit>::classDefinition(YieldHandling yieldHandling,
}
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 &&
@ -8133,7 +8166,7 @@ GeneralParser<ParseHandler, Unit>::classDefinition(YieldHandling yieldHandling,
}
AccessorType atype = ToAccessorType(propType);
if (!handler.addClassMethodDefinition(classMethods, propName, funNode, atype, isStatic)) {
if (!handler.addClassMethodDefinition(classMembers, propName, funNode, atype, isStatic)) {
return null();
}
}
@ -8149,7 +8182,7 @@ GeneralParser<ParseHandler, Unit>::classDefinition(YieldHandling yieldHandling,
}
Node nameNode = null();
Node methodsOrBlock = classMethods;
Node membersOrBlock = classMembers;
if (name) {
// The inner name is immutable.
if (!noteDeclaredName(name, DeclarationKind::Const, namePos)) {
@ -8161,12 +8194,12 @@ GeneralParser<ParseHandler, Unit>::classDefinition(YieldHandling yieldHandling,
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();
@ -8193,7 +8226,7 @@ GeneralParser<ParseHandler, Unit>::classDefinition(YieldHandling yieldHandling,
MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
return handler.newClass(nameNode, classHeritage, methodsOrBlock,
return handler.newClass(nameNode, classHeritage, membersOrBlock,
TokenPos(classStartOffset, classEndOffset));
}
@ -9604,6 +9637,7 @@ GeneralParser<ParseHandler, Unit>::memberExpr(YieldHandling yieldHandling,
if (!tokenStream.getToken(&tt)) {
return null();
}
if (TokenKindIsPossibleIdentifierName(tt)) {
PropertyName* field = anyChars.currentName();
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
@ -9817,7 +9851,7 @@ GeneralParser<ParseHandler, Unit>::checkLabelOrIdentifierReference(PropertyName*
tt = hint;
}
if (tt == TokenKind::Name) {
if (tt == TokenKind::Name || tt == TokenKind::PrivateName) {
return true;
}
if (TokenKindIsContextualKeyword(tt)) {
@ -10275,10 +10309,11 @@ GeneralParser<ParseHandler, Unit>::arrayInitializer(YieldHandling yieldHandling,
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(&ltok)) {
@ -10449,6 +10484,16 @@ GeneralParser<ParseHandler, Unit>::propertyName(YieldHandling yieldHandling,
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))
{
@ -10567,7 +10612,7 @@ GeneralParser<ParseHandler, Unit>::objectLiteral(YieldHandling yieldHandling,
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();
}

View File

@ -240,7 +240,8 @@ enum class PropertyType {
AsyncMethod,
AsyncGeneratorMethod,
Constructor,
DerivedConstructor
DerivedConstructor,
Field,
};
enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword };
@ -1243,7 +1244,9 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
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);

View File

@ -279,7 +279,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
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;
}
@ -305,11 +305,15 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
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; }

View File

@ -73,6 +73,7 @@
macro(LeftParen, "'('") \
macro(RightParen, "')'") \
macro(Name, "identifier") \
macro(PrivateName, "private identifier") \
macro(Number, "numeric literal") \
macro(String, "string literal") \
\
@ -322,6 +323,7 @@ inline MOZ_MUST_USE bool
TokenKindIsPossibleIdentifier(TokenKind tt)
{
return tt == TokenKind::Name ||
tt == TokenKind::PrivateName ||
TokenKindIsContextualKeyword(tt) ||
TokenKindIsStrictReservedWord(tt);
}

View File

@ -119,36 +119,28 @@ FindReservedWord<Utf8Unit>(const Utf8Unit* units, 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());
}
template <typename CharT>
static bool
IsIdentifierImpl(const CharT* chars, size_t length)
{
using namespace js;
if (length == 0) {
return false;
}
if (!unicode::IsIdentifierStart(char16_t(*chars))) {
return false;
}
const CharT* end = chars + length;
while (++chars != end) {
if (!unicode::IsIdentifierPart(char16_t(*chars))) {
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);
}
return true;
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);
}
static uint32_t
@ -171,15 +163,71 @@ GetSingleCodePoint(const char16_t** p, const char16_t* end)
return codePoint;
}
static bool
IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length)
{
using namespace js;
namespace js {
if (IsIdentifierImpl(chars, length)) {
return true;
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)
{
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;
}
@ -203,37 +251,45 @@ IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length)
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);
}
const char16_t* p = chars;
const char16_t* end = chars + length;
uint32_t codePoint;
bool
IsIdentifier(const char16_t* chars, size_t length)
{
return ::IsIdentifierImpl(chars, length);
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);
}
@ -243,17 +299,19 @@ IsKeyword(JSLinearString* str)
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);
}
@ -1660,6 +1718,42 @@ GeneralTokenStreamChars<Unit, AnyCharsAccess>::matchUnicodeEscapeIdent(uint32_t*
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,
@ -1931,9 +2025,11 @@ TokenStreamSpecific<Unit, AnyCharsAccess>::putIdentInCharBuffer(const Unit* iden
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.
@ -1995,11 +2091,14 @@ TokenStreamSpecific<Unit, AnyCharsAccess>::identifierName(TokenStart start,
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));
@ -2009,7 +2108,16 @@ TokenStreamSpecific<Unit, AnyCharsAccess>::identifierName(TokenStart start,
}
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;
}
@ -2440,7 +2548,7 @@ TokenStreamSpecific<Unit, AnyCharsAccess>::getTokenInternal(TokenKind* const ttp
"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);
@ -2489,7 +2597,8 @@ TokenStreamSpecific<Unit, AnyCharsAccess>::getTokenInternal(TokenKind* const ttp
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.
@ -2682,6 +2791,16 @@ TokenStreamSpecific<Unit, AnyCharsAccess>::getTokenInternal(TokenKind* const ttp
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;
@ -2705,7 +2824,7 @@ TokenStreamSpecific<Unit, AnyCharsAccess>::getTokenInternal(TokenKind* const ttp
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

View File

@ -289,6 +289,8 @@ enum class InvalidEscapeType {
// 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
@ -406,7 +408,7 @@ struct Token
// Mutators
void setName(PropertyName* name) {
MOZ_ASSERT(type == TokenKind::Name);
MOZ_ASSERT(type == TokenKind::Name || type == TokenKind::PrivateName);
u.name = name;
}
@ -432,7 +434,7 @@ struct Token
// 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
}
@ -627,7 +629,7 @@ class TokenStreamAnyChars
public:
PropertyName* currentName() const {
if (isCurrentTokenType(TokenKind::Name)) {
if (isCurrentTokenType(TokenKind::Name) || isCurrentTokenType(TokenKind::PrivateName)) {
return currentToken().name();
}
@ -636,7 +638,7 @@ class TokenStreamAnyChars
}
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();
}
@ -1927,6 +1929,15 @@ class GeneralTokenStreamChars
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);
@ -2000,6 +2011,7 @@ class GeneralTokenStreamChars
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|
@ -2335,6 +2347,7 @@ class MOZ_STACK_CLASS TokenStreamSpecific
using GeneralCharsBase::matchUnicodeEscapeIdStart;
using GeneralCharsBase::newAtomToken;
using GeneralCharsBase::newNameToken;
using GeneralCharsBase::newPrivateNameToken;
using GeneralCharsBase::newNumberToken;
using GeneralCharsBase::newRegExpToken;
using GeneralCharsBase::newSimpleToken;
@ -2670,7 +2683,9 @@ class MOZ_STACK_CLASS TokenStreamSpecific
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);

View File

@ -265,6 +265,7 @@ MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'fro
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")
@ -355,6 +356,8 @@ MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property na
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")

View File

@ -78,4 +78,5 @@ ASTDEF(AST_COMPUTED_NAME, "ComputedName", "computedNam
ASTDEF(AST_CLASS_STMT, "ClassStatement", "classStatement")
ASTDEF(AST_CLASS_METHOD, "ClassMethod", "classMethod")
ASTDEF(AST_CLASS_FIELD, "ClassField", "classField")
/* AST_LIMIT = last + 1 */

View File

@ -461,6 +461,14 @@ 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 #

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -2210,7 +2210,7 @@ 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;