1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "objectLiteralLowering.h"
17
18 #include "checker/ETSchecker.h"
19 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
20 #include "compiler/lowering/util.h"
21
22 namespace ark::es2panda::compiler {
23
Name() const24 std::string_view ObjectLiteralLowering::Name() const
25 {
26 return "ObjectLiteralLowering";
27 }
28
MaybeAllowConstAssign(checker::Type *targetType, ArenaVector<ir::Statement *> &statements)29 static void MaybeAllowConstAssign(checker::Type *targetType, ArenaVector<ir::Statement *> &statements)
30 {
31 if (!targetType->IsETSObjectType()) {
32 return;
33 }
34 for (auto *const stmt : statements) {
35 if (!stmt->IsExpressionStatement() ||
36 !stmt->AsExpressionStatement()->GetExpression()->IsAssignmentExpression()) {
37 continue;
38 }
39
40 auto *const assignmentExpr = stmt->AsExpressionStatement()->GetExpression()->AsAssignmentExpression();
41 auto *const variable = assignmentExpr->Left()->AsMemberExpression()->Property()->AsIdentifier()->Variable();
42
43 if (variable != nullptr && variable->HasFlag(varbinder::VariableFlags::READONLY)) {
44 assignmentExpr->SetIgnoreConstAssign();
45 }
46 }
47 }
48
49 static constexpr std::string_view NESTED_BLOCK_EXPRESSION = "_$NESTED_BLOCK_EXPRESSION$_";
50
RestoreNestedBlockExpression(const ArenaVector<ir::Statement *> &statements, std::deque<ir::BlockExpression *> &nestedBlckExprs, varbinder::Scope *scope)51 static void RestoreNestedBlockExpression(const ArenaVector<ir::Statement *> &statements,
52 std::deque<ir::BlockExpression *> &nestedBlckExprs, varbinder::Scope *scope)
53 {
54 if (!nestedBlckExprs.empty()) {
55 for (auto stmt : statements) {
56 if (!stmt->IsExpressionStatement() ||
57 !stmt->AsExpressionStatement()->GetExpression()->IsAssignmentExpression()) {
58 continue;
59 }
60
61 auto *assign = stmt->AsExpressionStatement()->GetExpression()->AsAssignmentExpression();
62
63 if (assign->Right()->IsStringLiteral() &&
64 assign->Right()->AsStringLiteral()->Str().Is(NESTED_BLOCK_EXPRESSION)) {
65 auto nestedBlckExpr = nestedBlckExprs.front();
66 nestedBlckExprs.pop_front();
67 nestedBlckExpr->Scope()->SetParent(scope);
68 assign->SetRight(nestedBlckExpr);
69 }
70 }
71 // All nested block expressions should be restored
72 ASSERT(nestedBlckExprs.empty());
73 }
74 }
75
AllowRequiredTypeInstantiation(const ir::Expression *const loweringResult)76 static void AllowRequiredTypeInstantiation(const ir::Expression *const loweringResult)
77 {
78 if (!loweringResult->IsBlockExpression()) {
79 return;
80 }
81
82 const auto *const blockExpression = loweringResult->AsBlockExpression();
83 const auto *const firstStatement = blockExpression->Statements().front();
84 if (!firstStatement->IsVariableDeclaration() ||
85 !firstStatement->AsVariableDeclaration()->Declarators().front()->Init()->IsETSNewClassInstanceExpression()) {
86 return;
87 }
88
89 const auto *const varDecl = firstStatement->AsVariableDeclaration()->Declarators().front()->Init();
90
91 varDecl->AddAstNodeFlags(ir::AstNodeFlags::ALLOW_REQUIRED_INSTANTIATION);
92
93 for (auto *const stmt : blockExpression->Statements()) {
94 if (!stmt->IsExpressionStatement() ||
95 !stmt->AsExpressionStatement()->GetExpression()->IsAssignmentExpression() ||
96 !stmt->AsExpressionStatement()->GetExpression()->AsAssignmentExpression()->Right()->IsBlockExpression()) {
97 continue;
98 }
99
100 AllowRequiredTypeInstantiation(
101 stmt->AsExpressionStatement()->GetExpression()->AsAssignmentExpression()->Right()->AsBlockExpression());
102 }
103 }
104
GenerateNewStatements(checker::ETSChecker *checker, ir::ObjectExpression *objExpr, std::stringstream &ss, std::vector<ir::AstNode *> &newStmts, std::deque<ir::BlockExpression *> &nestedBlckExprs)105 static void GenerateNewStatements(checker::ETSChecker *checker, ir::ObjectExpression *objExpr, std::stringstream &ss,
106 std::vector<ir::AstNode *> &newStmts,
107 std::deque<ir::BlockExpression *> &nestedBlckExprs)
108 {
109 auto *const allocator = checker->Allocator();
110
111 auto *const classType = objExpr->TsType()->AsETSObjectType();
112
113 auto addNode = [&newStmts](ir::AstNode *node) -> int {
114 newStmts.emplace_back(node);
115 return newStmts.size();
116 };
117
118 // Generating: let <genSym>: <TsType> = new <TsType>();
119 auto *genSymIdent = Gensym(allocator);
120 auto *type = checker->AllocNode<ir::OpaqueTypeNode>(classType);
121 ss << "let @@I" << addNode(genSymIdent) << ": @@T" << addNode(type) << " = new @@T"
122 << addNode(type->Clone(allocator, nullptr)) << "();" << std::endl;
123
124 // Generating: <genSym>.key_i = value_i ( i <= [0, object_literal.properties.size) )
125 for (auto *propExpr : objExpr->Properties()) {
126 ASSERT(propExpr->IsProperty());
127 auto *prop = propExpr->AsProperty();
128 ir::Expression *key = prop->Key();
129 ir::Expression *value = prop->Value();
130
131 ir::Identifier *keyIdent = key->IsStringLiteral()
132 ? checker->AllocNode<ir::Identifier>(key->AsStringLiteral()->Str(), allocator)
133 : key->AsIdentifier();
134
135 ss << "@@I" << addNode(genSymIdent->Clone(allocator, nullptr)) << ".@@I" << addNode(keyIdent);
136
137 if (value->IsBlockExpression()) {
138 // Case of nested object literal (all nested object literals has already been processed)
139 // Corresponding nested block expressions should be stored somewhere and restored after ScopesPhase
140 // Because it has already processed them
141 // Predefined String Literal acts as placeholder
142 ss << " = \"" << NESTED_BLOCK_EXPRESSION << "\";" << std::endl;
143 nestedBlckExprs.emplace_back(value->AsBlockExpression());
144 } else {
145 ss << " = @@E" << addNode(value) << ";" << std::endl;
146 }
147 }
148
149 ss << "(@@I" << addNode(genSymIdent->Clone(allocator, nullptr)) << ");" << std::endl;
150 }
151
HandleObjectLiteralLowering(public_lib::Context *ctx, ir::ObjectExpression *objExpr)152 static ir::AstNode *HandleObjectLiteralLowering(public_lib::Context *ctx, ir::ObjectExpression *objExpr)
153 {
154 /*
155 * For given object literal of class type generates following block expression:
156 *
157 * ({
158 * let <genSym>: <TsType> = new <TsType>();
159 * <genSym>.key_i = value_i ( i <= [0, object_literal.properties.size) )
160 * <genSym>; <-- NOTE: result of block expression
161 * })
162 */
163
164 if (objExpr->TsType() == nullptr) {
165 return objExpr;
166 }
167
168 auto *const checker = ctx->checker->AsETSChecker();
169 auto *const parser = ctx->parser->AsETSParser();
170 auto *const varbinder = ctx->checker->VarBinder()->AsETSBinder();
171
172 std::stringstream ss;
173 // Double-ended queue for storing nested block expressions that have already been processed earlier
174 std::deque<ir::BlockExpression *> nestedBlckExprs;
175 std::vector<ir::AstNode *> newStmts;
176
177 GenerateNewStatements(checker, objExpr, ss, newStmts, nestedBlckExprs);
178
179 auto *loweringResult = parser->CreateFormattedExpression(ss.str(), newStmts);
180 loweringResult->SetParent(objExpr->Parent());
181
182 MaybeAllowConstAssign(objExpr->TsType(), loweringResult->AsBlockExpression()->Statements());
183
184 auto scopeCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, NearestScope(objExpr));
185 InitScopesPhaseETS::RunExternalNode(loweringResult, varbinder);
186
187 // Restoring nested block expressions
188 RestoreNestedBlockExpression(loweringResult->AsBlockExpression()->Statements(), nestedBlckExprs,
189 loweringResult->Scope());
190
191 varbinder->ResolveReferencesForScope(loweringResult, NearestScope(loweringResult));
192
193 AllowRequiredTypeInstantiation(loweringResult);
194
195 checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY};
196 loweringResult->Check(checker);
197
198 return loweringResult;
199 }
200
Perform(public_lib::Context *ctx, parser::Program *program)201 bool ObjectLiteralLowering::Perform(public_lib::Context *ctx, parser::Program *program)
202 {
203 if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB) {
204 for (auto &[_, extPrograms] : program->ExternalSources()) {
205 (void)_;
206 for (auto *extProg : extPrograms) {
207 Perform(ctx, extProg);
208 }
209 }
210 }
211
212 program->Ast()->TransformChildrenRecursively(
213 [ctx](ir::AstNode *ast) -> ir::AstNode * {
214 // Skip processing dynamic objects
215 if (ast->IsObjectExpression() && !ast->AsObjectExpression()->TsType()->AsETSObjectType()->HasObjectFlag(
216 checker::ETSObjectFlags::DYNAMIC)) {
217 return HandleObjectLiteralLowering(ctx, ast->AsObjectExpression());
218 }
219 return ast;
220 },
221 Name());
222
223 return true;
224 }
225
ExternalSourcesPostcondition(public_lib::Context *ctx, const parser::Program *program)226 bool ObjectLiteralLowering::ExternalSourcesPostcondition(public_lib::Context *ctx, const parser::Program *program)
227 {
228 for (auto &[_, extPrograms] : program->ExternalSources()) {
229 (void)_;
230 for (auto *extProg : extPrograms) {
231 if (!Postcondition(ctx, extProg)) {
232 return false;
233 }
234 }
235 }
236 return true;
237 }
238
Postcondition(public_lib::Context *ctx, const parser::Program *program)239 bool ObjectLiteralLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program)
240 {
241 if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB &&
242 !ExternalSourcesPostcondition(ctx, program)) {
243 return false;
244 }
245
246 // In all object literal contexts (except dynamic) a substitution should take place
247 return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) -> bool {
248 return ast->IsObjectExpression() &&
249 !ast->AsObjectExpression()->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC);
250 });
251 }
252
253 } // namespace ark::es2panda::compiler
254