Skip to content

Commit

Permalink
Forbid await/yield/arguments in class field inits
Browse files Browse the repository at this point in the history
Summary:
Manual port of D66266912 to SemanticResolver.

Certain language features aren't allowed in class field initializers.
'arguments' is forbidden by the spec.

Yield/await should be forbidden:
tc39/ecma262#3333

Reviewed By: fbmal7

Differential Revision: D66711282

fbshipit-source-id: d0458700d42103917fcc5625884dd4eeb921e804
  • Loading branch information
avp authored and facebook-github-bot committed Dec 3, 2024
1 parent 2dd2314 commit 9b403d0
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 4 deletions.
6 changes: 6 additions & 0 deletions lib/Parser/JSParserImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4991,6 +4991,12 @@ Optional<ESTree::Node *> JSParserImpl::parseClassElement(
if (checkAndEat(TokenKind::equal)) {
// ClassElementName Initializer[opt]
// ^
// NOTE: This is technically non-compliant, but having yield/await in the
// field initializer doesn't make sense.
// See https://github.com/tc39/ecma262/issues/3333
// Do [~Yield, +Await, ~Return] as suggested and error in resolution.
llvh::SaveAndRestore<bool> saveParamYield{paramYield_, false};
llvh::SaveAndRestore<bool> saveParamAwait{paramAwait_, true};
auto optValue = parseAssignmentExpression();
if (!optValue)
return None;
Expand Down
24 changes: 20 additions & 4 deletions lib/Sema/SemanticResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,11 @@ void SemanticResolver::visit(ESTree::ClassPropertyNode *node) {
// method that performs the initializations.
// Field initializers can always reference super.
llvh::SaveAndRestore<bool> oldCanRefSuper{canReferenceSuper_, true};
llvh::SaveAndRestore<bool> oldForbidAwait{forbidAwaitExpression_, true};
// ES14.0 15.7.1
// It is a Syntax Error if Initializer is present and ContainsArguments of
// Initializer is true.
llvh::SaveAndRestore<bool> oldForbidArguments{forbidArguments_, true};
FunctionContext funcCtx(
*this, curClassContext_->getOrCreateFieldInitFunctionInfo());
visitESTreeNode(*this, node->_value, node);
Expand Down Expand Up @@ -1018,10 +1023,8 @@ void SemanticResolver::visit(ESTree::YieldExpressionNode *node) {
}

void SemanticResolver::visit(ESTree::AwaitExpressionNode *awaitExpr) {
if (functionContext()->isGlobalScope() ||
(functionContext()->node && !ESTree::isAsync(functionContext()->node))) {
if (forbidAwaitExpression_)
sm_.error(awaitExpr->getSourceRange(), "'await' not in an async function");
}

if (functionContext()->isFormalParams) {
// ES14.0 15.8.1
Expand Down Expand Up @@ -1331,6 +1334,16 @@ void SemanticResolver::visitFunctionLikeInFunctionContext(
// in an incorrect scope!
// visitESTreeNode(*this, getIdentifier(node), node);

// 'await' forbidden outside async functions.
llvh::SaveAndRestore<bool> oldForbidAwait{
forbidAwaitExpression_, !ESTree::isAsync(node)};
// Forbidden-ness of 'arguments' passes through arrow functions because they
// use the same 'arguments'.
llvh::SaveAndRestore<bool> oldForbidArguments{
forbidArguments_,
llvh::isa<ESTree::ArrowFunctionExpressionNode>(node) ? forbidArguments_
: false};

// Visit the parameters before we have hoisted the body declarations.
// If there's a parameter named arguments, then the parameter init expressions
// would refer to that declaration.
Expand Down Expand Up @@ -1461,8 +1474,11 @@ Decl *SemanticResolver::resolveIdentifier(
Decl *decl = checkIdentifierResolved(identifier);

// Is this the "arguments" object?
if (decl && decl->special == Decl::Special::Arguments)
if (decl && decl->special == Decl::Special::Arguments) {
if (forbidArguments_)
sm_.error(identifier->getSourceRange(), "invalid use of 'arguments'");
curFunctionInfo()->usesArguments = true;
}

if (LLVM_UNLIKELY(identifier->_name == kw_.identAwait) &&
forbidAwaitAsIdentifier_) {
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/SemanticResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ class SemanticResolver
/// SemanticResolver.
bool forbidAwaitAsIdentifier_ = false;

/// True if we are forbidding await expressions.
bool forbidAwaitExpression_{false};

/// True if we are forbidding 'arguments' identifier.
bool forbidArguments_{false};

public:
/// This constant enables the more expensive path in RecursiveVisitorDispatch,
/// enabling us to mutate NodeList.
Expand Down
20 changes: 20 additions & 0 deletions test/Parser/arguments-field-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: (! %hermesc -dump-transformed-ast %s 2>&1 ) | %FileCheckOrRegen --match-full-lines %s

function* foo() {
class C {
x = () => arguments;
}
}

// Auto-generated content below. Please do not modify manually.

// CHECK:{{.*}}arguments-field-error.js:12:15: error: invalid use of 'arguments'
// CHECK-NEXT: x = () => arguments;
// CHECK-NEXT: ^~~~~~~~~
26 changes: 26 additions & 0 deletions test/Parser/await-field-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: (! %hermesc -dump-transformed-ast %s 2>&1 ) | %FileCheck --match-full-lines %s

async function foo() {
class C {
x = await 1;
}

class D {
x = class { [await 1] = 1; };
}
}


// CHECK:{{.*}}await-field-error.js:12:9: error: 'await' not in an async function
// CHECK-NEXT: x = await 1;
// CHECK-NEXT: ^~~~~~~
// CHECK-NEXT:{{.*}}await-field-error.js:16:18: error: 'await' not in an async function
// CHECK-NEXT: x = class { [await 1] = 1; };
// CHECK-NEXT: ^~~~~~~
93 changes: 93 additions & 0 deletions test/Parser/class-props-yield-await.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: %hermesc -dump-ast -pretty-json %s | %FileCheck %s --match-full-lines

// CHECK-LABEL: {
// CHECK-NEXT: "type": "Program",
// CHECK-NEXT: "body": [
// CHECK-NEXT: {

async function* foo() {
// CHECK-NEXT: "type": "FunctionDeclaration",
// CHECK-NEXT: "id": {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "foo"
// CHECK-NEXT: },
// CHECK-NEXT: "params": [],
// CHECK-NEXT: "body": {
// CHECK-NEXT: "type": "BlockStatement",
// CHECK-NEXT: "body": [

class C {
// CHECK-NEXT: {
// CHECK-NEXT: "type": "ClassDeclaration",
// CHECK-NEXT: "id": {
// CHECK-NEXT: "type": "Identifier",
// CHECK-NEXT: "name": "C"
// CHECK-NEXT: },
// CHECK-NEXT: "superClass": null,
// CHECK-NEXT: "body": {
// CHECK-NEXT: "type": "ClassBody",
// CHECK-NEXT: "body": [

[yield 1] = 1;
// CHECK-NEXT: {
// CHECK-NEXT: "type": "ClassProperty",
// CHECK-NEXT: "key": {
// CHECK-NEXT: "type": "YieldExpression",
// CHECK-NEXT: "argument": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 1,
// CHECK-NEXT: "raw": "1"
// CHECK-NEXT: },
// CHECK-NEXT: "delegate": false
// CHECK-NEXT: },
// CHECK-NEXT: "value": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 1,
// CHECK-NEXT: "raw": "1"
// CHECK-NEXT: },
// CHECK-NEXT: "computed": true,
// CHECK-NEXT: "static": false,
// CHECK-NEXT: "declare": false
// CHECK-NEXT: },

[await 1] = 1;
// CHECK-NEXT: {
// CHECK-NEXT: "type": "ClassProperty",
// CHECK-NEXT: "key": {
// CHECK-NEXT: "type": "AwaitExpression",
// CHECK-NEXT: "argument": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 1,
// CHECK-NEXT: "raw": "1"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "value": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 1,
// CHECK-NEXT: "raw": "1"
// CHECK-NEXT: },
// CHECK-NEXT: "computed": true,
// CHECK-NEXT: "static": false,
// CHECK-NEXT: "declare": false
// CHECK-NEXT: }

}
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: ]

}
// CHECK-NEXT: },
// CHECK-NEXT: "generator": true,
// CHECK-NEXT: "async": true
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
20 changes: 20 additions & 0 deletions test/Parser/yield-field-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: (! %hermesc -dump-ast %s 2>&1 ) | %FileCheckOrRegen --match-full-lines %s

function* foo() {
class C {
x = yield;
}
}

// Auto-generated content below. Please do not modify manually.

// CHECK:{{.*}}yield-field-error.js:12:9: error: invalid expression
// CHECK-NEXT: x = yield;
// CHECK-NEXT: ^

0 comments on commit 9b403d0

Please sign in to comment.