diff --git a/JSTests/ChakraCore/test/es6/globalCatchNewTargetSyntaxError.baseline-jsc b/JSTests/ChakraCore/test/es6/globalCatchNewTargetSyntaxError.baseline-jsc index 692195c0e925c..e2a9a7ec49e74 100644 --- a/JSTests/ChakraCore/test/es6/globalCatchNewTargetSyntaxError.baseline-jsc +++ b/JSTests/ChakraCore/test/es6/globalCatchNewTargetSyntaxError.baseline-jsc @@ -1,2 +1,2 @@ -Exception: SyntaxError: new.target is only valid inside functions. +Exception: SyntaxError: new.target is only valid inside functions or static blocks. at globalCatchNewTargetSyntaxError.js:6 diff --git a/JSTests/ChakraCore/test/es6/globalNewTargetSyntaxError.baseline-jsc b/JSTests/ChakraCore/test/es6/globalNewTargetSyntaxError.baseline-jsc index 88e6bc8a80278..2345383e4645c 100644 --- a/JSTests/ChakraCore/test/es6/globalNewTargetSyntaxError.baseline-jsc +++ b/JSTests/ChakraCore/test/es6/globalNewTargetSyntaxError.baseline-jsc @@ -1,2 +1,2 @@ -Exception: SyntaxError: new.target is only valid inside functions. +Exception: SyntaxError: new.target is only valid inside functions or static blocks. at globalNewTargetSyntaxError.js:6 diff --git a/JSTests/ChakraCore/test/es6/globalParamCatchNewTargetSyntaxError.baseline-jsc b/JSTests/ChakraCore/test/es6/globalParamCatchNewTargetSyntaxError.baseline-jsc index cb886bacb83f1..985ea22f36972 100644 --- a/JSTests/ChakraCore/test/es6/globalParamCatchNewTargetSyntaxError.baseline-jsc +++ b/JSTests/ChakraCore/test/es6/globalParamCatchNewTargetSyntaxError.baseline-jsc @@ -1,2 +1,2 @@ -Exception: SyntaxError: new.target is only valid inside functions. +Exception: SyntaxError: new.target is only valid inside functions or static blocks. at globalParamCatchNewTargetSyntaxError.js:7 diff --git a/JSTests/stress/class-static-block.js b/JSTests/stress/class-static-block.js new file mode 100644 index 0000000000000..b40634e805d0d --- /dev/null +++ b/JSTests/stress/class-static-block.js @@ -0,0 +1,649 @@ +function assert(b) { + if (!b) { + throw "bad assert!" + } +} + +function shouldThrow(func, errorMessage) { + var errorThrown = false; + var error = null; + try { + func(); + } catch (e) { + errorThrown = true; + error = e; + } + if (!errorThrown) + throw new Error('not thrown'); + if (String(error) !== errorMessage) + throw new Error(` + bad error: ${String(error)} + expected error: ${errorMessage} + `); +} + +// ---------- single static block ---------- +{ + var y = 'Outer y'; + + class A { + static field = 'Inner y'; + static { + var y = this.field; + } + } + + assert(y === 'Outer y'); +} + +// ---------- multiple static blocks ---------- +{ + class C { + static { + assert(this.x === undefined); + } + static x = 10; + static { + assert(this.x === 10); + assert(this.y === undefined); + } + static y = 20; + static { + assert(this.y === 20); + } + } +} + +// ---------- use this ---------- +{ + class A { + static field = 'A static field'; + static { + assert(this.field === 'A static field'); + } + } +} + +// ---------- use super ---------- +{ + class A { + static fieldA = 'A.fieldA'; + } + class B extends A { + static { + assert(super.fieldA === 'A.fieldA'); + } + } +} + +// ---------- access to private fields ---------- +{ + let getDPrivateField; + + class D { + #privateField; + constructor(v) { + this.#privateField = v; + } + static { + getDPrivateField = (d) => d.#privateField; + } + } + + assert(getDPrivateField(new D('private')) === 'private'); +} + +// ---------- "friend" access ---------- +{ + let A, B; + + let friendA; + + A = class A { + #x; + constructor(x) { + this.#x = x; + } + static { + friendA = { + getX(obj) { return obj.#x }, + setX(obj, value) { obj.#x = value } + }; + } + getX() { + return this.#x; + } + }; + + B = class B { + constructor(a) { + const x = friendA.getX(a); + friendA.setX(a, x + 32); + } + }; + + let a = new A(10); + new B(a); + + assert(a.getX() === 42); +} + +// ---------- break ---------- +{ + class C { + static { + while (false) + break; // isStaticBlock = true, isValid = false, isCurrentScopeValid = true + } + } +} + +{ + class C { + static { + while (false) { + break; // isStaticBlock = true, isValid = true, isCurrentScopeValid = false + } + } + } +} + +shouldThrow(() => { + eval(` + while (false) { + class C { + static { + break; // isStaticBlock = true, isValid = false, isCurrentScopeValid = false + } + } + } + `); +}, `SyntaxError: 'break' cannot cross static block boundary.`); + +shouldThrow(() => { + eval(` + class C { + static { + break; // isStaticBlock = true, isValid = false, isCurrentScopeValid = false + } + } + `); +}, `SyntaxError: 'break' cannot cross static block boundary.`); + +shouldThrow(() => { + eval(` + while (false) { + class C { + static { + { + break; // isStaticBlock = true, isValid = false, isCurrentScopeValid = false + } + } + } + } + `); +}, `SyntaxError: 'break' cannot cross static block boundary.`); + +shouldThrow(() => { + eval(` + class C { + static { + { + break; // isStaticBlock = true, isValid = false, isCurrentScopeValid = false + } + } + } + `); +}, `SyntaxError: 'break' cannot cross static block boundary.`); + +// ---------- continue ---------- +{ + class C { + static { + while (false) + continue; // isStaticBlock = true, isValid = false, isCurrentScopeValid = true + } + } +} + +{ + class C { + static { + while (false) { + continue; // isStaticBlock = true, isValid = true, isCurrentScopeValid = false + } + } + } +} + +shouldThrow(() => { + eval(` + while (false) { + class C { + static { + continue; // isStaticBlock = true, isValid = false, isCurrentScopeValid = false + } + } + } + `); +}, `SyntaxError: 'continue' cannot cross static block boundary.`); + +shouldThrow(() => { + eval(` + class C { + static { + continue; // isStaticBlock = true, isValid = false, isCurrentScopeValid = false + } + } + `); +}, `SyntaxError: 'continue' cannot cross static block boundary.`); + +shouldThrow(() => { + eval(` + while (false) { + class C { + static { + { + continue; // isStaticBlock = true, isValid = false, isCurrentScopeValid = false + } + } + } + } + `); +}, `SyntaxError: 'continue' cannot cross static block boundary.`); + +shouldThrow(() => { + eval(` + class C { + static { + { + continue; // isStaticBlock = true, isValid = false, isCurrentScopeValid = false + } + } + } + `); +}, `SyntaxError: 'continue' cannot cross static block boundary.`); + +// ---------- arguments ---------- +{ + class C { + static { + function inner() { + [arguments](); + } + } + } +} + +{ + class C { + static { + class B { + inner() { + [arguments](); + } + } + } + } +} + +{ + class C { + static { + function inner() { + arguments[0]; + } + } + } +} + +{ + class C { + static { + class B { + inner() { + arguments[0]; + } + } + } + } +} + +{ + class C { + static { + (a, b) => { + this.arguments[0]; + }; + } + } +} + +shouldThrow(() => { + eval(` + class C { + static { + arguments; + } + } + `); +}, `SyntaxError: Cannot use 'arguments' as an identifier in static block.`); + +// ---------- yield ---------- + +{ + class A { + static { + function* gen() { + yield 42; + } + } + } +} + +{ + class A { + static { + class B { + *gen() { + yield 42; + } + } + } + + } +} + +shouldThrow(() => { + eval(` + class C { + static { + function inner() { + yield 0; + } + } + } + `); +}, `SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.`); + +shouldThrow(() => { + eval(` + class C { + static { + yield 0; + } + } + `); +}, `SyntaxError: Unexpected keyword 'yield'. Cannot use 'yield' within static block.`); + +// ---------- await ---------- +{ + class C { + static { + function inner() { + try { } catch (await) { } + } + } + } +} + +{ + class C { + static { + class B { + inner() { + try { } catch (await) { } + } + } + } + } +} + +{ + class C { + static { + async function inner() { + await 0; + } + } + } +} + +{ + class C { + static { + class B { + async inner() { + await 0; + } + } + } + } +} + +{ + class C { + static { + function inner() { + let await = 10; + } + } + } +} + +{ + class C { + static { + class B { + inner() { + let await = 10; + } + } + } + } +} + +{ + async function outer() { + class C { + static { + async function inner() { + await 0; + } + } + } + } +} + +{ + async function outer() { + class C { + static { + class B { + async inner() { + await 0; + } + } + } + } + } +} + +{ + async function outer() { + class C { + static { + function inner() { + let await = 10; + } + } + } + } +} + +{ + async function outer() { + class C { + static { + class B { + inner() { + let await = 10; + } + } + } + } + } +} + +{ + class C { + static { + function inner() { + [await](); + } + } + } +} + +{ + class C { + static { + class B { + inner() { + [await](); + } + } + } + } +} + +// ---------- others ---------- +{ + function doSomethingWith(x) { + return { + y: x + 1, + z: x + 2 + }; + } + + class C { + static x = 10; + static y; + static z; + static { + try { + const obj = doSomethingWith(this.x); + C.y = obj.y; + C["z"] = obj.z; + } catch { + } + } + } + + assert(C.y === 11); + assert(C.z === 12); +} + +{ + class C { + static y; + static z; + static { + try { + throw "err"; + } catch { + C.y = 13; + C['z'] = 14; + } + } + } + + assert(C.y === 13); + assert(C.z === 14); +} + +{ + var value = null; + class C { + static { + function inner() { + value = new.target; + } + inner(); + } + } + assert(value === undefined); +} + +{ + class C { + static { + value = new.target; + } + } + assert(value === undefined); +} + +{ + class C { + static { + function inner() { + { + return 10; + } + } + } + } +} + +{ + class C { + static { + function inner() { + Promise.resolve().then(makeMasquerader(), makeMasquerader()); + } + { + Promise.resolve().then(makeMasquerader(), makeMasquerader()); + } + } + } +} + +{ + class C { + static { + { + function foo(arg) { + let o; + if (arg) { + o = {}; + } else { + o = function() { } + } + return typeof o; + } + noInline(foo); + + for (let i = 0; i < 10000; i++) { + let bool = !!(i % 2); + let result = foo(bool); + if (bool) + assert(result === "object"); + else + assert(result === "function"); + } + } + } + } +} + +{ + function foo() { + assert(foo.caller === null); + } + class C { + static { + foo(); + } + } +} diff --git a/JSTests/stress/code-cache-incorrect-caching.js b/JSTests/stress/code-cache-incorrect-caching.js index 9e51af78cc9c5..83f5753e668ce 100644 --- a/JSTests/stress/code-cache-incorrect-caching.js +++ b/JSTests/stress/code-cache-incorrect-caching.js @@ -35,7 +35,7 @@ var global = this; eval('new.target'); } catch (e) { thrown = true; - shouldBe(String(e), "SyntaxError: new.target is only valid inside functions."); + shouldBe(String(e), "SyntaxError: new.target is only valid inside functions or static blocks."); } shouldBe(thrown, true); `); @@ -45,7 +45,7 @@ var global = this; globalEval('new.target'); } catch (e) { thrown = true; - shouldBe(String(e), "SyntaxError: new.target is only valid inside functions."); + shouldBe(String(e), "SyntaxError: new.target is only valid inside functions or static blocks."); } shouldBe(thrown, true); } diff --git a/JSTests/stress/modules-syntax-error.js b/JSTests/stress/modules-syntax-error.js index 701650df5d3a8..8810c9fb5ebbc 100644 --- a/JSTests/stress/modules-syntax-error.js +++ b/JSTests/stress/modules-syntax-error.js @@ -356,7 +356,7 @@ export * as "\ud800" from "./ok.js" checkModuleSyntaxError(String.raw` new.target; -`, `SyntaxError: new.target is only valid inside functions.:2`); +`, `SyntaxError: new.target is only valid inside functions or static blocks.:2`); checkModuleSyntaxError(String.raw` super(); diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp index 23fa6639e603e..bc2a2d1d03992 100644 --- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp +++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp @@ -649,7 +649,7 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe continue; } - if (p->isStaticClassField()) { + if (p->isStaticClassElement()) { ASSERT(staticFieldLocations); staticFieldLocations->append(p->position()); continue; @@ -719,7 +719,7 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe continue; } - if (p->isStaticClassField()) { + if (p->isStaticClassElement()) { ASSERT(staticFieldLocations); staticFieldLocations->append(p->position()); continue; @@ -1263,6 +1263,25 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re return ret; } +// ------------------------------ StaticBlockFunctionCallNode ---------------------------------- + +RegisterID* StaticBlockFunctionCallNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + // There are two possible optimizations in this implementation. + // https://bugs.webkit.org/show_bug.cgi?id=245925 + RefPtr homeObject = emitHomeObjectForCallee(generator); + RefPtr function = generator.emitNode(m_expr); + emitPutHomeObject(generator, function.get(), homeObject.get()); + RefPtr returnValue = generator.finalDestination(dst, function.get()); + + CallArguments callArguments(generator, nullptr); + generator.move(callArguments.thisRegister(), generator.thisRegister()); + RefPtr result = generator.emitCallInTailPosition(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes); + + generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); + return result.get(); +} + // ------------------------------ FunctionCallResolveNode ---------------------------------- RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h index 49d818591a444..c2cb6e23528ca 100644 --- a/Source/JavaScriptCore/parser/ASTBuilder.h +++ b/Source/JavaScriptCore/parser/ASTBuilder.h @@ -130,6 +130,7 @@ class ASTBuilder { static constexpr OptionSet DontBuildStrings = { }; ExpressionNode* makeBinaryNode(const JSTokenLocation&, int token, std::pair, std::pair); + ExpressionNode* makeStaticBlockFunctionCallNode(const JSTokenLocation&, ExpressionNode* func, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); ExpressionNode* makeFunctionCallNode(const JSTokenLocation&, ExpressionNode* func, bool previousBaseWasSuper, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth, bool isOptionalCall); JSC::SourceElements* createSourceElements() { return new (m_parserArena) JSC::SourceElements(); } @@ -520,6 +521,10 @@ class ASTBuilder { return new (m_parserArena) PropertyNode(ident, methodDef, type, SuperBinding::Needed, tag); } + PropertyNode* createProperty(const Identifier* propertyName, PropertyNode::Type type, SuperBinding superBinding, ClassElementTag tag) + { + return new (m_parserArena) PropertyNode(*propertyName, type, superBinding, tag); + } PropertyNode* createProperty(const Identifier* propertyName, ExpressionNode* node, PropertyNode::Type type, SuperBinding superBinding, InferName inferName, ClassElementTag tag) { if (inferName == InferName::Allowed) { @@ -1417,6 +1422,11 @@ ExpressionNode* ASTBuilder::makeCoalesceNode(const JSTokenLocation& location, Ex return new (m_parserArena) CoalesceNode(location, expr1, expr2, hasAbsorbedOptionalChain); } +ExpressionNode* ASTBuilder::makeStaticBlockFunctionCallNode(const JSTokenLocation& location, ExpressionNode* func, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) +{ + return new (m_parserArena) StaticBlockFunctionCallNode(location, func, divot, divotStart, divotEnd); +} + ExpressionNode* ASTBuilder::makeFunctionCallNode(const JSTokenLocation& location, ExpressionNode* func, bool previousBaseWasSuper, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth, bool isOptionalCall) { ASSERT(divot.offset >= divot.lineStartOffset); diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h index b4df24458926a..1d8459202b4f6 100644 --- a/Source/JavaScriptCore/parser/NodeConstructors.h +++ b/Source/JavaScriptCore/parser/NodeConstructors.h @@ -250,6 +250,14 @@ namespace JSC { { } + inline PropertyNode::PropertyNode(const Identifier& name, Type type, SuperBinding superBinding, ClassElementTag tag) + : m_name(&name) + , m_type(type) + , m_needsSuperBinding(superBinding == SuperBinding::Needed) + , m_classElementTag(static_cast(tag)) + { + } + inline PropertyNode::PropertyNode(const Identifier& name, ExpressionNode* assign, Type type, SuperBinding superBinding, ClassElementTag tag) : m_name(&name) , m_expression(nullptr) @@ -407,6 +415,14 @@ namespace JSC { ASSERT(divot.offset >= divotStart.offset); } + inline StaticBlockFunctionCallNode::StaticBlockFunctionCallNode(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_expr(expr) + { + ASSERT(divot.offset >= divotStart.offset); + } + inline FunctionCallResolveNode::FunctionCallResolveNode(const JSTokenLocation& location, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) : ExpressionNode(location) , ThrowableExpressionData(divot, divotStart, divotEnd) diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h index 253e6dade7f2b..ffdee03818a0e 100644 --- a/Source/JavaScriptCore/parser/Nodes.h +++ b/Source/JavaScriptCore/parser/Nodes.h @@ -736,8 +736,9 @@ namespace JSC { enum class ClassElementTag : uint8_t { No, Instance, Static, LastTag }; class PropertyNode final : public ParserArenaFreeable { public: - enum Type : uint16_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, PrivateField = 64, PrivateMethod = 128, PrivateSetter = 256, PrivateGetter = 512 }; + enum Type : uint16_t { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32, PrivateField = 64, PrivateMethod = 128, PrivateSetter = 256, PrivateGetter = 512, Block = 1024 }; + PropertyNode(const Identifier&, Type, SuperBinding, ClassElementTag); PropertyNode(const Identifier&, ExpressionNode*, Type, SuperBinding, ClassElementTag); PropertyNode(ExpressionNode*, Type, SuperBinding, ClassElementTag); PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, SuperBinding, ClassElementTag); @@ -754,6 +755,8 @@ namespace JSC { bool isClassField() const { return isClassProperty() && !needsSuperBinding(); } bool isInstanceClassField() const { return isInstanceClassProperty() && !needsSuperBinding(); } bool isStaticClassField() const { return isStaticClassProperty() && !needsSuperBinding(); } + bool isStaticClassBlock() const { return m_type & Block; } + bool isStaticClassElement() const { return isStaticClassBlock() || isStaticClassField(); } bool isOverriddenByDuplicate() const { return m_isOverriddenByDuplicate; } bool isPrivate() const { return m_type & (PrivateField | PrivateMethod | PrivateGetter | PrivateSetter); } bool hasComputedName() const { return m_expression; } @@ -772,10 +775,10 @@ namespace JSC { private: friend class PropertyListNode; - const Identifier* m_name; - ExpressionNode* m_expression; - ExpressionNode* m_assign; - unsigned m_type : 10; + const Identifier* m_name { nullptr }; + ExpressionNode* m_expression { nullptr }; + ExpressionNode* m_assign { nullptr }; + unsigned m_type : 11; unsigned m_needsSuperBinding : 1; static_assert(1 << 2 > static_cast(ClassElementTag::LastTag), "ClassElementTag shouldn't use more than two bits"); unsigned m_classElementTag : 2; @@ -803,6 +806,16 @@ namespace JSC { return m_node->isStaticClassField(); } + bool isStaticClassBlock() const + { + return m_node->isStaticClassBlock(); + } + + bool isStaticClassElement() const + { + return m_node->isStaticClassElement(); + } + void setHasPrivateAccessors(bool hasPrivateAccessors) { m_hasPrivateAccessors = hasPrivateAccessors; @@ -986,6 +999,18 @@ namespace JSC { ArgumentsNode* m_args; }; + class StaticBlockFunctionCallNode final : public ExpressionNode, public ThrowableExpressionData { + public: + StaticBlockFunctionCallNode(const JSTokenLocation&, ExpressionNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = nullptr) final; + + bool isFunctionCall() const final { return true; } + + ExpressionNode* m_expr { nullptr }; + }; + class FunctionCallResolveNode final : public ExpressionNode, public ThrowableExpressionData { public: FunctionCallResolveNode(const JSTokenLocation&, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp index 4f1250664a805..85676c8832c8d 100644 --- a/Source/JavaScriptCore/parser/Parser.cpp +++ b/Source/JavaScriptCore/parser/Parser.cpp @@ -384,7 +384,7 @@ template bool Parser::isArrowFunctionParameters(T } if (matchSpecIdentifier()) { - semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a parameter name in an async function"); + semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a parameter name ", disallowedIdentifierAwaitReason()); SavePoint saveArrowFunctionPoint = createSavePoint(context); next(); bool isArrowFunction = match(ARROWFUNCTION); @@ -776,6 +776,10 @@ template TreeStatement Parser::parseStatementList FALLTHROUGH; case AWAIT: case YIELD: { + if (UNLIKELY(currentScope()->isStaticBlock())) { + failIfTrue(match(YIELD), "Cannot use 'yield' within static block"); + failIfTrue(match(AWAIT), "Cannot use 'await' within static block"); + } // This is a convenient place to notice labeled statements // (even though we also parse them as normal statements) // because we allow the following type of code in sloppy mode: @@ -895,6 +899,7 @@ template TreeExpression Parser::parseVariableDecl failIfTrue(match(PRIVATENAME), "Cannot use a private name to declare a variable"); if (matchSpecIdentifier()) { + semanticFailIfTrue(currentScope()->isStaticBlock() && isArgumentsIdentifier(), "Cannot use 'arguments' as an identifier in static block"); failIfTrue(isPossiblyEscapedLet(m_token) && (declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::ConstDeclaration), "Cannot use 'let' as an identifier name for a LexicalDeclaration"); semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a ", declarationTypeToVariableKind(declarationType), " ", disallowedIdentifierAwaitReason()); @@ -1622,9 +1627,15 @@ template TreeStatement Parser::parseBreakStatemen JSTextPosition start = tokenStartPosition(); JSTextPosition end = tokenEndPosition(); next(); - + + std::optional isBreakValid; + if (UNLIKELY(currentScope()->isStaticBlock())) { + isBreakValid = breakIsValid(); + semanticFailIfTrue(!currentScope()->breakIsValid() && !isBreakValid.value(), "'break' cannot cross static block boundary"); + } + if (autoSemiColon()) { - semanticFailIfFalse(breakIsValid(), "'break' is only valid inside a switch or loop statement"); + semanticFailIfFalse(isBreakValid.value_or(breakIsValid()), "'break' is only valid inside a switch or loop statement"); return context.createBreakStatement(location, &m_vm.propertyNames->nullIdentifier, start, end); } failIfFalse(matchSpecIdentifier(), "Expected an identifier as the target for a break statement"); @@ -1644,9 +1655,15 @@ template TreeStatement Parser::parseContinueState JSTextPosition start = tokenStartPosition(); JSTextPosition end = tokenEndPosition(); next(); - + + std::optional isContinueValid; + if (UNLIKELY(currentScope()->isStaticBlock())) { + isContinueValid = continueIsValid(); + semanticFailIfTrue(!currentScope()->continueIsValid() && !isContinueValid.value(), "'continue' cannot cross static block boundary"); + } + if (autoSemiColon()) { - semanticFailIfFalse(continueIsValid(), "'continue' is only valid inside a loop statement"); + semanticFailIfFalse(isContinueValid.value_or(continueIsValid()), "'continue' is only valid inside a loop statement"); return context.createContinueStatement(location, &m_vm.propertyNames->nullIdentifier, start, end); } failIfFalse(matchSpecIdentifier(), "Expected an identifier as the target for a continue statement"); @@ -1665,7 +1682,7 @@ template TreeStatement Parser::parseReturnStateme { ASSERT(match(RETURN)); JSTokenLocation location(tokenLocation()); - semanticFailIfFalse(currentScope()->isFunction(), "Return statements are only valid inside functions"); + semanticFailIfFalse(currentScope()->isFunction() && !currentScope()->isStaticBlock(), "Return statements are only valid inside functions"); JSTextPosition start = tokenStartPosition(); JSTextPosition end = tokenEndPosition(); next(); @@ -1838,6 +1855,7 @@ template TreeStatement Parser::parseTryStatement( } else { handleProductionOrFail(OPENPAREN, "(", "start", "'catch' target"); DepthManager statementDepth(&m_statementDepth); + semanticFailIfTrue(currentScope()->isStaticBlock() && match(AWAIT), "Cannot use 'await' as identifier within static block"); m_statementDepth++; AutoPopScopeRef catchScope(this, pushScope()); catchScope->setIsLexicalScope(); @@ -1855,8 +1873,7 @@ template TreeStatement Parser::parseTryStatement( } handleProductionOrFail(CLOSEPAREN, ")", "end", "'catch' target"); matchOrFail(OPENBRACE, "Expected exception handler to be a block statement"); - constexpr bool isCatchBlock = true; - catchBlock = parseBlockStatement(context, isCatchBlock); + catchBlock = parseBlockStatement(context, BlockType::CatchBlock); failIfFalse(catchBlock, "Unable to parse 'catch' block"); std::tie(catchEnvironment, functionStack) = popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo); ASSERT(functionStack.isEmpty()); @@ -1889,7 +1906,7 @@ template TreeStatement Parser::parseDebuggerState } template -template TreeStatement Parser::parseBlockStatement(TreeBuilder& context, bool isCatchBlock) +template TreeStatement Parser::parseBlockStatement(TreeBuilder& context, BlockType type) { ASSERT(match(OPENBRACE)); @@ -1901,8 +1918,19 @@ template TreeStatement Parser::parseBlockStatemen ScopeRef newScope = pushScope(); newScope->setIsLexicalScope(); newScope->preventVarDeclarations(); - if (isCatchBlock) + switch (type) { + case BlockType::CatchBlock: newScope->setIsCatchBlockScope(); + break; + case BlockType::StaticBlock: + newScope->setIsStaticBlock(); + break; + case BlockType::Normal: + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } lexicalScope.setIsValid(newScope, this); } JSTokenLocation location(tokenLocation()); @@ -2222,6 +2250,7 @@ static const char* stringArticleForFunctionMode(SourceParseMode mode) case SourceParseMode::ModuleAnalyzeMode: case SourceParseMode::ModuleEvaluateMode: case SourceParseMode::ClassFieldInitializerMode: + case SourceParseMode::ClassStaticBlockMode: RELEASE_ASSERT_NOT_REACHED(); return ""; } @@ -2264,6 +2293,7 @@ static const char* stringForFunctionMode(SourceParseMode mode) case SourceParseMode::ModuleAnalyzeMode: case SourceParseMode::ModuleEvaluateMode: case SourceParseMode::ClassFieldInitializerMode: + case SourceParseMode::ClassStaticBlockMode: RELEASE_ASSERT_NOT_REACHED(); return ""; } @@ -2276,6 +2306,8 @@ template template bool Parser::parseFunctionInfo(TreeBuild context.setEndOffset(functionInfo.body, m_lexer->currentOffset()); if (functionScope->strictMode() && requirements != FunctionNameRequirements::Unnamed) { ASSERT(functionInfo.name); - RELEASE_ASSERT(SourceParseModeSet(SourceParseMode::NormalFunctionMode, SourceParseMode::MethodMode, SourceParseMode::ArrowFunctionMode, SourceParseMode::GeneratorBodyMode, SourceParseMode::GeneratorWrapperFunctionMode).contains(mode) || isAsyncFunctionOrAsyncGeneratorWrapperParseMode(mode)); + RELEASE_ASSERT(SourceParseModeSet(SourceParseMode::NormalFunctionMode, SourceParseMode::MethodMode, SourceParseMode::ArrowFunctionMode, SourceParseMode::GeneratorBodyMode, SourceParseMode::GeneratorWrapperFunctionMode, SourceParseMode::ClassStaticBlockMode).contains(mode) || isAsyncFunctionOrAsyncGeneratorWrapperParseMode(mode)); semanticFailIfTrue(m_vm.propertyNames->arguments == *functionInfo.name, "'", functionInfo.name->impl(), "' is not a valid function name in strict mode"); semanticFailIfTrue(m_vm.propertyNames->eval == *functionInfo.name, "'", functionInfo.name->impl(), "' is not a valid function name in strict mode"); } @@ -2928,6 +2960,7 @@ template TreeClassExpression Parser::parseClass(T classHeadScope->preventVarDeclarations(); classHeadScope->setStrictMode(); next(); + semanticFailIfTrue(currentScope()->isStaticBlock() && match(AWAIT), "Cannot use 'await' as a class name within static block"); ASSERT_WITH_MESSAGE(requirements != FunctionNameRequirements::Unnamed, "Currently, there is no caller that uses FunctionNameRequirements::Unnamed for class syntax."); ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !info.className), "When specifying FunctionNameRequirements::None, we need to initialize info.className with the default value in the caller side."); @@ -2982,6 +3015,7 @@ template TreeClassExpression Parser::parseClass(T // For backwards compatibility, "static" is a non-reserved keyword in non-strict mode. ClassElementTag tag = ClassElementTag::Instance; + SourceParseMode parseMode = SourceParseMode::MethodMode; auto type = PropertyNode::Constant; if (match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm.propertyNames->staticKeyword) { SavePoint savePoint = createSavePoint(context); @@ -2989,8 +3023,11 @@ template TreeClassExpression Parser::parseClass(T if (match(OPENPAREN) || match(SEMICOLON) || match(EQUAL)) { // Reparse "static()" as a method or "static" as a class field. restoreSavePoint(context, savePoint); - } else + } else { tag = ClassElementTag::Static; + if (match(OPENBRACE)) + parseMode = SourceParseMode::ClassStaticBlockMode; + } } // FIXME: Figure out a way to share more code with parseProperty. @@ -2999,7 +3036,6 @@ template TreeClassExpression Parser::parseClass(T TreeExpression computedPropertyName = 0; bool isGetter = false; bool isSetter = false; - SourceParseMode parseMode = SourceParseMode::MethodMode; if (consume(TIMES)) parseMode = SourceParseMode::GeneratorWrapperMethodMode; @@ -3052,6 +3088,7 @@ template TreeClassExpression Parser::parseClass(T break; case OPENBRACKET: next(); + semanticFailIfTrue(currentScope()->isStaticBlock() && match(IDENT) && isArgumentsIdentifier(), "Cannot use 'arguments' as an identifier in static block"); computedPropertyName = parseAssignmentExpression(context); type = static_cast(type | PropertyNode::Computed); failIfFalse(computedPropertyName, "Cannot parse computed property name"); @@ -3080,6 +3117,10 @@ template TreeClassExpression Parser::parseClass(T type = static_cast(type | PropertyNode::PrivateField); break; } + case OPENBRACE: + failIfFalse(parseMode == SourceParseMode::ClassStaticBlockMode, "Cannot parse static block without 'static'"); + type = static_cast(type | PropertyNode::Block); + break; default: if (m_token.m_type & KeywordTokenFlag) goto namedKeyword; @@ -3148,6 +3189,16 @@ template TreeClassExpression Parser::parseClass(T property = context.createProperty(ident, computedPropertyName, initializer, type, SuperBinding::NotNeeded, tag); else property = context.createProperty(ident, initializer, type, SuperBinding::NotNeeded, inferName, tag); + } else if (parseMode == SourceParseMode::ClassStaticBlockMode) { + matchOrFail(OPENBRACE, "Expected block statement for class static block"); + size_t usedVariablesSize = currentScope()->currentUsedVariablesSize(); + currentScope()->pushUsedVariableSet(); + SetForScope parsingWithClassStaticBlockMode(m_parseMode, parseMode); + DepthManager statementDepth(&m_statementDepth); + m_statementDepth = 1; + failIfFalse(parseBlockStatement(context, BlockType::StaticBlock), "Cannot parse class static block"); + property = context.createProperty(ident, type, SuperBinding::Needed, tag); + classScope->markLastUsedVariablesSetAsCaptured(usedVariablesSize); } else { ParserFunctionInfo methodInfo; bool isConstructor = tag == ClassElementTag::Instance && *ident == propertyNames.constructor; @@ -3280,6 +3331,9 @@ template TreeSourceElements Parser::parseClassFie type = DefineFieldNode::Type::ComputedName; break; } + case OPENBRACE: + RELEASE_ASSERT(isStaticField); + break; default: if (m_token.m_type & KeywordTokenFlag) goto namedKeyword; @@ -3288,17 +3342,35 @@ template TreeSourceElements Parser::parseClassFie } // Only valid class fields are handled in this function. - ASSERT(match(EQUAL) || match(SEMICOLON) || match(CLOSEBRACE) || m_lexer->hasLineTerminatorBeforeToken()); + ASSERT(match(EQUAL) || match(SEMICOLON) || match(CLOSEBRACE) || match(OPENBRACE) || m_lexer->hasLineTerminatorBeforeToken()); + + TreeStatement statement; + if (match(OPENBRACE) && isStaticField) { + JSTextPosition startPosition = tokenStartPosition(); + JSTokenLocation startLocation(tokenLocation()); + unsigned expressionStart = tokenStart(); - TreeExpression initializer = 0; - if (consume(EQUAL)) - initializer = parseAssignmentExpression(context); + ParserFunctionInfo functionInfo; + functionInfo.name = &m_vm.propertyNames->nullIdentifier; + SetForScope setInnerParseMode(m_parseMode, SourceParseMode::ClassStaticBlockMode); + failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::None, false, ConstructorKind::None, SuperBinding::Needed, expressionStart, functionInfo, FunctionDefinitionType::Expression)), "Cannot parse static block function"); + TreeExpression expression = context.createFunctionExpr(startLocation, functionInfo); - if (type == DefineFieldNode::Type::PrivateName) - currentScope()->useVariable(ident, false); + JSTextPosition endPosition = lastTokenEndPosition(); + expression = context.makeStaticBlockFunctionCallNode(startLocation, expression, endPosition, startPosition, lastTokenEndPosition()); - TreeStatement defineField = context.createDefineField(fieldLocation, ident, initializer, type); - context.appendStatement(sourceElements, defineField); + statement = context.createExprStatement(startLocation, expression, startPosition, m_lastTokenEndPosition.line); + } else { + TreeExpression initializer = 0; + if (consume(EQUAL)) + initializer = parseAssignmentExpression(context); + + if (type == DefineFieldNode::Type::PrivateName) + currentScope()->useVariable(ident, false); + + statement = context.createDefineField(fieldLocation, ident, initializer, type); + } + context.appendStatement(sourceElements, statement); } ASSERT(!hasError()); @@ -4112,6 +4184,22 @@ template TreeExpression Parser::parseAssignmen TreeExpression lhs = parseConditionalExpression(context); + // Current implementation of parseAssignmentExpression causes a weird parsing loop + // for this example: + // + // class C { + // static { + // ((x = await) => 0); + // } + // } + // + // which makes the 'await' error caught in parseConditionalExpression escaping from + // parseAssignmentExpression. Therefore, we need to capture the error directly after + // parseConditionalExpression. Besides, the usage of `await` is strictly limited in + // class static block. + if (UNLIKELY(!lhs && currentScope()->isStaticBlock() && match(AWAIT))) + propagateError(); + if (maybeValidArrowFunctionStart && !match(EOFTOK)) { bool isArrowFunctionToken = match(ARROWFUNCTION); if (!lhs || isArrowFunctionToken) { @@ -4939,6 +5027,7 @@ template TreeExpression Parser::parsePrimaryExpre return context.createThisExpr(location); } case AWAIT: + semanticFailIfTrue(currentScope()->isStaticBlock(), "The 'await' keyword is disallowed in the IdentifierReference position within static block"); if (m_parserState.functionParsePhase == FunctionParsePhase::Parameters) semanticFailIfFalse(m_parserState.allowAwait, "Cannot use 'await' within a parameter default expression"); else if (currentFunctionScope()->isAsyncFunctionBoundary() || isModuleParseMode(sourceParseMode())) @@ -4946,6 +5035,7 @@ template TreeExpression Parser::parsePrimaryExpre goto identifierExpression; case IDENT: { + semanticFailIfTrue(currentScope()->isStaticBlock() && isArgumentsIdentifier(), "Cannot use 'arguments' as an identifier in static block"); if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) { JSTextPosition start = tokenStartPosition(); const Identifier* ident = m_token.m_data.ident; @@ -4962,7 +5052,7 @@ template TreeExpression Parser::parsePrimaryExpre return createResolveAndUseVariable(context, ident, isEval, start, location); } if (UNLIKELY(m_parserState.isParsingClassFieldInitializer)) - failIfTrue(*m_token.m_data.ident == m_vm.propertyNames->arguments, "Cannot reference 'arguments' in class field initializer"); + failIfTrue(isArgumentsIdentifier(), "Cannot reference 'arguments' in class field initializer"); identifierExpression: JSTextPosition start = tokenStartPosition(); const Identifier* ident = m_token.m_data.ident; @@ -5168,7 +5258,7 @@ template TreeExpression Parser::parseMemberExpres ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); bool isClassFieldInitializer = m_parserState.isParsingClassFieldInitializer; bool isFunctionEvalContextType = m_isInsideOrdinaryFunction && (closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::InstanceFieldEvalContext); - semanticFailIfFalse(currentScope()->isFunction() || isFunctionEvalContextType || isClassFieldInitializer, "new.target is only valid inside functions"); + semanticFailIfFalse(currentScope()->isFunction() || currentScope()->isStaticBlock() || isFunctionEvalContextType || isClassFieldInitializer, "new.target is only valid inside functions or static blocks"); baseIsNewTarget = true; if (currentScope()->isArrowFunction()) { semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || isFunctionEvalContextType || isClassFieldInitializer, "new.target is not valid inside arrow functions in global code"); @@ -5455,8 +5545,10 @@ template TreeExpression Parser::parseUnaryExpress bool hasPrefixUpdateOp = false; unsigned lastOperator = 0; - if (UNLIKELY(match(AWAIT) && (currentFunctionScope()->isAsyncFunctionBoundary() || isModuleParseMode(sourceParseMode())))) + if (UNLIKELY(match(AWAIT) && (currentFunctionScope()->isAsyncFunctionBoundary() || isModuleParseMode(sourceParseMode())))) { + semanticFailIfTrue(currentScope()->isStaticBlock(), "Cannot use 'await' within static block"); return parseAwaitExpression(context); + } JSTokenLocation location(tokenLocation()); diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h index 0f73cbfe55bb1..678af7c95da7c 100644 --- a/Source/JavaScriptCore/parser/Parser.h +++ b/Source/JavaScriptCore/parser/Parser.h @@ -148,7 +148,7 @@ struct Scope { WTF_MAKE_NONCOPYABLE(Scope); public: - Scope(const VM& vm, ImplementationVisibility implementationVisibility, LexicalScopeFeatures lexicalScopeFeatures, bool isFunction, bool isGenerator, bool isArrowFunction, bool isAsyncFunction) + Scope(const VM& vm, ImplementationVisibility implementationVisibility, LexicalScopeFeatures lexicalScopeFeatures, bool isFunction, bool isGenerator, bool isArrowFunction, bool isAsyncFunction, bool isStaticBlock) : m_vm(vm) , m_implementationVisibility(implementationVisibility) , m_shadowsArguments(false) @@ -182,6 +182,7 @@ struct Scope { , m_loopDepth(0) , m_switchDepth(0) , m_innerArrowFunctionFeatures(0) + , m_isStaticBlock(isStaticBlock) { m_usedVariables.append(UniquedStringImplPtrSet()); } @@ -263,6 +264,11 @@ struct Scope { setIsFunction(); break; + case SourceParseMode::ClassStaticBlockMode: + setIsFunction(); + setIsStaticBlock(); + break; + case SourceParseMode::ArrowFunctionMode: setIsArrowFunction(); break; @@ -303,6 +309,14 @@ struct Scope { void setIsCatchBlockScope() { m_isCatchBlockScope = true; } bool isCatchBlockScope() { return m_isCatchBlockScope; } + void setIsStaticBlock() + { + m_isStaticBlock = true; + m_isStaticBlockBoundary = true; + } + bool isStaticBlock() { return m_isStaticBlock; } + bool isStaticBlockBoundary() { return m_isStaticBlockBoundary; } + void setIsLexicalScope() { m_isLexicalScope = true; @@ -824,6 +838,8 @@ struct Scope { m_isArrowFunction = false; m_isAsyncFunction = false; m_isAsyncFunctionBoundary = false; + m_isStaticBlock = false; + m_isStaticBlockBoundary = false; } void setIsGeneratorFunction() @@ -894,37 +910,39 @@ struct Scope { const VM& m_vm; ImplementationVisibility m_implementationVisibility; - bool m_shadowsArguments; - bool m_usesEval; - bool m_needsFullActivation; - bool m_hasDirectSuper; - bool m_needsSuperBinding; - bool m_allowsVarDeclarations; - bool m_allowsLexicalDeclarations; + bool m_shadowsArguments : 1 { false }; + bool m_usesEval : 1 { false }; + bool m_needsFullActivation : 1 { false }; + bool m_hasDirectSuper : 1 { false }; + bool m_needsSuperBinding : 1 { false }; + bool m_allowsVarDeclarations : 1 { true }; + bool m_allowsLexicalDeclarations : 1 { true }; LexicalScopeFeatures m_lexicalScopeFeatures; - bool m_isFunction; - bool m_isGenerator; - bool m_isGeneratorBoundary; - bool m_isArrowFunction; - bool m_isArrowFunctionBoundary; - bool m_isAsyncFunction; - bool m_isAsyncFunctionBoundary; - bool m_isLexicalScope; - bool m_isGlobalCodeScope; - bool m_isSimpleCatchParameterScope; - bool m_isCatchBlockScope; - bool m_isFunctionBoundary; - bool m_isValidStrictMode; - bool m_hasArguments; - bool m_isEvalContext; - bool m_hasNonSimpleParameterList; - bool m_isClassScope; - EvalContextType m_evalContextType; - unsigned m_constructorKind; - unsigned m_expectedSuperBinding; - int m_loopDepth; - int m_switchDepth; - InnerArrowFunctionCodeFeatures m_innerArrowFunctionFeatures; + bool m_isFunction : 1; + bool m_isGenerator : 1; + bool m_isGeneratorBoundary : 1 { false }; + bool m_isArrowFunction : 1; + bool m_isArrowFunctionBoundary : 1 { false }; + bool m_isAsyncFunction : 1; + bool m_isAsyncFunctionBoundary : 1 { false }; + bool m_isLexicalScope : 1 { false }; + bool m_isGlobalCodeScope : 1 { false }; + bool m_isSimpleCatchParameterScope : 1 { false }; + bool m_isCatchBlockScope : 1 { false }; + bool m_isStaticBlock : 1 { false }; + bool m_isStaticBlockBoundary : 1 { false }; + bool m_isFunctionBoundary : 1 { false }; + bool m_isValidStrictMode : 1 { true }; + bool m_hasArguments : 1 { false }; + bool m_isEvalContext : 1 { false }; + bool m_hasNonSimpleParameterList : 1 { false }; + bool m_isClassScope : 1 { false }; + EvalContextType m_evalContextType { EvalContextType::None }; + unsigned m_constructorKind { static_cast(ConstructorKind::None) }; + unsigned m_expectedSuperBinding { static_cast(SuperBinding::NotNeeded) }; + int m_loopDepth { 0 }; + int m_switchDepth { 0 }; + InnerArrowFunctionCodeFeatures m_innerArrowFunctionFeatures { 0 }; typedef Vector LabelStack; std::unique_ptr m_labels; @@ -1303,6 +1321,7 @@ class Parser { bool isGenerator = false; bool isArrowFunction = false; bool isAsyncFunction = false; + bool isStaticBlock = false; if (!m_scopeStack.isEmpty()) { implementationVisibility = m_scopeStack.last().implementationVisibility(); lexicalScopeFeatures = m_scopeStack.last().lexicalScopeFeatures(); @@ -1310,8 +1329,9 @@ class Parser { isGenerator = m_scopeStack.last().isGenerator(); isArrowFunction = m_scopeStack.last().isArrowFunction(); isAsyncFunction = m_scopeStack.last().isAsyncFunction(); + isStaticBlock = m_scopeStack.last().isStaticBlock(); } - m_scopeStack.constructAndAppend(m_vm, implementationVisibility, lexicalScopeFeatures, isFunction, isGenerator, isArrowFunction, isAsyncFunction); + m_scopeStack.constructAndAppend(m_vm, implementationVisibility, lexicalScopeFeatures, isFunction, isGenerator, isArrowFunction, isAsyncFunction, isStaticBlock); return currentScope(); } @@ -1696,7 +1716,7 @@ class Parser { { ScopeRef current = currentScope(); while (!current->breakIsValid()) { - if (!current.hasContainingScope()) + if (!current.hasContainingScope() || current->isStaticBlockBoundary()) return false; current = current.containingScope(); } @@ -1706,7 +1726,7 @@ class Parser { { ScopeRef current = currentScope(); while (!current->continueIsValid()) { - if (!current.hasContainingScope()) + if (!current.hasContainingScope() || current->isStaticBlockBoundary()) return false; current = current.containingScope(); } @@ -1773,7 +1793,8 @@ class Parser { template TreeStatement parseExpressionStatement(TreeBuilder&); template TreeStatement parseExpressionOrLabelStatement(TreeBuilder&, bool allowFunctionDeclarationAsStatement); template TreeStatement parseIfStatement(TreeBuilder&); - template TreeStatement parseBlockStatement(TreeBuilder&, bool isCatchBlock = false); + enum class BlockType : uint8_t { Normal, CatchBlock, StaticBlock }; + template TreeStatement parseBlockStatement(TreeBuilder&, BlockType = BlockType::Normal); enum class IsOnlyChildOfStatement { Yes, No }; template TreeExpression parseExpression(TreeBuilder&, IsOnlyChildOfStatement = IsOnlyChildOfStatement::No); @@ -1896,7 +1917,7 @@ class Parser { ALWAYS_INLINE bool canUseIdentifierAwait() { - return m_parserState.allowAwait && !currentScope()->isAsyncFunction() && m_scriptMode != JSParserScriptMode::Module; + return m_parserState.allowAwait && !currentScope()->isAsyncFunction() && !currentScope()->isStaticBlock() && m_scriptMode != JSParserScriptMode::Module; } bool isDisallowedIdentifierYield(const JSToken& token) @@ -1954,6 +1975,8 @@ class Parser { { if (!m_parserState.allowAwait || currentScope()->isAsyncFunction()) return "in an async function"; + if (currentScope()->isStaticBlock()) + return "in a static block"; if (m_scriptMode == JSParserScriptMode::Module) return "in a module"; RELEASE_ASSERT_NOT_REACHED(); @@ -1970,6 +1993,11 @@ class Parser { return nullptr; } + ALWAYS_INLINE bool isArgumentsIdentifier() + { + return *m_token.m_data.ident == m_vm.propertyNames->arguments; + } + enum class FunctionParsePhase { Parameters, Body }; struct ParserState { int assignmentCount { 0 }; diff --git a/Source/JavaScriptCore/parser/ParserModes.h b/Source/JavaScriptCore/parser/ParserModes.h index 1ae56ec439a43..6443ce636290e 100644 --- a/Source/JavaScriptCore/parser/ParserModes.h +++ b/Source/JavaScriptCore/parser/ParserModes.h @@ -69,6 +69,7 @@ enum class SourceParseMode : uint8_t { AsyncGeneratorWrapperMethodMode = 17, GeneratorWrapperMethodMode = 18, ClassFieldInitializerMode = 19, + ClassStaticBlockMode = 20, }; class SourceParseModeSet { @@ -118,7 +119,8 @@ ALWAYS_INLINE bool isFunctionParseMode(SourceParseMode parseMode) SourceParseMode::AsyncGeneratorBodyMode, SourceParseMode::AsyncGeneratorWrapperFunctionMode, SourceParseMode::AsyncGeneratorWrapperMethodMode, - SourceParseMode::ClassFieldInitializerMode).contains(parseMode); + SourceParseMode::ClassFieldInitializerMode, + SourceParseMode::ClassStaticBlockMode).contains(parseMode); } ALWAYS_INLINE bool isAsyncFunctionParseMode(SourceParseMode parseMode) @@ -206,7 +208,8 @@ ALWAYS_INLINE bool isMethodParseMode(SourceParseMode parseMode) SourceParseMode::SetterMode, SourceParseMode::MethodMode, SourceParseMode::AsyncMethodMode, - SourceParseMode::AsyncGeneratorWrapperMethodMode).contains(parseMode); + SourceParseMode::AsyncGeneratorWrapperMethodMode, + SourceParseMode::ClassStaticBlockMode).contains(parseMode); } ALWAYS_INLINE bool isGeneratorOrAsyncFunctionBodyParseMode(SourceParseMode parseMode) diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h index ed0da5fe56666..ae931fbf576fc 100644 --- a/Source/JavaScriptCore/parser/SyntaxChecker.h +++ b/Source/JavaScriptCore/parser/SyntaxChecker.h @@ -148,6 +148,7 @@ class SyntaxChecker { static constexpr OptionSet DontBuildStrings = LexerFlags::DontBuildStrings; int createSourceElements() { return SourceElementsResult; } + ExpressionType makeStaticBlockFunctionCallNode(const JSTokenLocation&, ExpressionType, int, int, int) { return CallExpr; } ExpressionType makeFunctionCallNode(const JSTokenLocation&, ExpressionType, bool, int, int, int, int, size_t, bool) { return CallExpr; } ExpressionType createCommaExpr(const JSTokenLocation&, ExpressionType) { return CommaExpr; } ExpressionType appendToCommaExpr(const JSTokenLocation&, ExpressionType, ExpressionType, ExpressionType) { return CommaExpr; } @@ -239,6 +240,10 @@ class SyntaxChecker { { return Property(type); } + Property createProperty(const Identifier*, PropertyNode::Type type, SuperBinding, ClassElementTag) + { + return Property(type); + } int createPropertyList(const JSTokenLocation&, Property) { return PropertyListResult; } int createPropertyList(const JSTokenLocation&, Property, int) { return PropertyListResult; } int createElementList(int, int) { return ElementsListResult; } diff --git a/Source/JavaScriptCore/runtime/FunctionExecutable.cpp b/Source/JavaScriptCore/runtime/FunctionExecutable.cpp index 3764e8993b104..8f79e466a12b2 100644 --- a/Source/JavaScriptCore/runtime/FunctionExecutable.cpp +++ b/Source/JavaScriptCore/runtime/FunctionExecutable.cpp @@ -206,6 +206,7 @@ JSString* FunctionExecutable::toStringSlow(JSGlobalObject* globalObject) case SourceParseMode::ArrowFunctionMode: case SourceParseMode::ClassFieldInitializerMode: + case SourceParseMode::ClassStaticBlockMode: break; case SourceParseMode::AsyncFunctionMode: