13af6ab5fSopenharmony_ci/* 23af6ab5fSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd. 33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License. 53af6ab5fSopenharmony_ci * You may obtain a copy of the License at 63af6ab5fSopenharmony_ci * 73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 83af6ab5fSopenharmony_ci * 93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and 133af6ab5fSopenharmony_ci * limitations under the License. 143af6ab5fSopenharmony_ci */ 153af6ab5fSopenharmony_ci 163af6ab5fSopenharmony_ci#include "objectLiteralLowering.h" 173af6ab5fSopenharmony_ci 183af6ab5fSopenharmony_ci#include "checker/ETSchecker.h" 193af6ab5fSopenharmony_ci#include "compiler/lowering/scopesInit/scopesInitPhase.h" 203af6ab5fSopenharmony_ci#include "compiler/lowering/util.h" 213af6ab5fSopenharmony_ci 223af6ab5fSopenharmony_cinamespace ark::es2panda::compiler { 233af6ab5fSopenharmony_ci 243af6ab5fSopenharmony_cistd::string_view ObjectLiteralLowering::Name() const 253af6ab5fSopenharmony_ci{ 263af6ab5fSopenharmony_ci return "ObjectLiteralLowering"; 273af6ab5fSopenharmony_ci} 283af6ab5fSopenharmony_ci 293af6ab5fSopenharmony_cistatic void MaybeAllowConstAssign(checker::Type *targetType, ArenaVector<ir::Statement *> &statements) 303af6ab5fSopenharmony_ci{ 313af6ab5fSopenharmony_ci if (!targetType->IsETSObjectType()) { 323af6ab5fSopenharmony_ci return; 333af6ab5fSopenharmony_ci } 343af6ab5fSopenharmony_ci for (auto *const stmt : statements) { 353af6ab5fSopenharmony_ci if (!stmt->IsExpressionStatement() || 363af6ab5fSopenharmony_ci !stmt->AsExpressionStatement()->GetExpression()->IsAssignmentExpression()) { 373af6ab5fSopenharmony_ci continue; 383af6ab5fSopenharmony_ci } 393af6ab5fSopenharmony_ci 403af6ab5fSopenharmony_ci auto *const assignmentExpr = stmt->AsExpressionStatement()->GetExpression()->AsAssignmentExpression(); 413af6ab5fSopenharmony_ci auto *const variable = assignmentExpr->Left()->AsMemberExpression()->Property()->AsIdentifier()->Variable(); 423af6ab5fSopenharmony_ci 433af6ab5fSopenharmony_ci if (variable != nullptr && variable->HasFlag(varbinder::VariableFlags::READONLY)) { 443af6ab5fSopenharmony_ci assignmentExpr->SetIgnoreConstAssign(); 453af6ab5fSopenharmony_ci } 463af6ab5fSopenharmony_ci } 473af6ab5fSopenharmony_ci} 483af6ab5fSopenharmony_ci 493af6ab5fSopenharmony_cistatic constexpr std::string_view NESTED_BLOCK_EXPRESSION = "_$NESTED_BLOCK_EXPRESSION$_"; 503af6ab5fSopenharmony_ci 513af6ab5fSopenharmony_cistatic void RestoreNestedBlockExpression(const ArenaVector<ir::Statement *> &statements, 523af6ab5fSopenharmony_ci std::deque<ir::BlockExpression *> &nestedBlckExprs, varbinder::Scope *scope) 533af6ab5fSopenharmony_ci{ 543af6ab5fSopenharmony_ci if (!nestedBlckExprs.empty()) { 553af6ab5fSopenharmony_ci for (auto stmt : statements) { 563af6ab5fSopenharmony_ci if (!stmt->IsExpressionStatement() || 573af6ab5fSopenharmony_ci !stmt->AsExpressionStatement()->GetExpression()->IsAssignmentExpression()) { 583af6ab5fSopenharmony_ci continue; 593af6ab5fSopenharmony_ci } 603af6ab5fSopenharmony_ci 613af6ab5fSopenharmony_ci auto *assign = stmt->AsExpressionStatement()->GetExpression()->AsAssignmentExpression(); 623af6ab5fSopenharmony_ci 633af6ab5fSopenharmony_ci if (assign->Right()->IsStringLiteral() && 643af6ab5fSopenharmony_ci assign->Right()->AsStringLiteral()->Str().Is(NESTED_BLOCK_EXPRESSION)) { 653af6ab5fSopenharmony_ci auto nestedBlckExpr = nestedBlckExprs.front(); 663af6ab5fSopenharmony_ci nestedBlckExprs.pop_front(); 673af6ab5fSopenharmony_ci nestedBlckExpr->Scope()->SetParent(scope); 683af6ab5fSopenharmony_ci assign->SetRight(nestedBlckExpr); 693af6ab5fSopenharmony_ci } 703af6ab5fSopenharmony_ci } 713af6ab5fSopenharmony_ci // All nested block expressions should be restored 723af6ab5fSopenharmony_ci ASSERT(nestedBlckExprs.empty()); 733af6ab5fSopenharmony_ci } 743af6ab5fSopenharmony_ci} 753af6ab5fSopenharmony_ci 763af6ab5fSopenharmony_cistatic void AllowRequiredTypeInstantiation(const ir::Expression *const loweringResult) 773af6ab5fSopenharmony_ci{ 783af6ab5fSopenharmony_ci if (!loweringResult->IsBlockExpression()) { 793af6ab5fSopenharmony_ci return; 803af6ab5fSopenharmony_ci } 813af6ab5fSopenharmony_ci 823af6ab5fSopenharmony_ci const auto *const blockExpression = loweringResult->AsBlockExpression(); 833af6ab5fSopenharmony_ci const auto *const firstStatement = blockExpression->Statements().front(); 843af6ab5fSopenharmony_ci if (!firstStatement->IsVariableDeclaration() || 853af6ab5fSopenharmony_ci !firstStatement->AsVariableDeclaration()->Declarators().front()->Init()->IsETSNewClassInstanceExpression()) { 863af6ab5fSopenharmony_ci return; 873af6ab5fSopenharmony_ci } 883af6ab5fSopenharmony_ci 893af6ab5fSopenharmony_ci const auto *const varDecl = firstStatement->AsVariableDeclaration()->Declarators().front()->Init(); 903af6ab5fSopenharmony_ci 913af6ab5fSopenharmony_ci varDecl->AddAstNodeFlags(ir::AstNodeFlags::ALLOW_REQUIRED_INSTANTIATION); 923af6ab5fSopenharmony_ci 933af6ab5fSopenharmony_ci for (auto *const stmt : blockExpression->Statements()) { 943af6ab5fSopenharmony_ci if (!stmt->IsExpressionStatement() || 953af6ab5fSopenharmony_ci !stmt->AsExpressionStatement()->GetExpression()->IsAssignmentExpression() || 963af6ab5fSopenharmony_ci !stmt->AsExpressionStatement()->GetExpression()->AsAssignmentExpression()->Right()->IsBlockExpression()) { 973af6ab5fSopenharmony_ci continue; 983af6ab5fSopenharmony_ci } 993af6ab5fSopenharmony_ci 1003af6ab5fSopenharmony_ci AllowRequiredTypeInstantiation( 1013af6ab5fSopenharmony_ci stmt->AsExpressionStatement()->GetExpression()->AsAssignmentExpression()->Right()->AsBlockExpression()); 1023af6ab5fSopenharmony_ci } 1033af6ab5fSopenharmony_ci} 1043af6ab5fSopenharmony_ci 1053af6ab5fSopenharmony_cistatic void GenerateNewStatements(checker::ETSChecker *checker, ir::ObjectExpression *objExpr, std::stringstream &ss, 1063af6ab5fSopenharmony_ci std::vector<ir::AstNode *> &newStmts, 1073af6ab5fSopenharmony_ci std::deque<ir::BlockExpression *> &nestedBlckExprs) 1083af6ab5fSopenharmony_ci{ 1093af6ab5fSopenharmony_ci auto *const allocator = checker->Allocator(); 1103af6ab5fSopenharmony_ci 1113af6ab5fSopenharmony_ci auto *const classType = objExpr->TsType()->AsETSObjectType(); 1123af6ab5fSopenharmony_ci 1133af6ab5fSopenharmony_ci auto addNode = [&newStmts](ir::AstNode *node) -> int { 1143af6ab5fSopenharmony_ci newStmts.emplace_back(node); 1153af6ab5fSopenharmony_ci return newStmts.size(); 1163af6ab5fSopenharmony_ci }; 1173af6ab5fSopenharmony_ci 1183af6ab5fSopenharmony_ci // Generating: let <genSym>: <TsType> = new <TsType>(); 1193af6ab5fSopenharmony_ci auto *genSymIdent = Gensym(allocator); 1203af6ab5fSopenharmony_ci auto *type = checker->AllocNode<ir::OpaqueTypeNode>(classType); 1213af6ab5fSopenharmony_ci ss << "let @@I" << addNode(genSymIdent) << ": @@T" << addNode(type) << " = new @@T" 1223af6ab5fSopenharmony_ci << addNode(type->Clone(allocator, nullptr)) << "();" << std::endl; 1233af6ab5fSopenharmony_ci 1243af6ab5fSopenharmony_ci // Generating: <genSym>.key_i = value_i ( i <= [0, object_literal.properties.size) ) 1253af6ab5fSopenharmony_ci for (auto *propExpr : objExpr->Properties()) { 1263af6ab5fSopenharmony_ci ASSERT(propExpr->IsProperty()); 1273af6ab5fSopenharmony_ci auto *prop = propExpr->AsProperty(); 1283af6ab5fSopenharmony_ci ir::Expression *key = prop->Key(); 1293af6ab5fSopenharmony_ci ir::Expression *value = prop->Value(); 1303af6ab5fSopenharmony_ci 1313af6ab5fSopenharmony_ci ir::Identifier *keyIdent = key->IsStringLiteral() 1323af6ab5fSopenharmony_ci ? checker->AllocNode<ir::Identifier>(key->AsStringLiteral()->Str(), allocator) 1333af6ab5fSopenharmony_ci : key->AsIdentifier(); 1343af6ab5fSopenharmony_ci 1353af6ab5fSopenharmony_ci ss << "@@I" << addNode(genSymIdent->Clone(allocator, nullptr)) << ".@@I" << addNode(keyIdent); 1363af6ab5fSopenharmony_ci 1373af6ab5fSopenharmony_ci if (value->IsBlockExpression()) { 1383af6ab5fSopenharmony_ci // Case of nested object literal (all nested object literals has already been processed) 1393af6ab5fSopenharmony_ci // Corresponding nested block expressions should be stored somewhere and restored after ScopesPhase 1403af6ab5fSopenharmony_ci // Because it has already processed them 1413af6ab5fSopenharmony_ci // Predefined String Literal acts as placeholder 1423af6ab5fSopenharmony_ci ss << " = \"" << NESTED_BLOCK_EXPRESSION << "\";" << std::endl; 1433af6ab5fSopenharmony_ci nestedBlckExprs.emplace_back(value->AsBlockExpression()); 1443af6ab5fSopenharmony_ci } else { 1453af6ab5fSopenharmony_ci ss << " = @@E" << addNode(value) << ";" << std::endl; 1463af6ab5fSopenharmony_ci } 1473af6ab5fSopenharmony_ci } 1483af6ab5fSopenharmony_ci 1493af6ab5fSopenharmony_ci ss << "(@@I" << addNode(genSymIdent->Clone(allocator, nullptr)) << ");" << std::endl; 1503af6ab5fSopenharmony_ci} 1513af6ab5fSopenharmony_ci 1523af6ab5fSopenharmony_cistatic ir::AstNode *HandleObjectLiteralLowering(public_lib::Context *ctx, ir::ObjectExpression *objExpr) 1533af6ab5fSopenharmony_ci{ 1543af6ab5fSopenharmony_ci /* 1553af6ab5fSopenharmony_ci * For given object literal of class type generates following block expression: 1563af6ab5fSopenharmony_ci * 1573af6ab5fSopenharmony_ci * ({ 1583af6ab5fSopenharmony_ci * let <genSym>: <TsType> = new <TsType>(); 1593af6ab5fSopenharmony_ci * <genSym>.key_i = value_i ( i <= [0, object_literal.properties.size) ) 1603af6ab5fSopenharmony_ci * <genSym>; <-- NOTE: result of block expression 1613af6ab5fSopenharmony_ci * }) 1623af6ab5fSopenharmony_ci */ 1633af6ab5fSopenharmony_ci 1643af6ab5fSopenharmony_ci if (objExpr->TsType() == nullptr) { 1653af6ab5fSopenharmony_ci return objExpr; 1663af6ab5fSopenharmony_ci } 1673af6ab5fSopenharmony_ci 1683af6ab5fSopenharmony_ci auto *const checker = ctx->checker->AsETSChecker(); 1693af6ab5fSopenharmony_ci auto *const parser = ctx->parser->AsETSParser(); 1703af6ab5fSopenharmony_ci auto *const varbinder = ctx->checker->VarBinder()->AsETSBinder(); 1713af6ab5fSopenharmony_ci 1723af6ab5fSopenharmony_ci std::stringstream ss; 1733af6ab5fSopenharmony_ci // Double-ended queue for storing nested block expressions that have already been processed earlier 1743af6ab5fSopenharmony_ci std::deque<ir::BlockExpression *> nestedBlckExprs; 1753af6ab5fSopenharmony_ci std::vector<ir::AstNode *> newStmts; 1763af6ab5fSopenharmony_ci 1773af6ab5fSopenharmony_ci GenerateNewStatements(checker, objExpr, ss, newStmts, nestedBlckExprs); 1783af6ab5fSopenharmony_ci 1793af6ab5fSopenharmony_ci auto *loweringResult = parser->CreateFormattedExpression(ss.str(), newStmts); 1803af6ab5fSopenharmony_ci loweringResult->SetParent(objExpr->Parent()); 1813af6ab5fSopenharmony_ci 1823af6ab5fSopenharmony_ci MaybeAllowConstAssign(objExpr->TsType(), loweringResult->AsBlockExpression()->Statements()); 1833af6ab5fSopenharmony_ci 1843af6ab5fSopenharmony_ci auto scopeCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, NearestScope(objExpr)); 1853af6ab5fSopenharmony_ci InitScopesPhaseETS::RunExternalNode(loweringResult, varbinder); 1863af6ab5fSopenharmony_ci 1873af6ab5fSopenharmony_ci // Restoring nested block expressions 1883af6ab5fSopenharmony_ci RestoreNestedBlockExpression(loweringResult->AsBlockExpression()->Statements(), nestedBlckExprs, 1893af6ab5fSopenharmony_ci loweringResult->Scope()); 1903af6ab5fSopenharmony_ci 1913af6ab5fSopenharmony_ci varbinder->ResolveReferencesForScope(loweringResult, NearestScope(loweringResult)); 1923af6ab5fSopenharmony_ci 1933af6ab5fSopenharmony_ci AllowRequiredTypeInstantiation(loweringResult); 1943af6ab5fSopenharmony_ci 1953af6ab5fSopenharmony_ci checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; 1963af6ab5fSopenharmony_ci loweringResult->Check(checker); 1973af6ab5fSopenharmony_ci 1983af6ab5fSopenharmony_ci return loweringResult; 1993af6ab5fSopenharmony_ci} 2003af6ab5fSopenharmony_ci 2013af6ab5fSopenharmony_cibool ObjectLiteralLowering::Perform(public_lib::Context *ctx, parser::Program *program) 2023af6ab5fSopenharmony_ci{ 2033af6ab5fSopenharmony_ci if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB) { 2043af6ab5fSopenharmony_ci for (auto &[_, extPrograms] : program->ExternalSources()) { 2053af6ab5fSopenharmony_ci (void)_; 2063af6ab5fSopenharmony_ci for (auto *extProg : extPrograms) { 2073af6ab5fSopenharmony_ci Perform(ctx, extProg); 2083af6ab5fSopenharmony_ci } 2093af6ab5fSopenharmony_ci } 2103af6ab5fSopenharmony_ci } 2113af6ab5fSopenharmony_ci 2123af6ab5fSopenharmony_ci program->Ast()->TransformChildrenRecursively( 2133af6ab5fSopenharmony_ci [ctx](ir::AstNode *ast) -> ir::AstNode * { 2143af6ab5fSopenharmony_ci // Skip processing dynamic objects 2153af6ab5fSopenharmony_ci if (ast->IsObjectExpression() && !ast->AsObjectExpression()->TsType()->AsETSObjectType()->HasObjectFlag( 2163af6ab5fSopenharmony_ci checker::ETSObjectFlags::DYNAMIC)) { 2173af6ab5fSopenharmony_ci return HandleObjectLiteralLowering(ctx, ast->AsObjectExpression()); 2183af6ab5fSopenharmony_ci } 2193af6ab5fSopenharmony_ci return ast; 2203af6ab5fSopenharmony_ci }, 2213af6ab5fSopenharmony_ci Name()); 2223af6ab5fSopenharmony_ci 2233af6ab5fSopenharmony_ci return true; 2243af6ab5fSopenharmony_ci} 2253af6ab5fSopenharmony_ci 2263af6ab5fSopenharmony_cibool ObjectLiteralLowering::ExternalSourcesPostcondition(public_lib::Context *ctx, const parser::Program *program) 2273af6ab5fSopenharmony_ci{ 2283af6ab5fSopenharmony_ci for (auto &[_, extPrograms] : program->ExternalSources()) { 2293af6ab5fSopenharmony_ci (void)_; 2303af6ab5fSopenharmony_ci for (auto *extProg : extPrograms) { 2313af6ab5fSopenharmony_ci if (!Postcondition(ctx, extProg)) { 2323af6ab5fSopenharmony_ci return false; 2333af6ab5fSopenharmony_ci } 2343af6ab5fSopenharmony_ci } 2353af6ab5fSopenharmony_ci } 2363af6ab5fSopenharmony_ci return true; 2373af6ab5fSopenharmony_ci} 2383af6ab5fSopenharmony_ci 2393af6ab5fSopenharmony_cibool ObjectLiteralLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program) 2403af6ab5fSopenharmony_ci{ 2413af6ab5fSopenharmony_ci if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB && 2423af6ab5fSopenharmony_ci !ExternalSourcesPostcondition(ctx, program)) { 2433af6ab5fSopenharmony_ci return false; 2443af6ab5fSopenharmony_ci } 2453af6ab5fSopenharmony_ci 2463af6ab5fSopenharmony_ci // In all object literal contexts (except dynamic) a substitution should take place 2473af6ab5fSopenharmony_ci return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) -> bool { 2483af6ab5fSopenharmony_ci return ast->IsObjectExpression() && 2493af6ab5fSopenharmony_ci !ast->AsObjectExpression()->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC); 2503af6ab5fSopenharmony_ci }); 2513af6ab5fSopenharmony_ci} 2523af6ab5fSopenharmony_ci 2533af6ab5fSopenharmony_ci} // namespace ark::es2panda::compiler 254