Skip to content

Commit 1b21908

Browse files
trossimel-scfacebook-github-bot
authored andcommitted
Add async generators support via AST transformation (#1657)
Summary: This PR introduces support for async generators through AST transformations. In detail: - Added AsyncGenerator.cpp to transform asynchronous generators into standard generators using AST transformations. This implementation mirrors the logic found in [Babel ](https://babeljs.io/docs/babel-plugin-transform-async-generator-functions). - Introduced an EnableAsyncGenerators flag to manage this functionality. Pull Request resolved: #1657 Test Plan: - Added unit tests Reviewed By: tmikov Differential Revision: D78378721 Pulled By: avp fbshipit-source-id: cb67810823e2a1478b562ad3f12c5a5dfbf3f754
1 parent 0a6126f commit 1b21908

File tree

25 files changed

+770
-156
lines changed

25 files changed

+770
-156
lines changed

API/hermes/hermes.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ class HermesRuntimeImpl final : public HermesRuntime,
241241

242242
compileFlags_.enableGenerator = runtimeConfig.getEnableGenerator();
243243
compileFlags_.enableES6BlockScoping = runtimeConfig.getES6BlockScoping();
244+
compileFlags_.enableAsyncGenerators =
245+
runtimeConfig.getEnableAsyncGenerators();
244246
compileFlags_.emitAsyncBreakCheck =
245247
runtimeConfig.getAsyncBreakCheckInEval();
246248
runtime_.addCustomRootsFunction(
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#ifndef HERMES_AST_ASYNCGENERATORS_H
9+
#define HERMES_AST_ASYNCGENERATORS_H
10+
11+
#include "hermes/AST/ESTree.h"
12+
13+
namespace hermes {
14+
15+
/// Recursively transforms the ESTree Node tree such that async generators
16+
/// are converted into generators
17+
ESTree::Node *transformAsyncGenerators(Context &context, ESTree::Node *node);
18+
19+
} // namespace hermes
20+
21+
#endif

include/hermes/AST/Context.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ class Context {
242242
/// Whether to parse TypeScript syntax.
243243
bool parseTS_{false};
244244

245+
/// Whether to enable support for async generators
246+
bool enableAsyncGenerators_{false};
247+
245248
/// Whether to enable support for ES6 block scoping.
246249
/// TODO: This is intended to provide a temporary way to configure block
247250
/// scoping until we have debugger support for it.
@@ -433,6 +436,14 @@ class Context {
433436
return parseTS_;
434437
}
435438

439+
void setEnableAsyncGenerators(bool enableAsyncGenerators) {
440+
enableAsyncGenerators_ = enableAsyncGenerators;
441+
}
442+
443+
bool getEnableAsyncGenerators() const {
444+
return enableAsyncGenerators_;
445+
}
446+
436447
void setEnableES6BlockScoping(bool enableES6BlockScoping) {
437448
enableES6BlockScoping_ = enableES6BlockScoping;
438449
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include "hermes/AST/RecursiveVisitor.h"
11+
#include "hermes/Parser/JSLexer.h"
12+
#include "llvh/ADT/StringRef.h"
13+
14+
namespace {
15+
using namespace hermes;
16+
17+
/// Mutable vector that helps dealing with arrays of nodes safely.
18+
/// Once done with the vector, it can create an ESTree::NodeList
19+
/// representation which is used by the ESTree API in several places.
20+
class NodeVector {
21+
public:
22+
using Storage = llvh::SmallVector<ESTree::Node *, 8>;
23+
24+
NodeVector() = default;
25+
NodeVector(std::initializer_list<ESTree::Node *> nodes) {
26+
for (auto &node : nodes) {
27+
_storage.push_back(node);
28+
}
29+
}
30+
31+
NodeVector(ESTree::NodeList &list) {
32+
for (auto &node : list) {
33+
_storage.push_back(&node);
34+
}
35+
}
36+
37+
~NodeVector() = default;
38+
39+
size_t size() const {
40+
return _storage.size();
41+
}
42+
43+
Storage::const_iterator begin() const {
44+
return _storage.begin();
45+
}
46+
47+
Storage::const_iterator end() const {
48+
return _storage.end();
49+
}
50+
51+
void append(ESTree::Node *node) {
52+
_storage.emplace_back(node);
53+
}
54+
55+
void prepend(ESTree::Node *node) {
56+
_storage.insert(_storage.begin(), node);
57+
}
58+
59+
ESTree::NodeList toNodeList() const {
60+
ESTree::NodeList nodeList;
61+
for (auto &node : _storage) {
62+
nodeList.push_back(*node);
63+
}
64+
return nodeList;
65+
}
66+
67+
private:
68+
Storage _storage;
69+
};
70+
71+
class TransformationsBase
72+
: public ESTree::RecursionDepthTracker<TransformationsBase> {
73+
public:
74+
static constexpr bool kEnableNodeListMutation = true;
75+
76+
TransformationsBase(Context &context)
77+
: context_(context),
78+
identVar_(context.getIdentifier("var").getUnderlyingPointer()) {}
79+
80+
void recursionDepthExceeded(ESTree::Node *n) {
81+
context_.getSourceErrorManager().error(
82+
n->getEndLoc(), "Too many nested expressions/statements/declarations");
83+
}
84+
85+
protected:
86+
Context &context_;
87+
UniqueString *const identVar_;
88+
89+
void doCopyLocation(ESTree::Node *src, ESTree::Node *dest) {
90+
if (src != nullptr) {
91+
dest->setStartLoc(src->getStartLoc());
92+
dest->setEndLoc(src->getEndLoc());
93+
dest->setDebugLoc(src->getDebugLoc());
94+
}
95+
}
96+
97+
template <typename T>
98+
T *copyLocation(ESTree::Node *src, T *dest) {
99+
doCopyLocation(src, dest);
100+
return dest;
101+
}
102+
103+
template <typename T, typename... Args>
104+
T *createTransformedNode(ESTree::Node *src, Args &&...args) {
105+
auto *node = new (context_) T(std::forward<Args>(args)...);
106+
return copyLocation(src, node);
107+
}
108+
109+
ESTree::IdentifierNode *makeIdentifierNode(
110+
ESTree::Node *srcNode,
111+
UniqueString *name) {
112+
return createTransformedNode<ESTree::IdentifierNode>(
113+
srcNode, name, nullptr, false);
114+
}
115+
116+
ESTree::IdentifierNode *makeIdentifierNode(
117+
ESTree::Node *srcNode,
118+
llvh::StringRef name) {
119+
return makeIdentifierNode(
120+
srcNode, context_.getIdentifier(name).getUnderlyingPointer());
121+
}
122+
123+
ESTree::Node *makeSingleVarDecl(
124+
ESTree::Node *srcNode,
125+
ESTree::Node *identifier,
126+
ESTree::Node *value) {
127+
auto *variableDeclarator =
128+
createTransformedNode<ESTree::VariableDeclaratorNode>(
129+
srcNode, value, identifier);
130+
ESTree::NodeList variableList;
131+
variableList.push_back(*variableDeclarator);
132+
return createTransformedNode<ESTree::VariableDeclarationNode>(
133+
srcNode, identVar_, std::move(variableList));
134+
}
135+
136+
ESTree::Node *makeHermesInternalCall(
137+
ESTree::Node *srcNode,
138+
llvh::StringRef methodName,
139+
const NodeVector &parameters) {
140+
auto hermesInternalIdentifier = getHermesInternalIdentifier(srcNode);
141+
auto methodIdentifier = makeIdentifierNode(srcNode, methodName);
142+
143+
auto *getPropertyNode = createTransformedNode<ESTree::MemberExpressionNode>(
144+
srcNode, hermesInternalIdentifier, methodIdentifier, false);
145+
return createTransformedNode<ESTree::CallExpressionNode>(
146+
srcNode, getPropertyNode, nullptr, parameters.toNodeList());
147+
}
148+
149+
virtual ESTree::Node *getHermesInternalIdentifier(ESTree::Node *srcNode) {
150+
return nullptr;
151+
};
152+
153+
virtual ~TransformationsBase() = default;
154+
};
155+
} // namespace

include/hermes/BCGen/HBC/HBC.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ struct CompileFlags {
6868
bool enableGenerator{true};
6969
/// Enable ES6 block scoping support
7070
bool enableES6BlockScoping{false};
71+
/// Enable async generators support
72+
bool enableAsyncGenerators{false};
7173
/// Define the output format of the generated bytecode. For instance, whether
7274
/// the bytecode is intended for execution or serialisation.
7375
OutputFormatKind format{Execute};

include/hermes/Runtime/Libhermes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const char libhermes[] =
1818
"var Error;"
1919
"var Function;"
2020
"var HermesInternal;"
21+
"var HermesAsyncIteratorsInternal;"
2122
"var JSON;"
2223
"var Map;"
2324
"var Math;"

include/hermes/Utils/CompilerRuntimeFlags.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ struct CompilerRuntimeFlags {
4848
llvh::cl::init(false),
4949
llvh::cl::desc("Enable support for ES6 block scoping"),
5050
llvh::cl::Hidden};
51+
52+
llvh::cl::opt<bool> EnableAsyncGenerators{
53+
"Xasync-generators",
54+
llvh::cl::init(false),
55+
llvh::cl::desc("Enable support for async generators"),
56+
llvh::cl::Hidden};
5157
};
5258

5359
} // namespace hermes

include/hermes/VM/Runtime.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,10 @@ class Runtime : public RuntimeBase, public HandleRootOwner {
964964
return hasES6Proxy_;
965965
}
966966

967+
bool hasAsyncGenerators() const {
968+
return hasAsyncGenerators_;
969+
}
970+
967971
bool hasES6BlockScoping() const {
968972
return hasES6BlockScoping_;
969973
}
@@ -1206,6 +1210,9 @@ class Runtime : public RuntimeBase, public HandleRootOwner {
12061210
/// Set to true if we should enable ES6 Proxy.
12071211
const bool hasES6Proxy_;
12081212

1213+
/// Set to true if we should enable async generators.
1214+
const bool hasAsyncGenerators_;
1215+
12091216
/// Set to true if we should enable ES6 block scoping.
12101217
const bool hasES6BlockScoping_;
12111218

0 commit comments

Comments
 (0)