/* * Copyright (c) 2021-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ETSCompiler.h" #include "compiler/base/catchTable.h" #include "checker/ets/dynamic/dynamicCall.h" #include "compiler/base/condition.h" #include "compiler/core/ETSGen-inl.h" #include "compiler/base/lreference.h" #include "compiler/core/switchBuilder.h" #include "compiler/function/functionBuilder.h" #include "checker/ETSchecker.h" #include "checker/types/ets/etsDynamicFunctionType.h" #include "checker/types/ets/etsTupleType.h" namespace ark::es2panda::compiler { ETSGen *ETSCompiler::GetETSGen() const { return static_cast(GetCodeGen()); } void ETSCompiler::Compile(const ir::CatchClause *st) const { ETSGen *etsg = GetETSGen(); compiler::LocalRegScope lrs(etsg, st->Scope()->ParamScope()); etsg->SetAccumulatorType(st->TsType()); auto lref = compiler::ETSLReference::Create(etsg, st->Param(), true); lref.SetValue(); st->Body()->Compile(etsg); } void ETSCompiler::Compile(const ir::ClassProperty *st) const { ETSGen *etsg = GetETSGen(); if (st->Value() == nullptr && st->TsType()->IsETSPrimitiveType()) { return; } auto ttctx = compiler::TargetTypeContext(etsg, st->TsType()); compiler::RegScope rs(etsg); ir::BoxingUnboxingFlags flags = (st->Value() != nullptr) ? st->Value()->GetBoxingUnboxingFlags() : ir::BoxingUnboxingFlags::NONE; if (st->Value() == nullptr) { etsg->LoadDefaultValue(st, st->TsType()); } else { st->Value()->Compile(etsg); etsg->ApplyConversion(st->Value(), st->TsType()); st->Value()->SetBoxingUnboxingFlags(flags); } if (st->IsStatic()) { etsg->StoreStaticOwnProperty(st, st->TsType(), st->Key()->AsIdentifier()->Name()); } else { etsg->StoreProperty(st, st->TsType(), etsg->GetThisReg(), st->Key()->AsIdentifier()->Name()); } } void ETSCompiler::Compile(const ir::TemplateElement *expr) const { ETSGen *etsg = GetETSGen(); etsg->LoadAccumulatorString(expr, expr->Cooked()); etsg->SetAccumulatorType(expr->TsType()); ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); } void ETSCompiler::Compile(const ir::ETSClassLiteral *expr) const { ETSGen *etsg = GetETSGen(); auto *literal = expr->Expr(); auto *literalType = literal->TsType(); bool const isPrimitive = !literalType->IsETSReferenceType(); if (!isPrimitive) { literal->Compile(etsg); } else { ES2PANDA_ASSERT(literalType->IsETSPrimitiveType()); etsg->SetAccumulatorType(literalType); } etsg->GetType(expr, isPrimitive); ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); } void ETSCompiler::Compile(const ir::ETSFunctionType *node) const { ETSGen *etsg = GetETSGen(); etsg->LoadAccumulatorPoison(node, node->TsType()); } void ETSCompiler::Compile(const ir::ETSNewArrayInstanceExpression *expr) const { ETSGen *etsg = GetETSGen(); auto const checker = const_cast(etsg->Checker()); compiler::RegScope rs(etsg); compiler::TargetTypeContext ttctx(etsg, checker->GlobalIntType()); expr->Dimension()->Compile(etsg); compiler::VReg arr = etsg->AllocReg(); compiler::VReg dim = etsg->AllocReg(); etsg->ApplyConversionAndStoreAccumulator(expr, dim, expr->Dimension()->TsType()); etsg->NewArray(expr, arr, dim, expr->TsType()); const auto *elementType = expr->TypeReference()->TsType(); const bool undefAssignable = checker->Relation()->IsSupertypeOf(elementType, checker->GlobalETSUndefinedType()); if (elementType->IsETSPrimitiveType() || undefAssignable) { // no-op } else { compiler::VReg countReg = etsg->AllocReg(); auto *startLabel = etsg->AllocLabel(); auto *endLabel = etsg->AllocLabel(); etsg->MoveImmediateToRegister(expr, countReg, checker::TypeFlag::INT, static_cast(0)); const auto indexReg = etsg->AllocReg(); etsg->SetLabel(expr, startLabel); etsg->LoadAccumulator(expr, dim); etsg->JumpCompareRegister(expr, countReg, endLabel); etsg->LoadAccumulator(expr, countReg); etsg->StoreAccumulator(expr, indexReg); if (expr->Signature() != nullptr) { const compiler::TargetTypeContext ttctx2(etsg, elementType); ArenaVector arguments(GetCodeGen()->Allocator()->Adapter()); etsg->InitObject(expr, expr->Signature(), arguments); } else { etsg->LoadAccumulatorPoison(expr, elementType); } etsg->StoreArrayElement(expr, arr, indexReg, elementType); etsg->IncrementImmediateRegister(expr, countReg, checker::TypeFlag::INT, static_cast(1)); etsg->JumpTo(expr, startLabel); etsg->SetLabel(expr, endLabel); } etsg->SetVRegType(arr, expr->TsType()); etsg->LoadAccumulator(expr, arr); ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); } static std::pair LoadDynamicName(compiler::ETSGen *etsg, const ir::AstNode *node, const ArenaVector &dynName, bool isConstructor) { auto *checker = const_cast(etsg->Checker()->AsETSChecker()); auto *callNames = checker->DynamicCallNames(isConstructor); auto qnameStart = etsg->AllocReg(); auto qnameLen = etsg->AllocReg(); TargetTypeContext ttctx(etsg, nullptr); // without this ints will be cast to JSValue etsg->LoadAccumulatorInt(node, callNames->at(dynName)); etsg->StoreAccumulator(node, qnameStart); etsg->LoadAccumulatorInt(node, dynName.size()); etsg->StoreAccumulator(node, qnameLen); return {qnameStart, qnameLen}; } static void CreateDynamicObject(const ir::AstNode *node, compiler::ETSGen *etsg, const ir::Expression *typeRef, checker::Signature *signature, const ArenaVector &arguments) { auto objReg = etsg->AllocReg(); auto callInfo = checker::DynamicCall::ResolveCall(etsg->VarBinder(), typeRef); if (callInfo.obj->IsETSImportDeclaration()) { etsg->LoadAccumulatorDynamicModule(node, callInfo.obj->AsETSImportDeclaration()); } else { callInfo.obj->Compile(etsg); } etsg->StoreAccumulator(node, objReg); auto [qnameStart, qnameLen] = LoadDynamicName(etsg, node, callInfo.name, true); etsg->CallDynamic(ETSGen::CallDynamicData {node, objReg, qnameStart}, qnameLen, signature, arguments); } static void ConvertRestArguments(checker::ETSChecker *const checker, const ir::ETSNewClassInstanceExpression *expr) { if (expr->GetSignature()->RestVar() != nullptr && (expr->GetSignature()->RestVar()->TsType()->IsETSArrayType() || expr->GetSignature()->RestVar()->TsType()->IsETSTupleType())) { std::size_t const argumentCount = expr->GetArguments().size(); std::size_t const parameterCount = expr->GetSignature()->Params().size(); ES2PANDA_ASSERT(argumentCount >= parameterCount); auto &arguments = const_cast &>(expr->GetArguments()); std::size_t i = parameterCount; if (i < argumentCount && expr->GetArguments()[i]->IsSpreadElement()) { arguments[i] = expr->GetArguments()[i]->AsSpreadElement()->Argument(); } else if (!expr->GetSignature()->RestVar()->TsType()->IsETSTupleType()) { ArenaVector elements(checker->Allocator()->Adapter()); for (; i < argumentCount; ++i) { elements.emplace_back(expr->GetArguments()[i]); } auto *arrayExpression = checker->AllocNode(std::move(elements), checker->Allocator()); ES2PANDA_ASSERT(arrayExpression != nullptr); arrayExpression->SetParent(const_cast(expr)); auto restType = expr->GetSignature()->RestVar()->TsType()->AsETSArrayType(); arrayExpression->SetTsType(restType); arrayExpression->SetPreferredType(restType->ElementType()); arguments.erase(expr->GetArguments().begin() + parameterCount, expr->GetArguments().end()); arguments.emplace_back(arrayExpression); } } } static void HandleUnionTypeInForOf(compiler::ETSGen *etsg, checker::Type const *const exprType, const ir::ForOfStatement *st, VReg objReg, VReg *countReg) { ArenaVector