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 ¶meterName = 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