diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 5a788f0982c1c3b03192087c2c64767440d065a7..d763c2f28cf17c7283afe28e77e2c609a27ac540 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -255,6 +255,7 @@ libes2panda_sources = [ "compiler/lowering/ets/topLevelStmts/topLevelStmts.cpp", "compiler/lowering/ets/typeFromLowering.cpp", "compiler/lowering/ets/unboxLowering.cpp", + "compiler/lowering/ets/transformToStaticInvoke.cpp", "compiler/lowering/ets/unionLowering.cpp", "compiler/lowering/ets/annotationCopyLowering.cpp", "compiler/lowering/ets/annotationCopyPostLowering.cpp", diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 214331408045028a732113408788dc4ef3eb74c6..487add136ae0ce87479d09c176f5838e729a351c 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -323,6 +323,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/annotationCopyPostLowering.cpp compiler/lowering/ets/primitiveConversionPhase.cpp compiler/lowering/ets/unboxLowering.cpp + compiler/lowering/ets/transformToStaticInvoke.cpp ir/astDump.cpp ir/srcDump.cpp ir/astNode.cpp diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 3aa4109c2eb8eb2d86c89482000a32026599fdd0..2e0c532ada2b21030d868318d8f3783410cd2998 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -1352,6 +1352,20 @@ checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallE auto &signatures = expr->IsETSConstructorCall() ? calleeType->AsETSObjectType()->ConstructSignatures() : calleeType->AsETSFunctionType()->CallSignaturesOfMethodOrArrow(); + if (checker->HasStatus(CheckerStatus::IN_CLASS_WITH_INSTANTIATE)) { + checker->RemoveStatus(CheckerStatus::IN_CLASS_WITH_INSTANTIATE); + for (auto &sig : signatures) { + sig->AddSignatureFlag(SignatureFlags::INSTANTIATE); + } + } + + if (checker->HasStatus(CheckerStatus::IN_CLASS_WITH_INVOKE)) { + checker->RemoveStatus(CheckerStatus::IN_CLASS_WITH_INVOKE); + for (auto &sig : signatures) { + sig->AddSignatureFlag(SignatureFlags::INVOKE); + } + } + return checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start()); } @@ -1378,6 +1392,7 @@ Type *ETSAnalyzer::GetReturnType(ir::CallExpression *expr, Type *calleeType) con if (!calleeType->IsETSFunctionType() && !expr->IsETSConstructorCall() && !calleeType->IsETSExtensionFuncHelperType()) { + std::cout << expr->DumpEtsSrc() << std::endl; checker->LogError(diagnostic::NO_CALL_SIGNATURE, {calleeType}, expr->Start()); return checker->GlobalTypeError(); } @@ -1502,16 +1517,14 @@ checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const } ES2PANDA_ASSERT(!expr->IsOptional()); - auto *oldCallee = expr->Callee(); + checker::Type *calleeType = checker->GetApparentType(expr->Callee()->Check(checker)); if (calleeType->IsTypeError()) { return checker->InvalidateType(expr); } - if (expr->Callee() != oldCallee) { - // If it is a static invoke, the callee will be transformed from an identifier to a member expression - // Type check the callee again for member expression - calleeType = checker->GetApparentType(expr->Callee()->Check(checker)); + if (checker->HasStatus(CheckerStatus::IN_CLASS_WITH_INVOKE) || checker->HasStatus(CheckerStatus::IN_CLASS_WITH_INSTANTIATE)) { + calleeType = checker->GetApparentType(expr->Callee()->Variable()->TsType()); } CheckCallee(checker, expr); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 978b0b2beed1b92403a6815b076183561f709bea..a8bb04e635ca154cf511070e6059543ab9316d2b 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -1031,11 +1031,7 @@ private: ArenaVector ExtendArgumentsWithFakeLamda(ir::CallExpression *callExpr); // Static invoke - bool SetStaticInvokeValues(ir::Identifier *const ident, ir::Identifier *classId, ir::Identifier *methodId, - varbinder::LocalVariable *instantiateMethod); - void CreateTransformedCallee(ir::Identifier *classId, ir::Identifier *methodId, ir::Identifier *const ident, - varbinder::LocalVariable *instantiateMethod); - bool TryTransformingToStaticInvoke(ir::Identifier *ident, const Type *resolvedType); + bool ValidateStaticInvoke(ir::Identifier *ident, const Type *resolvedType); // Partial Type *HandleUnionForPartialType(ETSUnionType *typeToBePartial); diff --git a/ets2panda/checker/checkerContext.h b/ets2panda/checker/checkerContext.h index aa5e6b26fcdbad5213fec9eecbe6e003013339d3..85f345ab47991d9025dc674e042777d67cd81ec4 100644 --- a/ets2panda/checker/checkerContext.h +++ b/ets2panda/checker/checkerContext.h @@ -77,6 +77,8 @@ enum class CheckerStatus : uint32_t { IN_SETTER = 1U << 27U, IN_EXTENSION_ACCESSOR_CHECK = 1U << 28U, IN_TYPE_INFER = 1U << 29U, + IN_CLASS_WITH_INSTANTIATE = 1U << 30U, + IN_CLASS_WITH_INVOKE = 1U << 31U, }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index 22f1c94672ee59da68944b5fef8c27d3f81db994..5843a8305f07b9d9ff1f2f1a06fde3455490c21f 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -492,6 +492,9 @@ bool ETSChecker::ValidateSignatureInvocationContext(Signature *substitutedSig, i flags |= (TypeRelationFlag::ONLY_CHECK_WIDENING); + if (substitutedSig->HasSignatureFlag(SignatureFlags::INSTANTIATE)) { + targetType = substitutedSig->Params()[index + 1]->TsType(); + } auto const invocationCtx = checker::InvocationContext(Relation(), argument, argumentType, targetType, argument->Start(), {{diagnostic::TYPE_MISMATCH_AT_IDX, {argumentType, targetType, index + 1}}}, flags); @@ -639,6 +642,10 @@ Signature *ETSChecker::ValidateSignature( compareCount = compareCount - 1; } + if (signature->HasSignatureFlag(SignatureFlags::INSTANTIATE)) { + compareCount = compareCount + 1; + } + if (compareCount < signature->MinArgCount() || (argCount > signature->ArgCount() && !hasRestParameter)) { if (reportError) { LogError(diagnostic::PARAM_COUNT_MISMATCH, {signature->MinArgCount(), argCount}, pos); @@ -776,6 +783,11 @@ ArenaVector ETSChecker::CollectSignatures(ArenaVector } } + if (!compatibleSignatures.empty() && signatures.size() == 1 && + signatures[0]->HasSignatureFlag(SignatureFlags::INSTANTIATE)) { + compatibleSignatures[0] = signatures[0]; + return compatibleSignatures; + } if (compatibleSignatures.empty() && notVisibleSignature != nullptr && ((resolveFlags & TypeRelationFlag::NO_THROW) == 0)) { LogError(diagnostic::SIG_INVISIBLE, {notVisibleSignature->Function()->Id()->Name(), notVisibleSignature}, pos); @@ -1293,6 +1305,10 @@ Signature *ETSChecker::MakeSignatureInvocable(Signature *sig, ir::CallExpression continue; } + if (sig->HasSignatureFlag(SignatureFlags::INSTANTIATE)) { + continue; + } + auto ctx = checker::AssignmentContext( Relation(), callExpr->Arguments().at(idx), callExpr->Arguments().at(idx)->TsType(), sig->Params().at(idx)->TsType(), callExpr->Arguments().at(idx)->Start(), diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 2d284a52d0cdb8acb5c41c3d66f89c35fa3dd538..30c5f37c983c129bf9882bca9ceec4310bab3ec0 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -2902,8 +2902,7 @@ void ETSChecker::GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *origin } } -// CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic -bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, const Type *resolvedType) +bool ETSChecker::ValidateStaticInvoke(ir::Identifier *const ident, const Type *resolvedType) { ES2PANDA_ASSERT(ident->Parent()->IsCallExpression()); ES2PANDA_ASSERT(ident->Parent()->AsCallExpression()->Callee() == ident); @@ -2913,7 +2912,6 @@ bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, cons } auto className = ident->Name(); - std::string_view propertyName; PropertySearchFlags searchFlag = PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::SEARCH_IN_BASE | PropertySearchFlags::SEARCH_STATIC_METHOD; @@ -2921,52 +2919,26 @@ bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, cons resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INSTANTIATE_METHOD, searchFlag); auto *invokeMethod = resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INVOKE_METHOD, searchFlag); - if (instantiateMethod != nullptr) { - propertyName = compiler::Signatures::STATIC_INSTANTIATE_METHOD; - } else if (invokeMethod != nullptr) { - propertyName = compiler::Signatures::STATIC_INVOKE_METHOD; - } else { + + if (!invokeMethod && !instantiateMethod) { LogError(diagnostic::NO_STATIC_INVOKE, {compiler::Signatures::STATIC_INVOKE_METHOD, compiler::Signatures::STATIC_INSTANTIATE_METHOD, className, className}, ident->Start()); return true; } - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *classId = ProgramAllocNode(className, ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *methodId = ProgramAllocNode(propertyName, ProgramAllocator()); - if (propertyName == compiler::Signatures::STATIC_INSTANTIATE_METHOD) { - methodId->SetVariable(instantiateMethod); - } else if (propertyName == compiler::Signatures::STATIC_INVOKE_METHOD) { - methodId->SetVariable(invokeMethod); - } - - auto *transformedCallee = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, - false); - classId->SetRange(ident->Range()); - methodId->SetRange(ident->Range()); - transformedCallee->SetRange(ident->Range()); + ident->SetTsType(invokeMethod != nullptr ? invokeMethod->TsType() : instantiateMethod->TsType()); + varbinder::LocalVariable *invokeinstantiateVar = + Allocator()->New(ident->Variable()->Declaration(), ident->Variable()->Flags()); + ident->SetVariable(invokeinstantiateVar); - auto *callExpr = ident->Parent()->AsCallExpression(); - transformedCallee->SetParent(callExpr); - callExpr->SetCallee(transformedCallee); - - if (instantiateMethod != nullptr) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *argExpr = GenerateImplicitInstantiateArg(std::string(className)); - - argExpr->SetParent(callExpr); - argExpr->SetRange(ident->Range()); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - VarBinder()->AsETSBinder()->HandleCustomNodes(argExpr); - - auto &arguments = callExpr->Arguments(); - arguments.insert(arguments.begin(), argExpr); + ident->Variable()->SetTsType(ident->TsType()); + if (invokeMethod) { + this->AddStatus(CheckerStatus::IN_CLASS_WITH_INVOKE); + } + if (instantiateMethod) { + this->AddStatus(CheckerStatus::IN_CLASS_WITH_INSTANTIATE); } return true; diff --git a/ets2panda/checker/ets/validateHelpers.cpp b/ets2panda/checker/ets/validateHelpers.cpp index 685c1d8b4babed5725f0907ff8a4534da1651ced..91bcbb10a70ba3c1a630ec3db8a9afb6b721ea41 100644 --- a/ets2panda/checker/ets/validateHelpers.cpp +++ b/ets2panda/checker/ets/validateHelpers.cpp @@ -71,8 +71,8 @@ void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, T if (type->IsETSFunctionType()) { return; } - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - if (TryTransformingToStaticInvoke(ident, type)) { + + if (ValidateStaticInvoke(ident, type)) { return; } diff --git a/ets2panda/checker/types/signature.h b/ets2panda/checker/types/signature.h index d94e607e8b2a5ab473eb2081fefd38dd9c6fc806..219a6fc04906733c40a8a12db0e686089ca7c075 100644 --- a/ets2panda/checker/types/signature.h +++ b/ets2panda/checker/types/signature.h @@ -91,6 +91,8 @@ enum class SignatureFlags : uint32_t { EXTENSION_FUNCTION = 1U << 18U, DUPLICATE_ASM = 1U << 19U, BRIDGE = 1U << 20U, + INSTANTIATE = 1U << 21U, + INVOKE = 1U << 22U, INTERNAL_PROTECTED = INTERNAL | PROTECTED, GETTER_OR_SETTER = GETTER | SETTER, diff --git a/ets2panda/compiler/lowering/ets/transformToStaticInvoke.cpp b/ets2panda/compiler/lowering/ets/transformToStaticInvoke.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b505ea5a66b69fadce7198bfa03bb4d841ebd82 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/transformToStaticInvoke.cpp @@ -0,0 +1,110 @@ +/** + * Copyright (c) 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 "checker/ETSchecker.h" +#include "varbinder/ETSBinder.h" +#include "transformToStaticInvoke.h" +#include +#include "compiler/lowering/util.h" + +namespace ark::es2panda::compiler { + +std::string_view TransformToStaticInvoke::Name() const +{ + return "TransformToStaticInvoke"; +} +bool TransformToStaticInvoke::IsNodeApplicable(ir::AstNode *const ast) +{ + return ast->IsCallExpression() && ast->AsCallExpression()->Callee()->IsIdentifier() && + (ast->AsCallExpression()->Signature()->HasSignatureFlag(checker::SignatureFlags::INSTANTIATE) || + ast->AsCallExpression()->Signature()->HasSignatureFlag(checker::SignatureFlags::INVOKE)); +} + +std::pair TransformToStaticInvoke::FindStaticCallable( + const checker::ETSObjectType *type) const +{ + checker::PropertySearchFlags searchFlags = checker::PropertySearchFlags::SEARCH_IN_INTERFACES | + checker::PropertySearchFlags::SEARCH_IN_BASE | + checker::PropertySearchFlags::SEARCH_STATIC_METHOD; + auto *invokeMethod = type->GetProperty(compiler::Signatures::STATIC_INVOKE_METHOD, searchFlags); + auto *instantiateMethod = type->GetProperty(compiler::Signatures::STATIC_INSTANTIATE_METHOD, searchFlags); + + return {invokeMethod, instantiateMethod}; +} +void TransformToStaticInvoke::TransformCallable(ir::AstNode *const ast, public_lib::Context *ctx) +{ + checker::ETSChecker *const checker = ctx->GetChecker()->AsETSChecker(); + if (!IsNodeApplicable(ast)) { + return; + } + auto ident = ast->AsCallExpression()->Callee()->AsIdentifier(); + const auto resolvedType = ast->AsCallExpression()->Callee()->Check(checker)->AsETSObjectType(); + std::string_view propertyName; + + auto [invokeMethod, instantiateMethod] = FindStaticCallable(resolvedType); + + if (instantiateMethod != nullptr) { + propertyName = compiler::Signatures::STATIC_INSTANTIATE_METHOD; + } else if (invokeMethod != nullptr) { + propertyName = compiler::Signatures::STATIC_INVOKE_METHOD; + } + + auto *classId = checker->AllocNode(ident->Name(), checker->Allocator()); + auto *methodId = checker->AllocNode(propertyName, checker->Allocator()); + + if (propertyName == compiler::Signatures::STATIC_INSTANTIATE_METHOD) { + methodId->SetVariable(instantiateMethod); + } else if (propertyName == compiler::Signatures::STATIC_INVOKE_METHOD) { + methodId->SetVariable(invokeMethod); + } + + auto *transformedCallee = checker->AllocNode( + classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + classId->SetRange(ident->Range()); + methodId->SetRange(ident->Range()); + transformedCallee->SetRange(ident->Range()); + + auto *callExpr = ast->AsCallExpression(); + transformedCallee->SetParent(callExpr); + callExpr->SetCallee(transformedCallee); + + if (instantiateMethod != nullptr) { + auto *argExpr = checker->GenerateImplicitInstantiateArg(std::string(ident->Name())); + + argExpr->SetParent(callExpr); + argExpr->SetRange(ident->Range()); + + checker->VarBinder()->AsETSBinder()->HandleCustomNodes(argExpr); + + auto &arguments = callExpr->Arguments(); + arguments.insert(arguments.begin(), argExpr); + } + + Recheck(ctx->phaseManager, checker->VarBinder()->AsETSBinder(), checker, transformedCallee); +} +bool TransformToStaticInvoke::PerformForModule(public_lib::Context *ctx, parser::Program *program) +{ + ir::NodeTransformer handleTransformToStaticInvoke = [this, ctx](ir::AstNode *const ast) { + TransformCallable(ast, ctx); + return ast; + }; + + program->Ast()->TransformChildrenRecursively(handleTransformToStaticInvoke, Name()); + + return true; +} + +} // namespace ark::es2panda::compiler \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/transformToStaticInvoke.h b/ets2panda/compiler/lowering/ets/transformToStaticInvoke.h new file mode 100644 index 0000000000000000000000000000000000000000..658b9a9cbe3d212f869b397ba86acf3aee6a3486 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/transformToStaticInvoke.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 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. + */ + +#ifndef ES2PANDA_COMPILER_LOWERING_TRANSFORM_TO_STATIC_INVOKE_H +#define ES2PANDA_COMPILER_LOWERING_TRANSFORM_TO_STATIC_INVOKE_H + +#include "compiler/lowering/phase.h" + +namespace ark::es2panda::compiler { + +class TransformToStaticInvoke : public PhaseForBodies { +public: + std::string_view Name() const override; + bool PerformForModule(public_lib::Context *ctx, parser::Program *program) override; + +private: + bool IsNodeApplicable(ir::AstNode *const ast); + void TransformCallable(ir::AstNode *const ast, public_lib::Context *ctx); + std::pair FindStaticCallable( + const checker::ETSObjectType *type) const; +}; + +} // namespace ark::es2panda::compiler + +#endif // ES2PANDA_COMPILER_LOWERING_TRANSFORM_TO_STATIC_INVOKE_H \ No newline at end of file diff --git a/ets2panda/compiler/lowering/phase.cpp b/ets2panda/compiler/lowering/phase.cpp index c321ed36b8b664e093983492e3613f3e40d3ed51..fb10206409cdae2a438c7b5c0950f4f7dd033d06 100644 --- a/ets2panda/compiler/lowering/phase.cpp +++ b/ets2panda/compiler/lowering/phase.cpp @@ -67,6 +67,7 @@ #include "compiler/lowering/ets/unboxLowering.h" #include "compiler/lowering/ets/unionLowering.h" #include "compiler/lowering/ets/typeFromLowering.h" +#include "compiler/lowering/ets/transformToStaticInvoke.h" #include "compiler/lowering/plugin_phase.h" #include "compiler/lowering/resolveIdentifiers.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" @@ -124,6 +125,7 @@ std::vector GetETSPhaseList() // pluginsAfterCheck has to go right after checkerPhase, nothing should be between them new PluginPhase {g_pluginsAfterCheck, ES2PANDA_STATE_CHECKED, &util::Plugin::AfterCheck}, // new ConvertPrimitiveCastMethodCall, + new TransformToStaticInvoke, // Can be only applied after checking phase and before lambda lowering new AnnotationCopyPostLowering, new AsyncMethodLowering, new DeclareOverloadLowering,