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 for (auto &[_, extPrograms] : program->ExternalSources()) {
204 (void)_;
205 for (auto *extProg : extPrograms) {
206 Perform(ctx, extProg);
207 }
208 }
209
210 program->Ast()->TransformChildrenRecursively(
211 [ctx](ir::AstNode *ast) -> ir::AstNode * {
212 // Skip processing dynamic objects
213 if (ast->IsObjectExpression() && !ast->AsObjectExpression()->TsType()->AsETSObjectType()->HasObjectFlag(
214 checker::ETSObjectFlags::DYNAMIC)) {
215 return HandleObjectLiteralLowering(ctx, ast->AsObjectExpression());
216 }
217 return ast;
218 },
219 Name());
220
221 return true;
222 }
223
Postcondition(public_lib::Context * ctx,const parser::Program * program)224 bool ObjectLiteralLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program)
225 {
226 for (auto &[_, extPrograms] : program->ExternalSources()) {
227 (void)_;
228 for (auto *extProg : extPrograms) {
229 if (!Postcondition(ctx, extProg)) {
230 return false;
231 }
232 }
233 }
234
235 // In all object literal contexts (except dynamic) a substitution should take place
236 return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) -> bool {
237 return ast->IsObjectExpression() &&
238 !ast->AsObjectExpression()->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC);
239 });
240 }
241
242 } // namespace ark::es2panda::compiler
243