diff --git a/lib/Parser/JSParserImpl.cpp b/lib/Parser/JSParserImpl.cpp index bd15e2b3a4e..eb58862cb41 100644 --- a/lib/Parser/JSParserImpl.cpp +++ b/lib/Parser/JSParserImpl.cpp @@ -1909,7 +1909,11 @@ Optional JSParserImpl::parseForStatement(Param param) { if (!optIdent) return None; ESTree::NodeList declList; - declList.push_back(**optIdent); + auto *declarator = setLocation( + *optIdent, + *optIdent, + new (context_) ESTree::VariableDeclaratorNode(nullptr, *optIdent)); + declList.push_back(*declarator); decl = setLocation( varStartLoc, @@ -1929,7 +1933,11 @@ Optional JSParserImpl::parseForStatement(Param param) { if (!optIdent) return None; ESTree::NodeList declList; - declList.push_back(**optIdent); + auto *declarator = setLocation( + *optIdent, + *optIdent, + new (context_) ESTree::VariableDeclaratorNode(nullptr, *optIdent)); + declList.push_back(*declarator); decl = setLocation( varStartLoc, diff --git a/test/Parser/for-using-declarator.js b/test/Parser/for-using-declarator.js new file mode 100644 index 00000000000..b33fa585c6b --- /dev/null +++ b/test/Parser/for-using-declarator.js @@ -0,0 +1,45 @@ +/** + * 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 + +// Regression test: in `for (using x of y)` and `for [await] (await using x of +// y)`, the binding must be wrapped in a `VariableDeclarator` (with `init: +// null`) inside `VariableDeclaration._declarations`, matching what +// `for (var x of y)` produces. Pushing a bare Identifier corrupts later +// stages that `cast` over the list +// (https://github.com/facebook/hermes/issues/1981). + +for (using x of y); +// CHECK: "type": "VariableDeclaration", +// CHECK-NEXT: "kind": "using", +// CHECK-NEXT: "declarations": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "VariableDeclarator", +// CHECK-NEXT: "init": null, +// CHECK-NEXT: "id": { +// CHECK-NEXT: "type": "Identifier", +// CHECK-NEXT: "name": "x" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] + +async function f() { + for await (await using x of y); +} +// CHECK: "type": "VariableDeclaration", +// CHECK-NEXT: "kind": "await using", +// CHECK-NEXT: "declarations": [ +// CHECK-NEXT: { +// CHECK-NEXT: "type": "VariableDeclarator", +// CHECK-NEXT: "init": null, +// CHECK-NEXT: "id": { +// CHECK-NEXT: "type": "Identifier", +// CHECK-NEXT: "name": "x" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] diff --git a/test/Parser/for-using.js b/test/Parser/for-using.js index 517c249c619..107dcf5f0e6 100644 --- a/test/Parser/for-using.js +++ b/test/Parser/for-using.js @@ -19,8 +19,12 @@ for (using x of y); // CHECK-NEXT: "kind": "using", // CHECK-NEXT: "declarations": [ // CHECK-NEXT: { -// CHECK-NEXT: "type": "Identifier", -// CHECK-NEXT: "name": "x" +// CHECK-NEXT: "type": "VariableDeclarator", +// CHECK-NEXT: "init": null, +// CHECK-NEXT: "id": { +// CHECK-NEXT: "type": "Identifier", +// CHECK-NEXT: "name": "x" +// CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK-NEXT: }, @@ -42,8 +46,12 @@ for (using x in y); // CHECK-NEXT: "kind": "using", // CHECK-NEXT: "declarations": [ // CHECK-NEXT: { -// CHECK-NEXT: "type": "Identifier", -// CHECK-NEXT: "name": "x" +// CHECK-NEXT: "type": "VariableDeclarator", +// CHECK-NEXT: "init": null, +// CHECK-NEXT: "id": { +// CHECK-NEXT: "type": "Identifier", +// CHECK-NEXT: "name": "x" +// CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK-NEXT: }, @@ -93,8 +101,12 @@ async function f() { // CHECK-NEXT: "kind": "await using", // CHECK-NEXT: "declarations": [ // CHECK-NEXT: { -// CHECK-NEXT: "type": "Identifier", -// CHECK-NEXT: "name": "x" +// CHECK-NEXT: "type": "VariableDeclarator", +// CHECK-NEXT: "init": null, +// CHECK-NEXT: "id": { +// CHECK-NEXT: "type": "Identifier", +// CHECK-NEXT: "name": "x" +// CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK-NEXT: }, diff --git a/test/Sema/for-using-not-supported.js b/test/Sema/for-using-not-supported.js new file mode 100644 index 00000000000..68b4473fa21 --- /dev/null +++ b/test/Sema/for-using-not-supported.js @@ -0,0 +1,35 @@ +/** + * 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 -emit-binary -out /dev/null -O0 %s 2>&1) | %FileCheck --match-full-lines %s +// RUN: (! %hermesc -emit-binary -out /dev/null -O %s 2>&1) | %FileCheck --match-full-lines %s + +// Regression test: parsing `for (using x of y)` and `for [await] (await using +// x of y)` used to push a bare IdentifierNode into VariableDeclaration's +// `_declarations`. Sema helpers (`ScopedFunctionPromoter::extractDeclaredIdents`, +// `SemanticResolver::extractIdentsFromDecl`) then aborted via `Casting.h` +// `cast` before reaching the explicit `using` +// rejection, masking the user-facing error and corrupting memory in release +// WASM (https://github.com/facebook/hermes/issues/1981). +// +// With the parser fix, the cast succeeds and Sema reaches the explicit +// rejection cleanly. + +function for_of_using(arr) { + for (using x of arr) {} +} +// CHECK: {{.*}}for-using-not-supported.js:[[@LINE-2]]:8: error: using declarations are not yet supported + +function for_in_using(obj) { + for (using x in obj) {} +} +// CHECK: {{.*}}for-using-not-supported.js:[[@LINE-2]]:8: error: using declarations are not yet supported + +async function for_await_of_await_using(arr) { + for await (await using x of arr) {} +} +// CHECK: {{.*}}for-using-not-supported.js:[[@LINE-2]]:14: error: using declarations are not yet supported