diff --git a/js/moz.configure b/js/moz.configure index 0b098e7ab7c6..a83a4242972b 100644 --- a/js/moz.configure +++ b/js/moz.configure @@ -173,6 +173,24 @@ def enable_decorators(value): set_config("ENABLE_DECORATORS", enable_decorators) set_define("ENABLE_DECORATORS", enable_decorators) +# Enable explicit resource management +# =================================================== +option( + "--enable-explicit-resource-management", + default=False, + help="Enable explicit resource management", +) + + +@depends("--enable-explicit-resource-management") +def enable_explicit_resource_management(value): + if value: + return True + + +set_config("ENABLE_EXPLICIT_RESOURCE_MANAGEMENT", enable_explicit_resource_management) +set_define("ENABLE_EXPLICIT_RESOURCE_MANAGEMENT", enable_explicit_resource_management) + # Enable JSON.parse with source # =================================================== diff --git a/js/public/TypeDecls.h b/js/public/TypeDecls.h index 1686b19173df..0ee8c952ff90 100644 --- a/js/public/TypeDecls.h +++ b/js/public/TypeDecls.h @@ -148,4 +148,10 @@ using jsid = JS::PropertyKey; # define IF_DECORATORS(x, ...) __VA_ARGS__ #endif +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT +# define IF_EXPLICIT_RESOURCE_MANAGEMENT(x, ...) x +#else +# define IF_EXPLICIT_RESOURCE_MANAGEMENT(x, ...) __VA_ARGS__ +#endif + #endif /* js_TypeDecls_h */ diff --git a/js/public/friend/ErrorNumbers.msg b/js/public/friend/ErrorNumbers.msg index 3500783210bb..13bea1c3a8d3 100644 --- a/js/public/friend/ErrorNumbers.msg +++ b/js/public/friend/ErrorNumbers.msg @@ -401,6 +401,7 @@ MSG_DEF(JSMSG_ASSERT_STRING_LITERAL, 0, JSEXN_SYNTAXERR, "expected string lite MSG_DEF(JSMSG_ASSERT_KEY_EXPECTED, 0, JSEXN_SYNTAXERR, "expected assertion key") MSG_DEF(JSMSG_DECORATOR_NAME_EXPECTED, 0, JSEXN_SYNTAXERR, "expected property name in decorator expression") MSG_DEF(JSMSG_CLASS_EXPECTED, 0, JSEXN_SYNTAXERR, "expected class") +MSG_DEF(JSMSG_NO_IN_WITH_USING, 0, JSEXN_SYNTAXERR, "'using' is not allowed in a for-in loop") // 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") diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index aa5666f238bb..e7c15493dc06 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -136,6 +136,9 @@ enum VarDeclKind { VARDECL_VAR = 0, VARDECL_CONST, VARDECL_LET, +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + VARDECL_USING, +#endif VARDECL_LIMIT }; @@ -1235,10 +1238,23 @@ bool NodeBuilder::variableDeclaration(NodeVector& elts, VarDeclKind kind, MOZ_ASSERT(kind > VARDECL_ERR && kind < VARDECL_LIMIT); RootedValue array(cx), kindName(cx); - if (!newArray(elts, &array) || !atomValue(kind == VARDECL_CONST ? "const" - : kind == VARDECL_LET ? "let" - : "var", - &kindName)) { + const char* s; + switch (kind) { + case VARDECL_CONST: + s = "const"; + break; + case VARDECL_LET: + s = "let"; + break; +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case VARDECL_USING: + s = "using"; + break; +#endif + default: + s = "var"; + } + if (!newArray(elts, &array) || !atomValue(s, &kindName)) { return false; } @@ -1729,6 +1745,9 @@ bool ASTSerializer::declaration(ParseNode* pn, MutableHandleValue dst) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Function) || pn->isKind(ParseNodeKind::VarStmt) || pn->isKind(ParseNodeKind::LetDecl) || +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + pn->isKind(ParseNodeKind::UsingDecl) || +#endif pn->isKind(ParseNodeKind::ConstDecl)); switch (pn->getKind()) { @@ -1740,6 +1759,9 @@ bool ASTSerializer::declaration(ParseNode* pn, MutableHandleValue dst) { default: MOZ_ASSERT(pn->isKind(ParseNodeKind::LetDecl) || +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + pn->isKind(ParseNodeKind::UsingDecl) || +#endif pn->isKind(ParseNodeKind::ConstDecl)); return variableDeclaration(&pn->as(), true, dst); } @@ -1748,6 +1770,9 @@ bool ASTSerializer::declaration(ParseNode* pn, MutableHandleValue dst) { bool ASTSerializer::variableDeclaration(ListNode* declList, bool lexical, MutableHandleValue dst) { MOZ_ASSERT_IF(lexical, declList->isKind(ParseNodeKind::LetDecl) || +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + declList->isKind(ParseNodeKind::UsingDecl) || +#endif declList->isKind(ParseNodeKind::ConstDecl)); MOZ_ASSERT_IF(!lexical, declList->isKind(ParseNodeKind::VarStmt)); @@ -1755,8 +1780,17 @@ bool ASTSerializer::variableDeclaration(ListNode* declList, bool lexical, // Treat both the toplevel const binding (secretly var-like) and the lexical // const the same way if (lexical) { - kind = - declList->isKind(ParseNodeKind::LetDecl) ? VARDECL_LET : VARDECL_CONST; + if (declList->isKind(ParseNodeKind::LetDecl)) { + kind = VARDECL_LET; + } +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + else if (declList->isKind(ParseNodeKind::UsingDecl)) { + kind = VARDECL_USING; + } +#endif + else { + kind = VARDECL_CONST; + } } else { kind = declList->isKind(ParseNodeKind::VarStmt) ? VARDECL_VAR : VARDECL_CONST; @@ -2143,7 +2177,11 @@ bool ASTSerializer::forInit(ParseNode* pn, MutableHandleValue dst) { } bool lexical = pn->isKind(ParseNodeKind::LetDecl) || - pn->isKind(ParseNodeKind::ConstDecl); + pn->isKind(ParseNodeKind::ConstDecl) +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + || pn->isKind(ParseNodeKind::UsingDecl) +#endif + ; return (lexical || pn->isKind(ParseNodeKind::VarStmt)) ? variableDeclaration(&pn->as(), lexical, dst) : expression(pn, dst); @@ -2218,6 +2256,9 @@ bool ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) { case ParseNodeKind::LetDecl: case ParseNodeKind::ConstDecl: +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case ParseNodeKind::UsingDecl: +#endif return declaration(pn, dst); case ParseNodeKind::ImportDecl: @@ -2339,6 +2380,9 @@ bool ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) { } } else if (!initNode->isKind(ParseNodeKind::VarStmt) && !initNode->isKind(ParseNodeKind::LetDecl) && +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + !initNode->isKind(ParseNodeKind::UsingDecl) && +#endif !initNode->isKind(ParseNodeKind::ConstDecl)) { if (!pattern(initNode, &var)) { return false; @@ -2347,6 +2391,9 @@ bool ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) { if (!variableDeclaration( &initNode->as(), initNode->isKind(ParseNodeKind::LetDecl) || +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + initNode->isKind(ParseNodeKind::UsingDecl) || +#endif initNode->isKind(ParseNodeKind::ConstDecl), &var)) { return false; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index cb3b47181f14..4e10adcbf53a 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -604,6 +604,15 @@ static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) { return false; } +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + value = BooleanValue(true); +#else + value = BooleanValue(false); +#endif + if (!JS_SetProperty(cx, info, "explicit-resource-management", value)) { + return false; + } + #ifdef FUZZING value = BooleanValue(true); #else diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index f288d333e195..08ab288d4922 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1086,6 +1086,11 @@ restart: MOZ_CRASH("Decorators are not supported yet"); #endif +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case ParseNodeKind::UsingDecl: + MOZ_CRASH("Using declarations are not supported yet"); +#endif + // Most other binary operations (parsed as lists in SpiderMonkey) may // perform conversions triggering side effects. Math operations perform // ToNumber and may fail invoking invalid user-defined toString/valueOf: diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index b72c6d47264b..fd07072c7afe 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -105,6 +105,9 @@ restart: // Non-global lexical declarations are block-scoped (ergo not hoistable). case ParseNodeKind::LetDecl: case ParseNodeKind::ConstDecl: +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case ParseNodeKind::UsingDecl: +#endif MOZ_ASSERT(node->is()); *result = false; return true; diff --git a/js/src/frontend/GenerateReservedWords.py b/js/src/frontend/GenerateReservedWords.py index 078f8bf833c0..5a4456389e7e 100644 --- a/js/src/frontend/GenerateReservedWords.py +++ b/js/src/frontend/GenerateReservedWords.py @@ -6,7 +6,9 @@ import re import sys -def read_reserved_word_list(filename, enable_decorators): +def read_reserved_word_list( + filename, enable_decorators, enable_explicit_resource_management +): macro_pat = re.compile(r"MACRO\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?") reserved_word_list = [] @@ -18,6 +20,8 @@ def read_reserved_word_list(filename, enable_decorators): reserved_word = m.group(1) if reserved_word == "accessor" and not enable_decorators: continue + if reserved_word == "using" and not enable_explicit_resource_management: + continue reserved_word_list.append((index, reserved_word)) index += 1 @@ -216,8 +220,20 @@ def generate_switch(opt, reserved_word_list): line(opt, "JSRW_NO_MATCH()") -def main(output, reserved_words_h, enable_decorators=False): - reserved_word_list = read_reserved_word_list(reserved_words_h, enable_decorators) +def main(output, reserved_words_h, *args): + enable_decorators = False + enable_explicit_resource_management = False + for arg in args: + if arg == "--enable-decorators": + enable_decorators = True + elif arg == "--enable-explicit-resource-management": + enable_explicit_resource_management = True + else: + raise ValueError("Unknown argument: " + arg) + + reserved_word_list = read_reserved_word_list( + reserved_words_h, enable_decorators, enable_explicit_resource_management + ) opt = { "indent_level": 1, diff --git a/js/src/frontend/NameAnalysisTypes.h b/js/src/frontend/NameAnalysisTypes.h index d2f44c170c23..7b169b61d5fb 100644 --- a/js/src/frontend/NameAnalysisTypes.h +++ b/js/src/frontend/NameAnalysisTypes.h @@ -81,6 +81,9 @@ enum class DeclarationKind : uint8_t { Var, Let, Const, +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + Using, +#endif Class, // Handled as same as `let` after parsing. Import, BodyLevelFunction, @@ -120,6 +123,10 @@ static inline BindingKind DeclarationKindToBindingKind(DeclarationKind kind) { return BindingKind::Let; case DeclarationKind::Const: +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case DeclarationKind::Using: // we treat using as a const for now. (Bug + // 1897609) +#endif return BindingKind::Const; case DeclarationKind::Import: diff --git a/js/src/frontend/ParseContext.cpp b/js/src/frontend/ParseContext.cpp index 622c4678222a..6b736ea2c9c9 100644 --- a/js/src/frontend/ParseContext.cpp +++ b/js/src/frontend/ParseContext.cpp @@ -33,6 +33,10 @@ const char* DeclarationKindString(DeclarationKind kind) { return "let"; case DeclarationKind::Const: return "const"; +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case DeclarationKind::Using: + return "using"; +#endif case DeclarationKind::Class: return "class"; case DeclarationKind::Import: diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index a6747897d672..96f837a3f5a7 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -60,185 +60,186 @@ class FullParseHandler; class FunctionBox; -#define FOR_EACH_PARSE_NODE_KIND(F) \ - F(EmptyStmt, NullaryNode) \ - F(ExpressionStmt, UnaryNode) \ - F(CommaExpr, ListNode) \ - F(ConditionalExpr, ConditionalExpression) \ - F(PropertyDefinition, PropertyDefinition) \ - F(Shorthand, BinaryNode) \ - F(PosExpr, UnaryNode) \ - F(NegExpr, UnaryNode) \ - F(PreIncrementExpr, UnaryNode) \ - F(PostIncrementExpr, UnaryNode) \ - F(PreDecrementExpr, UnaryNode) \ - F(PostDecrementExpr, UnaryNode) \ - F(PropertyNameExpr, NameNode) \ - F(DotExpr, PropertyAccess) \ - F(ArgumentsLength, ArgumentsLength) \ - F(ElemExpr, PropertyByValue) \ - F(PrivateMemberExpr, PrivateMemberAccess) \ - F(OptionalDotExpr, OptionalPropertyAccess) \ - F(OptionalChain, UnaryNode) \ - F(OptionalElemExpr, OptionalPropertyByValue) \ - F(OptionalPrivateMemberExpr, OptionalPrivateMemberAccess) \ - F(OptionalCallExpr, CallNode) \ - F(ArrayExpr, ListNode) \ - F(Elision, NullaryNode) \ - F(StatementList, ListNode) \ - F(LabelStmt, LabeledStatement) \ - F(ObjectExpr, ListNode) \ - F(CallExpr, CallNode) \ - F(Arguments, ListNode) \ - F(Name, NameNode) \ - F(ObjectPropertyName, NameNode) \ - F(PrivateName, NameNode) \ - F(ComputedName, UnaryNode) \ - F(NumberExpr, NumericLiteral) \ - F(BigIntExpr, BigIntLiteral) \ - F(StringExpr, NameNode) \ - F(TemplateStringListExpr, ListNode) \ - F(TemplateStringExpr, NameNode) \ - F(TaggedTemplateExpr, CallNode) \ - F(CallSiteObj, CallSiteNode) \ - F(RegExpExpr, RegExpLiteral) \ - F(TrueExpr, BooleanLiteral) \ - F(FalseExpr, BooleanLiteral) \ - F(NullExpr, NullLiteral) \ - F(RawUndefinedExpr, RawUndefinedLiteral) \ - F(ThisExpr, UnaryNode) \ - IF_RECORD_TUPLE(F(RecordExpr, ListNode)) \ - IF_RECORD_TUPLE(F(TupleExpr, ListNode)) \ - F(Function, FunctionNode) \ - F(Module, ModuleNode) \ - F(IfStmt, TernaryNode) \ - F(SwitchStmt, SwitchStatement) \ - F(Case, CaseClause) \ - F(WhileStmt, BinaryNode) \ - F(DoWhileStmt, BinaryNode) \ - F(ForStmt, ForNode) \ - F(BreakStmt, BreakStatement) \ - F(ContinueStmt, ContinueStatement) \ - F(VarStmt, DeclarationListNode) \ - F(ConstDecl, DeclarationListNode) \ - F(WithStmt, BinaryNode) \ - F(ReturnStmt, UnaryNode) \ - F(NewExpr, CallNode) \ - IF_DECORATORS(F(DecoratorList, ListNode)) \ - /* Delete operations. These must be sequential. */ \ - F(DeleteNameExpr, UnaryNode) \ - F(DeletePropExpr, UnaryNode) \ - F(DeleteElemExpr, UnaryNode) \ - F(DeleteOptionalChainExpr, UnaryNode) \ - F(DeleteExpr, UnaryNode) \ - F(TryStmt, TernaryNode) \ - F(Catch, BinaryNode) \ - F(ThrowStmt, UnaryNode) \ - F(DebuggerStmt, DebuggerStatement) \ - F(Generator, NullaryNode) \ - F(InitialYield, UnaryNode) \ - F(YieldExpr, UnaryNode) \ - F(YieldStarExpr, UnaryNode) \ - F(LexicalScope, LexicalScopeNode) \ - F(LetDecl, DeclarationListNode) \ - F(ImportDecl, BinaryNode) \ - F(ImportSpecList, ListNode) \ - F(ImportSpec, BinaryNode) \ - F(ImportNamespaceSpec, UnaryNode) \ - F(ImportAttributeList, ListNode) \ - F(ImportAttribute, BinaryNode) \ - F(ImportModuleRequest, BinaryNode) \ - F(ExportStmt, UnaryNode) \ - F(ExportFromStmt, BinaryNode) \ - F(ExportDefaultStmt, BinaryNode) \ - F(ExportSpecList, ListNode) \ - F(ExportSpec, BinaryNode) \ - F(ExportNamespaceSpec, UnaryNode) \ - F(ExportBatchSpecStmt, NullaryNode) \ - F(ForIn, TernaryNode) \ - F(ForOf, TernaryNode) \ - F(ForHead, TernaryNode) \ - F(ParamsBody, ParamsBodyNode) \ - F(Spread, UnaryNode) \ - F(MutateProto, UnaryNode) \ - F(ClassDecl, ClassNode) \ - F(DefaultConstructor, ClassMethod) \ - F(ClassBodyScope, ClassBodyScopeNode) \ - F(ClassMethod, ClassMethod) \ - F(StaticClassBlock, StaticClassBlock) \ - F(ClassField, ClassField) \ - F(ClassMemberList, ListNode) \ - F(ClassNames, ClassNames) \ - F(NewTargetExpr, NewTargetNode) \ - F(PosHolder, NullaryNode) \ - F(SuperBase, UnaryNode) \ - F(SuperCallExpr, CallNode) \ - F(SetThis, BinaryNode) \ - F(ImportMetaExpr, BinaryNode) \ - F(CallImportExpr, BinaryNode) \ - F(CallImportSpec, BinaryNode) \ - F(InitExpr, BinaryNode) \ - \ - /* Unary operators. */ \ - F(TypeOfNameExpr, UnaryNode) \ - F(TypeOfExpr, UnaryNode) \ - F(VoidExpr, UnaryNode) \ - F(NotExpr, UnaryNode) \ - F(BitNotExpr, UnaryNode) \ - F(AwaitExpr, UnaryNode) \ - \ - /* \ - * Binary operators. \ - * This list must be kept in the same order in several places: \ - * - The binary operators in ParseNode.h \ - * - the binary operators in TokenKind.h \ - * - the precedence list in Parser.cpp \ - * - the JSOp code list in BytecodeEmitter.cpp \ - */ \ - F(CoalesceExpr, ListNode) \ - F(OrExpr, ListNode) \ - F(AndExpr, ListNode) \ - F(BitOrExpr, ListNode) \ - F(BitXorExpr, ListNode) \ - F(BitAndExpr, ListNode) \ - F(StrictEqExpr, ListNode) \ - F(EqExpr, ListNode) \ - F(StrictNeExpr, ListNode) \ - F(NeExpr, ListNode) \ - F(LtExpr, ListNode) \ - F(LeExpr, ListNode) \ - F(GtExpr, ListNode) \ - F(GeExpr, ListNode) \ - F(InstanceOfExpr, ListNode) \ - F(InExpr, ListNode) \ - F(PrivateInExpr, ListNode) \ - F(LshExpr, ListNode) \ - F(RshExpr, ListNode) \ - F(UrshExpr, ListNode) \ - F(AddExpr, ListNode) \ - F(SubExpr, ListNode) \ - F(MulExpr, ListNode) \ - F(DivExpr, ListNode) \ - F(ModExpr, ListNode) \ - F(PowExpr, ListNode) \ - \ - /* Assignment operators (= += -= etc.). */ \ - /* AssignmentNode::test assumes all these are consecutive. */ \ - F(AssignExpr, AssignmentNode) \ - F(AddAssignExpr, AssignmentNode) \ - F(SubAssignExpr, AssignmentNode) \ - F(CoalesceAssignExpr, AssignmentNode) \ - F(OrAssignExpr, AssignmentNode) \ - F(AndAssignExpr, AssignmentNode) \ - F(BitOrAssignExpr, AssignmentNode) \ - F(BitXorAssignExpr, AssignmentNode) \ - F(BitAndAssignExpr, AssignmentNode) \ - F(LshAssignExpr, AssignmentNode) \ - F(RshAssignExpr, AssignmentNode) \ - F(UrshAssignExpr, AssignmentNode) \ - F(MulAssignExpr, AssignmentNode) \ - F(DivAssignExpr, AssignmentNode) \ - F(ModAssignExpr, AssignmentNode) \ +#define FOR_EACH_PARSE_NODE_KIND(F) \ + F(EmptyStmt, NullaryNode) \ + F(ExpressionStmt, UnaryNode) \ + F(CommaExpr, ListNode) \ + F(ConditionalExpr, ConditionalExpression) \ + F(PropertyDefinition, PropertyDefinition) \ + F(Shorthand, BinaryNode) \ + F(PosExpr, UnaryNode) \ + F(NegExpr, UnaryNode) \ + F(PreIncrementExpr, UnaryNode) \ + F(PostIncrementExpr, UnaryNode) \ + F(PreDecrementExpr, UnaryNode) \ + F(PostDecrementExpr, UnaryNode) \ + F(PropertyNameExpr, NameNode) \ + F(DotExpr, PropertyAccess) \ + F(ArgumentsLength, ArgumentsLength) \ + F(ElemExpr, PropertyByValue) \ + F(PrivateMemberExpr, PrivateMemberAccess) \ + F(OptionalDotExpr, OptionalPropertyAccess) \ + F(OptionalChain, UnaryNode) \ + F(OptionalElemExpr, OptionalPropertyByValue) \ + F(OptionalPrivateMemberExpr, OptionalPrivateMemberAccess) \ + F(OptionalCallExpr, CallNode) \ + F(ArrayExpr, ListNode) \ + F(Elision, NullaryNode) \ + F(StatementList, ListNode) \ + F(LabelStmt, LabeledStatement) \ + F(ObjectExpr, ListNode) \ + F(CallExpr, CallNode) \ + F(Arguments, ListNode) \ + F(Name, NameNode) \ + F(ObjectPropertyName, NameNode) \ + F(PrivateName, NameNode) \ + F(ComputedName, UnaryNode) \ + F(NumberExpr, NumericLiteral) \ + F(BigIntExpr, BigIntLiteral) \ + F(StringExpr, NameNode) \ + F(TemplateStringListExpr, ListNode) \ + F(TemplateStringExpr, NameNode) \ + F(TaggedTemplateExpr, CallNode) \ + F(CallSiteObj, CallSiteNode) \ + F(RegExpExpr, RegExpLiteral) \ + F(TrueExpr, BooleanLiteral) \ + F(FalseExpr, BooleanLiteral) \ + F(NullExpr, NullLiteral) \ + F(RawUndefinedExpr, RawUndefinedLiteral) \ + F(ThisExpr, UnaryNode) \ + IF_RECORD_TUPLE(F(RecordExpr, ListNode)) \ + IF_RECORD_TUPLE(F(TupleExpr, ListNode)) \ + F(Function, FunctionNode) \ + F(Module, ModuleNode) \ + F(IfStmt, TernaryNode) \ + F(SwitchStmt, SwitchStatement) \ + F(Case, CaseClause) \ + F(WhileStmt, BinaryNode) \ + F(DoWhileStmt, BinaryNode) \ + F(ForStmt, ForNode) \ + F(BreakStmt, BreakStatement) \ + F(ContinueStmt, ContinueStatement) \ + F(VarStmt, DeclarationListNode) \ + F(ConstDecl, DeclarationListNode) \ + IF_EXPLICIT_RESOURCE_MANAGEMENT(F(UsingDecl, DeclarationListNode)) \ + F(WithStmt, BinaryNode) \ + F(ReturnStmt, UnaryNode) \ + F(NewExpr, CallNode) \ + IF_DECORATORS(F(DecoratorList, ListNode)) \ + /* Delete operations. These must be sequential. */ \ + F(DeleteNameExpr, UnaryNode) \ + F(DeletePropExpr, UnaryNode) \ + F(DeleteElemExpr, UnaryNode) \ + F(DeleteOptionalChainExpr, UnaryNode) \ + F(DeleteExpr, UnaryNode) \ + F(TryStmt, TernaryNode) \ + F(Catch, BinaryNode) \ + F(ThrowStmt, UnaryNode) \ + F(DebuggerStmt, DebuggerStatement) \ + F(Generator, NullaryNode) \ + F(InitialYield, UnaryNode) \ + F(YieldExpr, UnaryNode) \ + F(YieldStarExpr, UnaryNode) \ + F(LexicalScope, LexicalScopeNode) \ + F(LetDecl, DeclarationListNode) \ + F(ImportDecl, BinaryNode) \ + F(ImportSpecList, ListNode) \ + F(ImportSpec, BinaryNode) \ + F(ImportNamespaceSpec, UnaryNode) \ + F(ImportAttributeList, ListNode) \ + F(ImportAttribute, BinaryNode) \ + F(ImportModuleRequest, BinaryNode) \ + F(ExportStmt, UnaryNode) \ + F(ExportFromStmt, BinaryNode) \ + F(ExportDefaultStmt, BinaryNode) \ + F(ExportSpecList, ListNode) \ + F(ExportSpec, BinaryNode) \ + F(ExportNamespaceSpec, UnaryNode) \ + F(ExportBatchSpecStmt, NullaryNode) \ + F(ForIn, TernaryNode) \ + F(ForOf, TernaryNode) \ + F(ForHead, TernaryNode) \ + F(ParamsBody, ParamsBodyNode) \ + F(Spread, UnaryNode) \ + F(MutateProto, UnaryNode) \ + F(ClassDecl, ClassNode) \ + F(DefaultConstructor, ClassMethod) \ + F(ClassBodyScope, ClassBodyScopeNode) \ + F(ClassMethod, ClassMethod) \ + F(StaticClassBlock, StaticClassBlock) \ + F(ClassField, ClassField) \ + F(ClassMemberList, ListNode) \ + F(ClassNames, ClassNames) \ + F(NewTargetExpr, NewTargetNode) \ + F(PosHolder, NullaryNode) \ + F(SuperBase, UnaryNode) \ + F(SuperCallExpr, CallNode) \ + F(SetThis, BinaryNode) \ + F(ImportMetaExpr, BinaryNode) \ + F(CallImportExpr, BinaryNode) \ + F(CallImportSpec, BinaryNode) \ + F(InitExpr, BinaryNode) \ + \ + /* Unary operators. */ \ + F(TypeOfNameExpr, UnaryNode) \ + F(TypeOfExpr, UnaryNode) \ + F(VoidExpr, UnaryNode) \ + F(NotExpr, UnaryNode) \ + F(BitNotExpr, UnaryNode) \ + F(AwaitExpr, UnaryNode) \ + \ + /* \ + * Binary operators. \ + * This list must be kept in the same order in several places: \ + * - The binary operators in ParseNode.h \ + * - the binary operators in TokenKind.h \ + * - the precedence list in Parser.cpp \ + * - the JSOp code list in BytecodeEmitter.cpp \ + */ \ + F(CoalesceExpr, ListNode) \ + F(OrExpr, ListNode) \ + F(AndExpr, ListNode) \ + F(BitOrExpr, ListNode) \ + F(BitXorExpr, ListNode) \ + F(BitAndExpr, ListNode) \ + F(StrictEqExpr, ListNode) \ + F(EqExpr, ListNode) \ + F(StrictNeExpr, ListNode) \ + F(NeExpr, ListNode) \ + F(LtExpr, ListNode) \ + F(LeExpr, ListNode) \ + F(GtExpr, ListNode) \ + F(GeExpr, ListNode) \ + F(InstanceOfExpr, ListNode) \ + F(InExpr, ListNode) \ + F(PrivateInExpr, ListNode) \ + F(LshExpr, ListNode) \ + F(RshExpr, ListNode) \ + F(UrshExpr, ListNode) \ + F(AddExpr, ListNode) \ + F(SubExpr, ListNode) \ + F(MulExpr, ListNode) \ + F(DivExpr, ListNode) \ + F(ModExpr, ListNode) \ + F(PowExpr, ListNode) \ + \ + /* Assignment operators (= += -= etc.). */ \ + /* AssignmentNode::test assumes all these are consecutive. */ \ + F(AssignExpr, AssignmentNode) \ + F(AddAssignExpr, AssignmentNode) \ + F(SubAssignExpr, AssignmentNode) \ + F(CoalesceAssignExpr, AssignmentNode) \ + F(OrAssignExpr, AssignmentNode) \ + F(AndAssignExpr, AssignmentNode) \ + F(BitOrAssignExpr, AssignmentNode) \ + F(BitXorAssignExpr, AssignmentNode) \ + F(BitAndAssignExpr, AssignmentNode) \ + F(LshAssignExpr, AssignmentNode) \ + F(RshAssignExpr, AssignmentNode) \ + F(UrshAssignExpr, AssignmentNode) \ + F(MulAssignExpr, AssignmentNode) \ + F(DivAssignExpr, AssignmentNode) \ + F(ModAssignExpr, AssignmentNode) \ F(PowAssignExpr, AssignmentNode) /* @@ -1256,7 +1257,7 @@ class ListNode : public ParseNode { void checkConsistency() const #ifndef DEBUG - {} + {} #endif ; @@ -1483,6 +1484,9 @@ class DeclarationListNode : public ListNode { static bool test(const ParseNode& node) { bool match = node.isKind(ParseNodeKind::VarStmt) || node.isKind(ParseNodeKind::LetDecl) || +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + node.isKind(ParseNodeKind::UsingDecl) || +#endif node.isKind(ParseNodeKind::ConstDecl); MOZ_ASSERT_IF(match, node.is()); return match; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 997b7ce8eece..d13c3d8350b2 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -733,6 +733,9 @@ bool GeneralParser::noteDeclaredName( case DeclarationKind::Let: case DeclarationKind::Const: +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case DeclarationKind::Using: +#endif case DeclarationKind::Class: // The BoundNames of LexicalDeclaration and ForDeclaration must not // contain 'let'. (CatchParameter is the only lexical binding form @@ -4761,6 +4764,12 @@ GeneralParser::declarationName(DeclarationKind declKind, if (isForIn) { *forHeadKind = ParseNodeKind::ForIn; +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + if (declKind == DeclarationKind::Using) { + errorAt(namePos.begin, JSMSG_NO_IN_WITH_USING); + return errorResult(); + } +#endif } else if (isForOf) { *forHeadKind = ParseNodeKind::ForOf; } else { @@ -4797,7 +4806,11 @@ GeneralParser::declarationList( ParseNodeKind* forHeadKind /* = nullptr */, Node* forInOrOfExpression /* = nullptr */) { MOZ_ASSERT(kind == ParseNodeKind::VarStmt || kind == ParseNodeKind::LetDecl || - kind == ParseNodeKind::ConstDecl); + kind == ParseNodeKind::ConstDecl +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + || kind == ParseNodeKind::UsingDecl +#endif + ); DeclarationKind declKind; switch (kind) { @@ -4810,6 +4823,11 @@ GeneralParser::declarationList( case ParseNodeKind::LetDecl: declKind = DeclarationKind::Let; break; +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case ParseNodeKind::UsingDecl: + declKind = DeclarationKind::Using; + break; +#endif default: MOZ_CRASH("Unknown declaration kind"); } @@ -4862,7 +4880,11 @@ template typename ParseHandler::DeclarationListNodeResult GeneralParser::lexicalDeclaration( YieldHandling yieldHandling, DeclarationKind kind) { - MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); + MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + || kind == DeclarationKind::Using +#endif + ); if (options().selfHostingMode) { error(JSMSG_SELFHOSTED_LEXICAL); @@ -4881,10 +4903,23 @@ GeneralParser::lexicalDeclaration( * See 8.1.1.1.6 and the note in 13.2.1. */ DeclarationListNodeType decl; - MOZ_TRY_VAR(decl, - declarationList(yieldHandling, kind == DeclarationKind::Const - ? ParseNodeKind::ConstDecl - : ParseNodeKind::LetDecl)); + ParseNodeKind pnk; + switch (kind) { + case DeclarationKind::Const: + pnk = ParseNodeKind::ConstDecl; + break; +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case DeclarationKind::Using: + pnk = ParseNodeKind::UsingDecl; + break; +#endif + case DeclarationKind::Let: + pnk = ParseNodeKind::LetDecl; + break; + default: + MOZ_CRASH("unexpected node kind"); + } + MOZ_TRY_VAR(decl, declarationList(yieldHandling, pnk)); if (!matchOrInsertSemicolon()) { return errorResult(); } @@ -6440,10 +6475,30 @@ bool GeneralParser::forHeadStart( bool parsingLexicalDeclaration = false; bool letIsIdentifier = false; bool startsWithForOf = false; + if (tt == TokenKind::Const) { parsingLexicalDeclaration = true; tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp); - } else if (tt == TokenKind::Let) { + } +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + else if (tt == TokenKind::Using) { + tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp); + + // Look ahead to find either a 'of' token or if not identifier + TokenKind nextTok = TokenKind::Eof; + if (!tokenStream.peekTokenSameLine(&nextTok)) { + return false; + } + + if (nextTok == TokenKind::Of || !TokenKindIsPossibleIdentifier(nextTok)) { + anyChars.ungetToken(); // we didnt find a valid case of using decl put + // back the token + } else { + parsingLexicalDeclaration = true; + } + } +#endif + else if (tt == TokenKind::Let) { // We could have a {For,Lexical}Declaration, or we could have a // LeftHandSideExpression with lookahead restrictions so it's not // ambiguous with the former. Check for a continuation of the former @@ -6499,13 +6554,27 @@ bool GeneralParser::forHeadStart( // statements. ParseContext::Statement forHeadStmt(pc_, StatementKind::ForLoopLexicalHead); - MOZ_TRY_VAR_OR_RETURN( - *forInitialPart, - declarationList(yieldHandling, - tt == TokenKind::Const ? ParseNodeKind::ConstDecl - : ParseNodeKind::LetDecl, - forHeadKind, forInOrOfExpression), - false); + ParseNodeKind declKind; + switch (tt) { + case TokenKind::Const: + declKind = ParseNodeKind::ConstDecl; + break; +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case TokenKind::Using: + declKind = ParseNodeKind::UsingDecl; + break; +#endif + case TokenKind::Let: + declKind = ParseNodeKind::LetDecl; + break; + default: + MOZ_CRASH("unexpected node kind"); + } + + MOZ_TRY_VAR_OR_RETURN(*forInitialPart, + declarationList(yieldHandling, declKind, forHeadKind, + forInOrOfExpression), + false); return true; } @@ -9612,6 +9681,26 @@ GeneralParser::statementListItem( // their heads to handle |in| in this situation. return lexicalDeclaration(yieldHandling, DeclarationKind::Const); +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + case TokenKind::Using: { + TokenKind nextTok = TokenKind::Eol; + if (!tokenStream.peekTokenSameLine(&nextTok)) { + return errorResult(); + } + if (!TokenKindIsPossibleIdentifier(nextTok)) { + if (!tokenStream.peekToken(&nextTok)) { + return errorResult(); + } + // labelled statement could be like using\n:\nexpr + if (nextTok == TokenKind::Colon) { + return labeledStatement(yieldHandling); + } + return expressionStatement(yieldHandling); + } + return lexicalDeclaration(yieldHandling, DeclarationKind::Using); + } +#endif + // ImportDeclaration (only inside modules) case TokenKind::Import: return importDeclarationOrImportExpr(yieldHandling); diff --git a/js/src/frontend/ReservedWords.h b/js/src/frontend/ReservedWords.h index 723f310ecf2e..87b7fcab23c1 100644 --- a/js/src/frontend/ReservedWords.h +++ b/js/src/frontend/ReservedWords.h @@ -9,70 +9,71 @@ #ifndef vm_ReservedWords_h #define vm_ReservedWords_h -#define FOR_EACH_JAVASCRIPT_RESERVED_WORD(MACRO) \ - MACRO(false, false_, TokenKind::False) \ - MACRO(true, true_, TokenKind::True) \ - MACRO(null, null, TokenKind::Null) \ - \ - /* Keywords. */ \ - MACRO(break, break_, TokenKind::Break) \ - MACRO(case, case_, TokenKind::Case) \ - MACRO(catch, catch_, TokenKind::Catch) \ - MACRO(const, const_, TokenKind::Const) \ - MACRO(continue, continue_, TokenKind::Continue) \ - MACRO(debugger, debugger, TokenKind::Debugger) \ - MACRO(default, default_, TokenKind::Default) \ - MACRO(delete, delete_, TokenKind::Delete) \ - MACRO(do, do_, TokenKind::Do) \ - MACRO(else, else_, TokenKind::Else) \ - MACRO(finally, finally_, TokenKind::Finally) \ - MACRO(for, for_, TokenKind::For) \ - MACRO(function, function, TokenKind::Function) \ - MACRO(if, if_, TokenKind::If) \ - MACRO(in, in, TokenKind::In) \ - MACRO(instanceof, instanceof, TokenKind::InstanceOf) \ - MACRO(new, new_, TokenKind::New) \ - MACRO(return, return_, TokenKind::Return) \ - MACRO(switch, switch_, TokenKind::Switch) \ - MACRO(this, this_, TokenKind::This) \ - MACRO(throw, throw_, TokenKind::Throw) \ - MACRO(try, try_, TokenKind::Try) \ - MACRO(typeof, typeof_, TokenKind::TypeOf) \ - MACRO(var, var, TokenKind::Var) \ - MACRO(void, void_, TokenKind::Void) \ - MACRO(while, while_, TokenKind::While) \ - MACRO(with, with, TokenKind::With) \ - MACRO(import, import, TokenKind::Import) \ - MACRO(export, export_, TokenKind::Export) \ - MACRO(class, class_, TokenKind::Class) \ - MACRO(extends, extends, TokenKind::Extends) \ - IF_DECORATORS(MACRO(accessor, accessor, TokenKind::Accessor)) \ - MACRO(super, super, TokenKind::Super) \ - \ - /* Future reserved words. */ \ - MACRO(enum, enum_, TokenKind::Enum) \ - \ - /* Future reserved words, but only in strict mode. */ \ - MACRO(implements, implements, TokenKind::Implements) \ - MACRO(interface, interface, TokenKind::Interface) \ - MACRO(package, package, TokenKind::Package) \ - MACRO(private, private_, TokenKind::Private) \ - MACRO(protected, protected_, TokenKind::Protected) \ - MACRO(public, public_, TokenKind::Public) \ - \ - /* Contextual keywords. */ \ - MACRO(as, as, TokenKind::As) \ - MACRO(assert, assert_, TokenKind::Assert) \ - MACRO(async, async, TokenKind::Async) \ - MACRO(await, await, TokenKind::Await) \ - MACRO(from, from, TokenKind::From) \ - MACRO(get, get, TokenKind::Get) \ - MACRO(let, let, TokenKind::Let) \ - MACRO(meta, meta, TokenKind::Meta) \ - MACRO(of, of, TokenKind::Of) \ - MACRO(set, set, TokenKind::Set) \ - MACRO(static, static_, TokenKind::Static) \ - MACRO(target, target, TokenKind::Target) \ +#define FOR_EACH_JAVASCRIPT_RESERVED_WORD(MACRO) \ + MACRO(false, false_, TokenKind::False) \ + MACRO(true, true_, TokenKind::True) \ + MACRO(null, null, TokenKind::Null) \ + \ + /* Keywords. */ \ + MACRO(break, break_, TokenKind::Break) \ + MACRO(case, case_, TokenKind::Case) \ + MACRO(catch, catch_, TokenKind::Catch) \ + MACRO(const, const_, TokenKind::Const) \ + MACRO(continue, continue_, TokenKind::Continue) \ + MACRO(debugger, debugger, TokenKind::Debugger) \ + MACRO(default, default_, TokenKind::Default) \ + MACRO(delete, delete_, TokenKind::Delete) \ + MACRO(do, do_, TokenKind::Do) \ + MACRO(else, else_, TokenKind::Else) \ + MACRO(finally, finally_, TokenKind::Finally) \ + MACRO(for, for_, TokenKind::For) \ + MACRO(function, function, TokenKind::Function) \ + MACRO(if, if_, TokenKind::If) \ + MACRO(in, in, TokenKind::In) \ + MACRO(instanceof, instanceof, TokenKind::InstanceOf) \ + MACRO(new, new_, TokenKind::New) \ + MACRO(return, return_, TokenKind::Return) \ + MACRO(switch, switch_, TokenKind::Switch) \ + MACRO(this, this_, TokenKind::This) \ + MACRO(throw, throw_, TokenKind::Throw) \ + MACRO(try, try_, TokenKind::Try) \ + MACRO(typeof, typeof_, TokenKind::TypeOf) \ + IF_EXPLICIT_RESOURCE_MANAGEMENT(MACRO(using, using_, TokenKind::Using)) \ + MACRO(var, var, TokenKind::Var) \ + MACRO(void, void_, TokenKind::Void) \ + MACRO(while, while_, TokenKind::While) \ + MACRO(with, with, TokenKind::With) \ + MACRO(import, import, TokenKind::Import) \ + MACRO(export, export_, TokenKind::Export) \ + MACRO(class, class_, TokenKind::Class) \ + MACRO(extends, extends, TokenKind::Extends) \ + IF_DECORATORS(MACRO(accessor, accessor, TokenKind::Accessor)) \ + MACRO(super, super, TokenKind::Super) \ + \ + /* Future reserved words. */ \ + MACRO(enum, enum_, TokenKind::Enum) \ + \ + /* Future reserved words, but only in strict mode. */ \ + MACRO(implements, implements, TokenKind::Implements) \ + MACRO(interface, interface, TokenKind::Interface) \ + MACRO(package, package, TokenKind::Package) \ + MACRO(private, private_, TokenKind::Private) \ + MACRO(protected, protected_, TokenKind::Protected) \ + MACRO(public, public_, TokenKind::Public) \ + \ + /* Contextual keywords. */ \ + MACRO(as, as, TokenKind::As) \ + MACRO(assert, assert_, TokenKind::Assert) \ + MACRO(async, async, TokenKind::Async) \ + MACRO(await, await, TokenKind::Await) \ + MACRO(from, from, TokenKind::From) \ + MACRO(get, get, TokenKind::Get) \ + MACRO(let, let, TokenKind::Let) \ + MACRO(meta, meta, TokenKind::Meta) \ + MACRO(of, of, TokenKind::Of) \ + MACRO(set, set, TokenKind::Set) \ + MACRO(static, static_, TokenKind::Static) \ + MACRO(target, target, TokenKind::Target) \ MACRO(yield, yield, TokenKind::Yield) #endif /* vm_ReservedWords_h */ diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index fa63b1e9d34a..ab2d938b70d4 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -693,6 +693,9 @@ class SyntaxParseHandler { MOZ_ASSERT(kind != ParseNodeKind::LetDecl); MOZ_ASSERT(kind != ParseNodeKind::ConstDecl); MOZ_ASSERT(kind != ParseNodeKind::ParamsBody); +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + MOZ_ASSERT(kind != ParseNodeKind::UsingDecl); +#endif return NodeGeneric; } @@ -706,7 +709,11 @@ class SyntaxParseHandler { return NodeVarDeclaration; } MOZ_ASSERT(kind == ParseNodeKind::LetDecl || - kind == ParseNodeKind::ConstDecl); + kind == ParseNodeKind::ConstDecl +#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT + || kind == ParseNodeKind::UsingDecl +#endif + ); return NodeLexicalDeclaration; } diff --git a/js/src/frontend/TokenKind.h b/js/src/frontend/TokenKind.h index 8117603f346d..cbe87fe39350 100644 --- a/js/src/frontend/TokenKind.h +++ b/js/src/frontend/TokenKind.h @@ -143,6 +143,7 @@ MACRO(Set, "'set'") \ MACRO(Static, "'static'") \ MACRO(Target, "'target'") \ + IF_EXPLICIT_RESOURCE_MANAGEMENT(MACRO(Using, "'using'")) \ MACRO(Yield, "'yield'") \ RANGE(ContextualKeywordLast, Yield) \ \ diff --git a/js/src/frontend/moz.build b/js/src/frontend/moz.build index 0a820086a50b..db4d8a578846 100644 --- a/js/src/frontend/moz.build +++ b/js/src/frontend/moz.build @@ -14,12 +14,17 @@ include("../js-cxxflags.mozbuild") # Generate frontend/ReservedWordsGenerated.h from frontend/ReservedWords.h +flags = [] if CONFIG["ENABLE_DECORATORS"]: + flags.append("--enable-decorators") +if CONFIG["ENABLE_EXPLICIT_RESOURCE_MANAGEMENT"]: + flags.append("--enable-explicit-resource-management") +if flags: GeneratedFile( "ReservedWordsGenerated.h", script="GenerateReservedWords.py", inputs=["ReservedWords.h"], - flags=["--enable-decorators"], + flags=flags, ) else: GeneratedFile( diff --git a/js/src/jit-test/tests/explicit-resource-management/syntax.js b/js/src/jit-test/tests/explicit-resource-management/syntax.js new file mode 100644 index 000000000000..f120aae9ca25 --- /dev/null +++ b/js/src/jit-test/tests/explicit-resource-management/syntax.js @@ -0,0 +1,159 @@ +// |jit-test| skip-if: !getBuildConfiguration("explicit-resource-management") + +load(libdir + "asserts.js"); + +Reflect.parse("using x = {}"); +Reflect.parse("using x = fn()"); +Reflect.parse("{ using x = fn(); }"); +Reflect.parse("function f() { using x = fn(); }"); +Reflect.parse("using a = fn1(), b = fn2();"); +Reflect.parse("for (using x of y) {}"); +Reflect.parse("async function fn() { for await (using x of y) {} }"); +Reflect.parse("using x = null"); +Reflect.parse("using x = undefined"); +// Existing syntaxes that should not break +Reflect.parse("for (using of y) {}"); +Reflect.parse("for (using of of) {}"); +Reflect.parse("for (using\nof y) {}"); +Reflect.parse("const using = 10"); +Reflect.parse("let using = 10"); +Reflect.parse("var using = 10"); +Reflect.parse("using = 10"); +Reflect.parse("using + 1"); +Reflect.parse("using++"); +Reflect.parse("using\nx = 10"); +Reflect.parse("using = {x: 10}"); +Reflect.parse("x = { using: 10 }"); +Reflect.parse("x.using = 10"); +Reflect.parse("x\n.using = 10"); +Reflect.parse("using.x = 10"); +Reflect.parse("using\n.x = 10"); +Reflect.parse("for (using[1] of {}) {}"); +Reflect.parse("for (using\n[1] of {}) {}") +Reflect.parse("for (using.x of {}) {}"); +Reflect.parse("for (using\n.x of {}) {}"); +Reflect.parse("for (x.using in {}) {}"); +Reflect.parse("for (x\n.using in {}) {}") +Reflect.parse("using: x = 10;"); +Reflect.parse("using\n: x = 10;"); +Reflect.parse("using /a/g;"); +Reflect.parse("/using/g"); +Reflect.parse("export const using = 10", { target: "module" }); +Reflect.parse("import using from 'xyz'", { target: "module" }); + +// ast checks +const ast = Reflect.parse("using x = {}"); +assertEq(ast.body[0].type, "VariableDeclaration"); +assertEq(ast.body[0].kind, "using"); +assertEq(ast.body[0].declarations[0].type, "VariableDeclarator"); +assertEq(ast.body[0].declarations[0].id.name, "x"); + +const ast2 = Reflect.parse("for (using x of y) {}"); +assertEq(ast2.body[0].type, "ForOfStatement"); +assertEq(ast2.body[0].left.type, "VariableDeclaration"); +assertEq(ast2.body[0].left.kind, "using"); +assertEq(ast2.body[0].left.declarations[0].type, "VariableDeclarator"); +assertEq(ast2.body[0].left.declarations[0].id.type, "Identifier"); +assertEq(ast2.body[0].left.declarations[0].id.name, "x"); + +const ast3 = Reflect.parse("async function fn() { for await (using x of y) {} }"); +const forStatement = ast3.body[0].body.body[0]; +assertEq(forStatement.type, "ForOfStatement"); +assertEq(forStatement.left.type, "VariableDeclaration"); +assertEq(forStatement.left.kind, "using"); +assertEq(forStatement.left.declarations[0].type, "VariableDeclarator"); +assertEq(forStatement.left.declarations[0].id.type, "Identifier"); +assertEq(forStatement.left.declarations[0].id.name, "x"); + +const ast4 = Reflect.parse("using = 10"); +assertEq(ast4.body[0].type, "ExpressionStatement"); +assertEq(ast4.body[0].expression.type, "AssignmentExpression"); +assertEq(ast4.body[0].expression.left.type, "Identifier"); +assertEq(ast4.body[0].expression.left.name, "using"); + +const ast5 = Reflect.parse("for (using of y) {}"); +assertEq(ast5.body[0].type, "ForOfStatement"); +assertEq(ast5.body[0].left.type, "Identifier"); +assertEq(ast5.body[0].left.name, "using"); + +const ast6 = Reflect.parse("using + 1"); +assertEq(ast6.body[0].type, "ExpressionStatement"); +assertEq(ast6.body[0].expression.type, "BinaryExpression"); +assertEq(ast6.body[0].expression.left.type, "Identifier"); +assertEq(ast6.body[0].expression.left.name, "using"); + +const ast7 = Reflect.parse("for (using of of) {}"); +assertEq(ast7.body[0].type, "ForOfStatement"); +assertEq(ast7.body[0].left.type, "Identifier"); +assertEq(ast7.body[0].left.name, "using"); +assertEq(ast7.body[0].right.type, "Identifier"); +assertEq(ast7.body[0].right.name, "of"); + +const ast8 = Reflect.parse("for (using\nof y) {}"); +assertEq(ast8.body[0].type, "ForOfStatement"); +assertEq(ast8.body[0].left.type, "Identifier"); +assertEq(ast8.body[0].left.name, "using"); +assertEq(ast8.body[0].right.type, "Identifier"); +assertEq(ast8.body[0].right.name, "y"); + +const ast9 = Reflect.parse("for (using[1] of {}) {}"); +assertEq(ast9.body[0].type, "ForOfStatement"); +assertEq(ast9.body[0].left.type, "MemberExpression"); +assertEq(ast9.body[0].left.object.type, "Identifier"); +assertEq(ast9.body[0].left.object.name, "using"); +assertEq(ast9.body[0].left.property.type, "Literal"); +assertEq(ast9.body[0].left.property.value, 1); + +const ast10 = Reflect.parse("for (using\n[1] of {}) {}"); +assertEq(ast10.body[0].type, "ForOfStatement"); +assertEq(ast10.body[0].left.type, "MemberExpression"); +assertEq(ast10.body[0].left.object.type, "Identifier"); +assertEq(ast10.body[0].left.object.name, "using"); +assertEq(ast10.body[0].left.property.type, "Literal"); +assertEq(ast10.body[0].left.property.value, 1); + +const ast11 = Reflect.parse("/using/g"); +assertEq(ast11.body[0].type, "ExpressionStatement"); +assertEq(ast11.body[0].expression.type, "Literal"); +assertEq(ast11.body[0].expression.value.source, "using"); +assertEq(ast11.body[0].expression.value.flags, "g"); + +const ast12 = Reflect.parse("using: x = 10;"); +assertEq(ast12.body[0].type, "LabeledStatement"); +assertEq(ast12.body[0].label.type, "Identifier"); +assertEq(ast12.body[0].label.name, "using"); + +const ast13 = Reflect.parse("using\n: x = 10;"); +assertEq(ast13.body[0].type, "LabeledStatement"); +assertEq(ast13.body[0].label.type, "Identifier"); +assertEq(ast13.body[0].label.name, "using"); + +const ast14 = Reflect.parse("using /a/g;"); +// should be parsed as division, not regex +assertEq(ast14.body[0].type, "ExpressionStatement"); +assertEq(ast14.body[0].expression.type, "BinaryExpression"); +assertEq(ast14.body[0].expression.operator, "/"); +assertEq(ast14.body[0].expression.left.type, "BinaryExpression"); +assertEq(ast14.body[0].expression.left.operator, "/"); + +const ast15 = Reflect.parse("import using from 'xyz'", { target: "module" }); +assertEq(ast15.body[0].type, "ImportDeclaration"); +assertEq(ast15.body[0].specifiers[0].type, "ImportSpecifier"); +assertEq(ast15.body[0].specifiers[0].name.name, "using"); + +assertThrowsInstanceOf(() => Reflect.parse("const using x = fn()"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("let using x = fn()"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("var using x = fn()"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("using const x = fn()"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("using let x = fn()"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("using var x = fn()"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("using x = fn(), x = fn()"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("for (using x in y) {}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("for (using\nx of y) {}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("for (using of of y) {}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("using /a/;"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("for (using /a/g of y) {}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("if(1) using x = {}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("for (;;) using x = 10;"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("foo: using x = 10;"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("export using x = 10", { target: "module" }), SyntaxError); \ No newline at end of file diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index a8e874d410a9..b4b828d52ea4 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -605,6 +605,7 @@ MACRO_(use_asm_, "use asm") \ MACRO_(use_strict_, "use strict") \ MACRO_(useGrouping, "useGrouping") \ + IF_EXPLICIT_RESOURCE_MANAGEMENT(MACRO_(using_, "using")) \ MACRO_(UTC, "UTC") \ MACRO_(value, "value") \ MACRO_(valueOf, "valueOf") \