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 22namespace ark::es2panda::compiler { 23 24std::string_view ObjectLiteralLowering::Name() const 25{ 26 return "ObjectLiteralLowering"; 27} 28 29static 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 49static constexpr std::string_view NESTED_BLOCK_EXPRESSION = "_$NESTED_BLOCK_EXPRESSION$_"; 50 51static 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 76static 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 105static 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 152static 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 201bool 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 226bool 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 239bool 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