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