Bug 1891041 - Implement parsing for explicit resource management. r=arai

Implements simple parsing for the explicit resource management
proposal.

Differential Revision: https://phabricator.services.mozilla.com/D207371
This commit is contained in:
Debadree Chatterjee 2024-05-19 08:01:59 +00:00
parent c3ecdeeb2d
commit 49af7ffb9e
18 changed files with 653 additions and 270 deletions

View File

@ -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
# ===================================================

View File

@ -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 */

View File

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

View File

@ -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<ListNode>(), 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<ListNode>(), 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<ListNode>(),
initNode->isKind(ParseNodeKind::LetDecl) ||
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
initNode->isKind(ParseNodeKind::UsingDecl) ||
#endif
initNode->isKind(ParseNodeKind::ConstDecl),
&var)) {
return false;

View File

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

View File

@ -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:

View File

@ -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<ListNode>());
*result = false;
return true;

View File

@ -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,

View File

@ -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:

View File

@ -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:

View File

@ -121,6 +121,7 @@ class FunctionBox;
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) \
@ -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<ListNode>());
return match;

View File

@ -733,6 +733,9 @@ bool GeneralParser<ParseHandler, Unit>::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<ParseHandler, Unit>::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<ParseHandler, Unit>::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<ParseHandler, Unit>::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 <class ParseHandler, typename Unit>
typename ParseHandler::DeclarationListNodeResult
GeneralParser<ParseHandler, Unit>::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<ParseHandler, Unit>::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<ParseHandler, Unit>::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,12 +6554,26 @@ bool GeneralParser<ParseHandler, Unit>::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),
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<ParseHandler, Unit>::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);

View File

@ -38,6 +38,7 @@
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) \

View File

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

View File

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

View File

@ -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(

View File

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

View File

@ -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") \