diff --git a/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp b/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp index 29df4c1d5624a787c5122eb0820404314f806632..bc15ac423aa7c9cb1596bfa6ff5a5569d6f6155d 100644 --- a/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp +++ b/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp @@ -17,60 +17,160 @@ #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "compiler/lowering/util.h" +#include namespace ark::es2panda::compiler { -std::string GenericBridgesPhase::CreateMethodDefinitionString(ir::ClassDefinition const *classDefinition, - checker::Signature const *baseSignature, - ir::ScriptFunction const *derivedFunction, - std::vector &typeNodes) const noexcept +std::string GenericBridgesPhase::BuildMethodSignature(ir::ScriptFunction const *derivedFunction, + checker::Signature const *baseSignature, + std::vector &typeNodes) const noexcept { - constexpr std::size_t SOURCE_CODE_LENGTH = 128U; - - std::string str1 {}; - str1.reserve(2U * SOURCE_CODE_LENGTH); - - std::string str2 {}; - str2.reserve(SOURCE_CODE_LENGTH); - + std::ostringstream signature {}; auto const &functionName = derivedFunction->Id()->Name().Mutf8(); - str1 = functionName + '('; - str2 += ")." + functionName + '('; + // Add method type prefix (get/set for accessors) + if (derivedFunction->IsGetter()) { + signature << "get "; + } else if (derivedFunction->IsSetter()) { + signature << "set "; + } + + signature << functionName << '('; + // Add parameters auto const &baseParameters = baseSignature->Params(); auto const &derivedParameters = derivedFunction->Signature()->Params(); auto const parameterNumber = baseParameters.size(); for (std::size_t i = 0U; i < parameterNumber; ++i) { if (i != 0U) { - str1 += ", "; - str2 += ", "; + signature << ", "; } - auto const *const derivedParameter = derivedParameters[i]; - auto const ¶meterName = derivedParameter->Name().Utf8(); - str1 += parameterName; + signature << GetAdjustedParameterName(derivedFunction, derivedParameters[i]->Name().Utf8()); + + // Add base parameter type typeNodes.emplace_back( context_->AllocNode(baseParameters[i]->TsType(), context_->Allocator())); - str1 += ": @@T" + std::to_string(typeNodes.size()); + signature << ": @@T" << typeNodes.size(); + } - str2 += parameterName; - typeNodes.emplace_back( - context_->AllocNode(derivedParameter->TsType(), context_->Allocator())); - str2 += " as @@T" + std::to_string(typeNodes.size()); + signature << ")"; + + // Add return type (not for setters) + if (!derivedFunction->IsSetter()) { + typeNodes.emplace_back(context_->AllocNode( + const_cast(baseSignature->ReturnType()), context_->Allocator())); + signature << ": @@T" << typeNodes.size(); } - typeNodes.emplace_back(context_->AllocNode( - const_cast(baseSignature->ReturnType()), context_->Allocator())); - str1 += "): @@T" + std::to_string(typeNodes.size()) + ' '; + signature << " "; + return signature.str(); +} +std::string GenericBridgesPhase::BuildMethodBody(ir::ClassDefinition const *classDefinition, + ir::ScriptFunction const *derivedFunction, + std::vector &typeNodes) const noexcept +{ + std::ostringstream body {}; + auto const &functionName = derivedFunction->Id()->Name().Mutf8(); + + // Add class type for casting typeNodes.emplace_back(context_->AllocNode( const_cast(classDefinition->TsType()), context_->Allocator())); - str2 = "{ return (this as @@T" + std::to_string(typeNodes.size()) + str2 + "); }"; + auto const classTypeIndex = typeNodes.size(); + + if (derivedFunction->IsGetter()) { + body << "{ return (this as @@T" << classTypeIndex << ")." << functionName << "; }"; + } else if (derivedFunction->IsSetter()) { + body << "{ (this as @@T" << classTypeIndex << ")." << functionName + << BuildSetterAssignment(derivedFunction, typeNodes) << "; }"; + } else { + body << "{ return (this as @@T" << classTypeIndex << ")." << functionName + << BuildMethodCall(derivedFunction, typeNodes) << "; }"; + } + + return body.str(); +} + +std::string GenericBridgesPhase::GetAdjustedParameterName(ir::ScriptFunction const *derivedFunction, + std::string_view parameterName) const noexcept +{ + // For setters, remove property prefix if present + if (derivedFunction->IsSetter() && parameterName.rfind(compiler::Signatures::PROPERTY, 0) == 0) { + return std::string(parameterName.substr(compiler::Signatures::PROPERTY.size())); + } + return std::string(parameterName); +} + +std::string GenericBridgesPhase::BuildSetterAssignment(ir::ScriptFunction const *derivedFunction, + std::vector &typeNodes) const noexcept +{ + std::ostringstream assignment {}; + auto const &derivedParameters = derivedFunction->Signature()->Params(); + + for (std::size_t i = 0U; i < derivedParameters.size(); ++i) { + if (i != 0U) { + assignment << ", "; + } + + assignment << " = "; + auto const ¶meterName = derivedParameters[i]->Name().Utf8(); + auto const adjustedParameterName = GetAdjustedParameterName(derivedFunction, parameterName); + assignment << adjustedParameterName; + + // Add derived parameter type for casting + typeNodes.emplace_back( + context_->AllocNode(derivedParameters[i]->TsType(), context_->Allocator())); + assignment << " as @@T" << typeNodes.size(); + } - str1 += str2; - return str1; + return assignment.str(); +} + +std::string GenericBridgesPhase::BuildMethodCall(ir::ScriptFunction const *derivedFunction, + std::vector &typeNodes) const noexcept +{ + std::ostringstream call {}; + call << "("; + auto const &derivedParameters = derivedFunction->Signature()->Params(); + + for (std::size_t i = 0U; i < derivedParameters.size(); ++i) { + if (i != 0U) { + call << ", "; + } + + auto const ¶meterName = derivedParameters[i]->Name().Utf8(); + auto const adjustedParameterName = GetAdjustedParameterName(derivedFunction, parameterName); + call << adjustedParameterName; + + // Add derived parameter type for casting + typeNodes.emplace_back( + context_->AllocNode(derivedParameters[i]->TsType(), context_->Allocator())); + call << " as @@T" << typeNodes.size(); + } + + call << ")"; + return call.str(); +} + +std::string GenericBridgesPhase::CreateMethodDefinitionString(ir::ClassDefinition const *classDefinition, + checker::Signature const *baseSignature, + ir::ScriptFunction const *derivedFunction, + std::vector &typeNodes) const noexcept +{ + constexpr std::size_t SOURCE_CODE_LENGTH = 128U; + std::string result {}; + result.reserve(2U * SOURCE_CODE_LENGTH); + + // Build method signature (name, parameters, return type) + std::string signature = BuildMethodSignature(derivedFunction, baseSignature, typeNodes); + + // Build method body (implementation) + std::string body = BuildMethodBody(classDefinition, derivedFunction, typeNodes); + + result = signature + body; + return result; } void GenericBridgesPhase::AddGenericBridge(ir::ClassDefinition const *const classDefinition, @@ -89,10 +189,16 @@ void GenericBridgesPhase::AddGenericBridge(ir::ClassDefinition const *const clas ES2PANDA_ASSERT(bridgeMethodDefinition != nullptr); auto *const bridgeMethod = bridgeMethodDefinition->AsMethodDefinition(); ES2PANDA_ASSERT(bridgeMethod != nullptr && methodDefinition->Id() != nullptr); - bridgeMethod->AddModifier(methodDefinition->Modifiers()); - bridgeMethod->ClearModifier(ir::ModifierFlags::NATIVE | ir::ModifierFlags::ABSTRACT); - bridgeMethod->AddAstNodeFlags(methodDefinition->GetAstNodeFlags()); + + auto configureModifiersAndFlags = [](auto *target, auto *source) { + target->AddModifier(source->Modifiers()); + target->ClearModifier(ir::ModifierFlags::NATIVE | ir::ModifierFlags::ABSTRACT); + target->AddAstNodeFlags(source->GetAstNodeFlags()); + }; + + configureModifiersAndFlags(bridgeMethod, methodDefinition); bridgeMethod->SetParent(const_cast(classDefinition)); + configureModifiersAndFlags(bridgeMethod->Function(), methodDefinition->Function()); auto *varBinder = context_->GetChecker()->VarBinder()->AsETSBinder(); auto *scope = NearestScope(methodDefinition); @@ -210,7 +316,10 @@ static ir::MethodDefinition *FindBridgeCandidate(ir::ClassDefinition const *cons auto const &classBody = classDefinition->Body(); // Skip `static`, `final` and special methods... - if (baseMethod->Kind() != ir::MethodDefinitionKind::METHOD || baseMethod->IsStatic() || baseMethod->IsFinal() || + bool const isSpecialMethodKind = + (baseMethod->Kind() != ir::MethodDefinitionKind::METHOD && + baseMethod->Kind() != ir::MethodDefinitionKind::GET && baseMethod->Kind() != ir::MethodDefinitionKind::SET); + if (isSpecialMethodKind || baseMethod->IsStatic() || baseMethod->IsFinal() || baseMethod->Id()->Name().Utf8().find("lambda_invoke-") != std::string_view::npos) { return nullptr; } @@ -337,11 +446,9 @@ ir::ClassDefinition *GenericBridgesPhase::ProcessClassDefinition(ir::ClassDefini // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) !substitutions.derivedSubstitutions.empty()) { // If it has, then probably the generic bridges should be created. - auto const &superClassBody = - classDefinition->Super()->TsType()->AsETSObjectType()->GetDeclNode()->AsClassDefinition()->Body(); + auto const &superClassBody = superType->GetDeclNode()->AsClassDefinition()->Body(); CreateGenericBridges(classDefinition, substitutions, superClassBody); - ArenaVector interfaces = - classDefinition->Super()->TsType()->AsETSObjectType()->Interfaces(); + ArenaVector interfaces = superType->Interfaces(); if (!interfaces.empty()) { for (checker::ETSObjectType *interface : interfaces) { auto &interfaceBody = interface->GetDeclNode()->AsTSInterfaceDeclaration()->Body()->Body(); diff --git a/ets2panda/compiler/lowering/ets/genericBridgesLowering.h b/ets2panda/compiler/lowering/ets/genericBridgesLowering.h index a15c7ed01de3946dd06977c8e2dfb6307cce0338..2a6feba7c6685fbff7c2b657679e68dee64590bc 100644 --- a/ets2panda/compiler/lowering/ets/genericBridgesLowering.h +++ b/ets2panda/compiler/lowering/ets/genericBridgesLowering.h @@ -61,6 +61,21 @@ private: ir::ScriptFunction const *derivedFunction, std::vector &typeNodes) const noexcept; + std::string BuildMethodSignature(ir::ScriptFunction const *derivedFunction, checker::Signature const *baseSignature, + std::vector &typeNodes) const noexcept; + + std::string BuildMethodBody(ir::ClassDefinition const *classDefinition, ir::ScriptFunction const *derivedFunction, + std::vector &typeNodes) const noexcept; + + std::string GetAdjustedParameterName(ir::ScriptFunction const *derivedFunction, + std::string_view parameterName) const noexcept; + + std::string BuildSetterAssignment(ir::ScriptFunction const *derivedFunction, + std::vector &typeNodes) const noexcept; + + std::string BuildMethodCall(ir::ScriptFunction const *derivedFunction, + std::vector &typeNodes) const noexcept; + public_lib::Context *context_ = nullptr; }; } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/lowering/phase.cpp b/ets2panda/compiler/lowering/phase.cpp index 4dd62cd22f43e0431bce0cac6462659346fa8501..0fbbaf87747bace6350face67e3a5079e3ff1012 100644 --- a/ets2panda/compiler/lowering/phase.cpp +++ b/ets2panda/compiler/lowering/phase.cpp @@ -153,12 +153,12 @@ std::vector GetETSPhaseList() new UnionLowering, new ExpandBracketsPhase, new PartialExportClassGen, - new InterfaceObjectLiteralLowering, // must be put after all classes are generated. - new ObjectLiteralLowering, new StringConstructorLowering, new StringComparisonLowering, new OptionalArgumentsLowering, // #22952 could be moved to earlier phase new GenericBridgesPhase, + new InterfaceObjectLiteralLowering, + new ObjectLiteralLowering, new TypeFromLowering, new GradualTypeNarrowing, new PrimitiveConversionPhase, diff --git a/ets2panda/test/ast/compiler/ets/generic_interface_implementation.ets b/ets2panda/test/ast/compiler/ets/generic_interface_implementation.ets new file mode 100644 index 0000000000000000000000000000000000000000..03a58a2e434882a4d868d0db83c120577041026a --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/generic_interface_implementation.ets @@ -0,0 +1,24 @@ +/* + * 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. + */ + +declare interface Father { + foo(instance: T): void; +} + +class B { } + +declare class A implements Father { + foo(instance: B): void; +} \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/generic_bridges_for_accessors.ets b/ets2panda/test/runtime/ets/generic_bridges_for_accessors.ets new file mode 100644 index 0000000000000000000000000000000000000000..d30e6a19cca408f6821a86e0095c5d17f3458c2d --- /dev/null +++ b/ets2panda/test/runtime/ets/generic_bridges_for_accessors.ets @@ -0,0 +1,33 @@ +/* + * 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. + */ + +interface I { + start: T +} + +class C implements I { + start: number = 10 +} + +function foo(x: I) { + arktest.assertEQ(x.start, 10) + x.start = 1234 + arktest.assertEQ(x.start, 1234) +} + +function main() { + let x = new C() + foo(x) +} \ No newline at end of file