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
22namespace ark::es2panda::compiler {
23
24std::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
77void 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
126void 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
179void 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
190void 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
230ir::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
278bool 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