• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2025 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 "genericBridgesLowering.h"
17 
18 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
19 #include "compiler/lowering/util.h"
20 
21 namespace ark::es2panda::compiler {
22 
CreateMethodDefinitionString(ir::ClassDefinition const * classDefinition,checker::Signature const * baseSignature,ir::ScriptFunction const * derivedFunction,std::vector<ir::AstNode * > & typeNodes) const23 std::string GenericBridgesPhase::CreateMethodDefinitionString(ir::ClassDefinition const *classDefinition,
24                                                               checker::Signature const *baseSignature,
25                                                               ir::ScriptFunction const *derivedFunction,
26                                                               std::vector<ir::AstNode *> &typeNodes) const noexcept
27 {
28     constexpr std::size_t SOURCE_CODE_LENGTH = 128U;
29 
30     std::string str1 {};
31     str1.reserve(2U * SOURCE_CODE_LENGTH);
32 
33     std::string str2 {};
34     str2.reserve(SOURCE_CODE_LENGTH);
35 
36     auto const &functionName = derivedFunction->Id()->Name().Mutf8();
37     str1 = functionName + '(';
38 
39     str2 += ")." + functionName + '(';
40 
41     auto const &baseParameters = baseSignature->Params();
42     auto const &derivedParameters = derivedFunction->Signature()->Params();
43     auto const parameterNumber = baseParameters.size();
44 
45     for (std::size_t i = 0U; i < parameterNumber; ++i) {
46         if (i != 0U) {
47             str1 += ", ";
48             str2 += ", ";
49         }
50 
51         auto const *const derivedParameter = derivedParameters[i];
52         auto const &parameterName = derivedParameter->Name().Utf8();
53         str1 += parameterName;
54         typeNodes.emplace_back(
55             context_->AllocNode<ir::OpaqueTypeNode>(baseParameters[i]->TsType(), context_->Allocator()));
56         str1 += ": @@T" + std::to_string(typeNodes.size());
57 
58         str2 += parameterName;
59         typeNodes.emplace_back(
60             context_->AllocNode<ir::OpaqueTypeNode>(derivedParameter->TsType(), context_->Allocator()));
61         str2 += " as @@T" + std::to_string(typeNodes.size());
62     }
63 
64     typeNodes.emplace_back(context_->AllocNode<ir::OpaqueTypeNode>(
65         const_cast<checker::Type *>(derivedFunction->Signature()->ReturnType()), context_->Allocator()));
66     str1 += "): @@T" + std::to_string(typeNodes.size()) + ' ';
67 
68     typeNodes.emplace_back(context_->AllocNode<ir::OpaqueTypeNode>(
69         const_cast<checker::Type *>(classDefinition->TsType()), context_->Allocator()));
70     str2 = "{ return (this as @@T" + std::to_string(typeNodes.size()) + str2 + "); }";
71 
72     str1 += str2;
73     return str1;
74 }
75 
AddGenericBridge(ir::ClassDefinition const * const classDefinition,ir::MethodDefinition * const methodDefinition,checker::Signature const * baseSignature,ir::ScriptFunction const * const derivedFunction) const76 void GenericBridgesPhase::AddGenericBridge(ir::ClassDefinition const *const classDefinition,
77                                            ir::MethodDefinition *const methodDefinition,
78                                            checker::Signature const *baseSignature,
79                                            ir::ScriptFunction const *const derivedFunction) const
80 {
81     auto *parser = context_->parser->AsETSParser();
82     std::vector<ir::AstNode *> typeNodes {};
83     typeNodes.reserve(2U * baseSignature->Params().size() + 2U);
84 
85     auto const sourceCode = CreateMethodDefinitionString(classDefinition, baseSignature, derivedFunction, typeNodes);
86     auto *const bridgeMethodDefinition = parser->CreateFormattedClassMethodDefinition(sourceCode, typeNodes);
87     ES2PANDA_ASSERT(bridgeMethodDefinition != nullptr);
88     auto *const bridgeMethod = bridgeMethodDefinition->AsMethodDefinition();
89     ES2PANDA_ASSERT(bridgeMethod != nullptr && methodDefinition->Id() != nullptr);
90     bridgeMethod->AddModifier(methodDefinition->Modifiers());
91     bridgeMethod->ClearModifier(ir::ModifierFlags::NATIVE | ir::ModifierFlags::ABSTRACT);
92     bridgeMethod->AddAstNodeFlags(methodDefinition->GetAstNodeFlags());
93     bridgeMethod->SetParent(const_cast<ir::ClassDefinition *>(classDefinition));
94 
95     auto *varBinder = context_->checker->VarBinder()->AsETSBinder();
96     auto *scope = NearestScope(methodDefinition);
97     auto scopeGuard = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, scope);
98     InitScopesPhaseETS::RunExternalNode(bridgeMethod, varBinder);
99 
100     varbinder::BoundContext boundCtx {varBinder->GetRecordTable(), const_cast<ir::ClassDefinition *>(classDefinition),
101                                       true};
102     varBinder->AsETSBinder()->ResolveReferencesForScopeWithContext(bridgeMethod, scope);
103 
104     auto *checker = context_->checker->AsETSChecker();
105     auto const checkerCtx =
106         checker::SavedCheckerContext(checker,
107                                      checker::CheckerStatus::IN_CLASS | checker::CheckerStatus::IGNORE_VISIBILITY |
108                                          checker::CheckerStatus::IN_BRIDGE_TEST,
109                                      classDefinition->TsType()->AsETSObjectType());
110     auto scopeCtx = checker::ScopeContext(checker, scope);
111 
112     //  Note: we need to create and set function/method type here because the general method `BuildMethodSignature(...)`
113     //  is not suitable for this case. Moreover, we have to save and restore proper type for `methodDefinition` because
114     //  call to `BuildFunctionSignature(...)` breaks it!
115     auto *methodType = methodDefinition->Id()->Variable()->TsType()->AsETSFunctionType();
116 
117     checker->BuildFunctionSignature(bridgeMethod->Function());
118     auto *const bridgeMethodType = checker->BuildMethodType(bridgeMethod->Function());
119     checker->CheckIdenticalOverloads(methodType, bridgeMethodType, bridgeMethod);
120     bridgeMethod->SetTsType(bridgeMethodType);
121     methodType->AddCallSignature(bridgeMethod->Function()->Signature());
122     methodDefinition->Id()->Variable()->SetTsType(methodType);
123 
124     bridgeMethod->Check(checker);
125 }
126 
ProcessScriptFunction(ir::ClassDefinition const * const classDefinition,ir::ScriptFunction * const baseFunction,ir::MethodDefinition * const derivedMethod,Substitutions const & substitutions) const127 void GenericBridgesPhase::ProcessScriptFunction(ir::ClassDefinition const *const classDefinition,
128                                                 ir::ScriptFunction *const baseFunction,
129                                                 ir::MethodDefinition *const derivedMethod,
130                                                 Substitutions const &substitutions) const
131 {
132     auto *const checker = context_->checker->AsETSChecker();
133     auto *const relation = checker->Relation();
134 
135     auto const overrides = [checker, relation, classDefinition](checker::Signature const *source,
136                                                                 checker::Signature const *target) -> bool {
137         checker::SavedCheckerContext const checkerCtx(
138             checker, checker->Context().Status() | checker::CheckerStatus::IN_BRIDGE_TEST,
139             classDefinition->TsType()->AsETSObjectType());
140         checker::SavedTypeRelationFlagsContext const savedFlags(relation, checker::TypeRelationFlag::BRIDGE_CHECK);
141         return relation->SignatureIsSupertypeOf(const_cast<checker::Signature *>(source),
142                                                 const_cast<checker::Signature *>(target));
143     };
144 
145     //  We are not interested in functions that either don't have type parameters at all
146     //  or have type parameters that are not modified in the derived class
147     ES2PANDA_ASSERT(baseFunction);
148     auto const *baseSignature1 = baseFunction->Signature()->Substitute(relation, substitutions.baseConstraints);
149     if (baseSignature1 == baseFunction->Signature()) {
150         return;
151     }
152 
153     auto *baseSignature2 = baseFunction->Signature()->Substitute(relation, substitutions.derivedSubstitutions);
154     if (baseSignature2 == baseFunction->Signature()) {
155         return;
156     }
157     baseSignature2 = baseSignature2->Substitute(relation, substitutions.derivedConstraints);
158 
159     ir::ScriptFunction const *derivedFunction = nullptr;
160     checker::ETSFunctionType const *methodType = derivedMethod->Id()->Variable()->TsType()->AsETSFunctionType();
161     for (auto *signature : methodType->CallSignatures()) {
162         signature = signature->Substitute(relation, substitutions.derivedConstraints);
163         if (overrides(baseSignature1, signature) || checker->HasSameAssemblySignature(baseSignature1, signature)) {
164             //  NOTE: we already have custom-implemented method with the required bridge signature.
165             //  Probably sometimes we will issue warning notification here...
166             return;
167         }
168 
169         if (overrides(signature, baseSignature1) && overrides(baseSignature1, baseSignature2)) {
170             // This derived overload already handles the base union signature.
171             return;
172         }
173 
174         if (derivedFunction == nullptr && overrides(signature, baseSignature2)) {
175             //  NOTE: we don't care the possible case of mapping several derived function to the same bridge
176             //  signature. Probably sometimes we will process it correctly or issue warning notification here...
177             derivedFunction = signature->Function();
178         }
179     }
180 
181     if (derivedFunction != nullptr && derivedFunction != baseFunction) {
182         AddGenericBridge(classDefinition, derivedMethod, baseSignature1, derivedFunction);
183     }
184 }
185 
MaybeAddGenericBridges(ir::ClassDefinition const * const classDefinition,ir::MethodDefinition * const baseMethod,ir::MethodDefinition * const derivedMethod,Substitutions const & substitutions) const186 void GenericBridgesPhase::MaybeAddGenericBridges(ir::ClassDefinition const *const classDefinition,
187                                                  ir::MethodDefinition *const baseMethod,
188                                                  ir::MethodDefinition *const derivedMethod,
189                                                  Substitutions const &substitutions) const
190 {
191     ProcessScriptFunction(classDefinition, baseMethod->Function(), derivedMethod, substitutions);
192     for (auto *const overload : baseMethod->Overloads()) {
193         ProcessScriptFunction(classDefinition, overload->Function(), derivedMethod, substitutions);
194     }
195 }
196 
CreateGenericBridges(ir::ClassDefinition const * const classDefinition,Substitutions & substitutions,ArenaVector<ir::AstNode * > const & items) const197 void GenericBridgesPhase::CreateGenericBridges(ir::ClassDefinition const *const classDefinition,
198                                                Substitutions &substitutions,
199                                                ArenaVector<ir::AstNode *> const &items) const
200 {
201     auto const &classBody = classDefinition->Body();
202 
203     //  Collect type parameters defaults/constraints in the derived class
204     auto *checker = context_->checker->AsETSChecker();
205     substitutions.derivedConstraints = checker->NewSubstitution();
206 
207     auto const *const classType = classDefinition->TsType()->AsETSObjectType();
208     auto const &typeParameters = classType->GetConstOriginalBaseType()->AsETSObjectType()->TypeArguments();
209     for (auto *const parameter : typeParameters) {
210         auto *const typeParameter = parameter->AsETSTypeParameter();
211         checker->EmplaceSubstituted(substitutions.derivedConstraints, typeParameter,
212                                     typeParameter->GetConstraintType());
213     }
214 
215     for (auto *item : items) {
216         if (item->IsMethodDefinition()) {
217             // Skip `static`, `final` and special methods...
218             auto *const method = item->AsMethodDefinition();
219             ES2PANDA_ASSERT(method->Id());
220             if (method->Kind() != ir::MethodDefinitionKind::METHOD || method->IsStatic() || method->IsFinal() ||
221                 method->Id()->Name().Utf8().find("lambda$invoke$") != std::string_view::npos) {
222                 continue;
223             }
224 
225             // Check if the derived class has any possible overrides of this method
226             auto isOverridePred = [&name = method->Id()->Name()](ir::AstNode const *node) -> bool {
227                 return node->IsMethodDefinition() && !node->IsStatic() &&
228                        node->AsMethodDefinition()->Id()->Name() == name;
229             };
230             auto it = std::find_if(classBody.cbegin(), classBody.end(), isOverridePred);
231             if (it != classBody.cend()) {
232                 MaybeAddGenericBridges(classDefinition, method, (*it)->AsMethodDefinition(), substitutions);
233             }
234         }
235     }
236 }
237 
GetSubstitutions(checker::ETSObjectType const * const objectType,ArenaVector<checker::Type * > const & typeParameters) const238 GenericBridgesPhase::Substitutions GenericBridgesPhase::GetSubstitutions(
239     checker::ETSObjectType const *const objectType, ArenaVector<checker::Type *> const &typeParameters) const noexcept
240 {
241     auto const &typeArguments = objectType->TypeArguments();
242     auto const parameterNumber = typeParameters.size();
243     ES2PANDA_ASSERT(parameterNumber == typeArguments.size());
244 
245     auto *checker = context_->checker->AsETSChecker();
246     Substitutions substitutions {};
247     substitutions.derivedSubstitutions = checker->NewSubstitution();
248     substitutions.baseConstraints = checker->NewSubstitution();
249 
250     //  We need to check if the class derived from base generic class (or implementing generic interface)
251     //  has either explicit class type substitutions or type parameters with narrowing constraints.
252     for (std::size_t i = 0U; i < parameterNumber; ++i) {
253         auto *const typeParameter = typeParameters[i]->AsETSTypeParameter();
254         checker::Type *const typeArgument = typeArguments[i];
255 
256         //  Collect type parameters defaults/constraints in the base class
257         //  and type argument substitutions in the derived class
258         checker->EmplaceSubstituted(substitutions.derivedSubstitutions, typeParameter, typeArgument);
259         if (auto *const defaultType = typeParameter->GetDefaultType(); defaultType != nullptr) {
260             checker->EmplaceSubstituted(substitutions.baseConstraints, typeParameter, defaultType);
261         } else {
262             checker->EmplaceSubstituted(substitutions.baseConstraints, typeParameter,
263                                         typeParameter->GetConstraintType());
264         }
265     }
266 
267     return substitutions;
268 }
269 
ProcessInterfaces(ir::ClassDefinition * const classDefinition,ArenaVector<checker::ETSObjectType * > const & interfaces) const270 void GenericBridgesPhase::ProcessInterfaces(ir::ClassDefinition *const classDefinition,
271                                             ArenaVector<checker::ETSObjectType *> const &interfaces) const
272 {
273     for (auto const *interfaceType : interfaces) {
274         if (auto const &typeParameters = interfaceType->GetConstOriginalBaseType()->AsETSObjectType()->TypeArguments();
275             !typeParameters.empty()) {
276             if (Substitutions substitutions = GetSubstitutions(interfaceType, typeParameters);
277                 // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
278                 (substitutions.derivedSubstitutions != nullptr) && !substitutions.derivedSubstitutions->empty()) {
279                 ES2PANDA_ASSERT(interfaceType->GetDeclNode()->IsTSInterfaceDeclaration());
280                 auto const &interfaceBody = interfaceType->GetDeclNode()->AsTSInterfaceDeclaration()->Body()->Body();
281                 CreateGenericBridges(classDefinition, substitutions, interfaceBody);
282             }
283         }
284 
285         ProcessInterfaces(classDefinition, interfaceType->Interfaces());
286     }
287 }
288 
ProcessClassDefinition(ir::ClassDefinition * const classDefinition) const289 ir::ClassDefinition *GenericBridgesPhase::ProcessClassDefinition(ir::ClassDefinition *const classDefinition) const
290 {
291     //  Check class interfaces.
292     ProcessInterfaces(classDefinition, classDefinition->TsType()->AsETSObjectType()->Interfaces());
293 
294     //  Check if the base class is a generic class.
295     if (classDefinition->Super() == nullptr || classDefinition->Super()->TsType() == nullptr ||
296         !classDefinition->Super()->TsType()->IsETSObjectType()) {
297         return classDefinition;
298     }
299 
300     auto const *const superType = classDefinition->Super()->TsType()->AsETSObjectType();
301     auto const &typeParameters = superType->GetConstOriginalBaseType()->AsETSObjectType()->TypeArguments();
302     if (typeParameters.empty()) {
303         return classDefinition;
304     }
305 
306     //  Check if the class derived from base generic class has either explicit class type substitutions
307     //  or type parameters with narrowing constraints.
308     if (Substitutions substitutions = GetSubstitutions(superType, typeParameters);
309         (substitutions.derivedSubstitutions != nullptr) &&
310         // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
311         !substitutions.derivedSubstitutions->empty()) {
312         // If it has, then probably the generic bridges should be created.
313         auto const &superClassBody =
314             classDefinition->Super()->TsType()->AsETSObjectType()->GetDeclNode()->AsClassDefinition()->Body();
315         CreateGenericBridges(classDefinition, substitutions, superClassBody);
316     }
317 
318     return classDefinition;
319 }
320 
PerformForModule(public_lib::Context * ctx,parser::Program * program)321 bool GenericBridgesPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program)
322 {
323     context_ = ctx;
324 
325     program->Ast()->TransformChildrenRecursively(
326         // CC-OFFNXT(G.FMT.14-CPP) project code style
327         [this](ir::AstNode *ast) -> ir::AstNode * {
328             if (ast->IsClassDefinition()) {
329                 return ProcessClassDefinition(ast->AsClassDefinition());
330             }
331             return ast;
332         },
333         Name());
334 
335     return true;
336 }
337 
338 }  // namespace ark::es2panda::compiler
339