1 /** 2 * Copyright (c) 2021-2022 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 #ifndef PANDA_RUNTIME_VTABLE_BUILDER_H 16 #define PANDA_RUNTIME_VTABLE_BUILDER_H 17 18 #include "libpandabase/macros.h" 19 #include "libpandabase/utils/hash.h" 20 #include "libpandabase/utils/utf.h" 21 #include "libpandafile/class_data_accessor-inl.h" 22 #include "libpandafile/file-inl.h" 23 #include "libpandafile/file_items.h" 24 #include "libpandafile/proto_data_accessor-inl.h" 25 #include "runtime/include/class-inl.h" 26 #include "runtime/include/mem/panda_containers.h" 27 #include "runtime/include/mem/panda_smart_pointers.h" 28 #include "runtime/include/method.h" 29 30 namespace panda { 31 32 bool IsMaxSpecificMethod(const Class *iface, const Method &method, size_t startindex, const ITable &itable); 33 34 class ClassLinker; 35 class ClassLinkerContext; 36 37 class MethodInfo { 38 public: 39 static constexpr size_t INVALID_METHOD_IDX = std::numeric_limits<size_t>::max(); MethodInfo(const panda_file::MethodDataAccessor & mda,size_t index,ClassLinkerContext * ctx)40 MethodInfo(const panda_file::MethodDataAccessor &mda, size_t index, ClassLinkerContext *ctx) 41 : pf_(&mda.GetPandaFile()), 42 classId_(mda.GetClassId()), 43 accessFlags_(mda.GetAccessFlags()), 44 name_(pf_->GetStringData(mda.GetNameId())), 45 proto_(*pf_, mda.GetProtoId()), 46 ctx_(ctx), 47 index_(index), 48 sourceLang_(GetSourceLang()), 49 returnType_(mda.GetReturnType()) 50 { 51 } 52 53 explicit MethodInfo(Method *method, size_t index = 0, bool isBase = false, bool needsCopy = false) method_(method)54 : method_(method), 55 pf_(method->GetPandaFile()), 56 classId_(method->GetClass()->GetFileId()), 57 accessFlags_(method->GetAccessFlags()), 58 name_(method->GetName()), 59 proto_(method->GetProtoId()), 60 ctx_(method->GetClass()->GetLoadContext()), 61 index_(index), 62 needsCopy_(needsCopy), 63 isBase_(isBase), 64 sourceLang_(GetSourceLang()), 65 returnType_(method->GetReturnType()) 66 { 67 } 68 IsEqualByNameAndSignature(const MethodInfo & other)69 bool IsEqualByNameAndSignature(const MethodInfo &other) const 70 { 71 return GetName() == other.GetName() && GetProtoId() == other.GetProtoId(); 72 } 73 GetSourceLang()74 panda_file::SourceLang GetSourceLang() const 75 { 76 return sourceLang_; 77 } 78 GetReturnType()79 panda_file::Type GetReturnType() const 80 { 81 return returnType_; 82 } 83 GetName()84 const panda_file::File::StringData &GetName() const 85 { 86 return name_; 87 } 88 GetClassName()89 const uint8_t *GetClassName() const 90 { 91 return method_ != nullptr ? method_->GetClass()->GetDescriptor() : pf_->GetStringData(classId_).data; 92 } 93 GetProtoId()94 const Method::ProtoId &GetProtoId() const 95 { 96 return proto_; 97 } 98 GetMethod()99 Method *GetMethod() const 100 { 101 return method_; 102 } 103 GetIndex()104 size_t GetIndex() const 105 { 106 return index_; 107 } 108 IsAbstract()109 bool IsAbstract() const 110 { 111 return (accessFlags_ & ACC_ABSTRACT) != 0; 112 } 113 IsPublic()114 bool IsPublic() const 115 { 116 return (accessFlags_ & ACC_PUBLIC) != 0; 117 } 118 IsProtected()119 bool IsProtected() const 120 { 121 return (accessFlags_ & ACC_PROTECTED) != 0; 122 } 123 IsPrivate()124 bool IsPrivate() const 125 { 126 return (accessFlags_ & ACC_PRIVATE) != 0; 127 } 128 IsInterfaceMethod()129 bool IsInterfaceMethod() const 130 { 131 if (method_ != nullptr) { 132 return method_->GetClass()->IsInterface(); 133 } 134 135 panda_file::ClassDataAccessor cda(*pf_, classId_); 136 return cda.IsInterface(); 137 } 138 NeedsCopy()139 bool NeedsCopy() const 140 { 141 return needsCopy_; 142 } 143 IsBase()144 bool IsBase() const 145 { 146 return isBase_; 147 } 148 GetLoadContext()149 ClassLinkerContext *GetLoadContext() const 150 { 151 return ctx_; 152 } 153 IsCopied()154 bool IsCopied() const 155 { 156 if (method_ == nullptr) { 157 return false; 158 } 159 160 return method_->IsDefaultInterfaceMethod(); 161 } 162 163 ~MethodInfo() = default; 164 165 DEFAULT_COPY_CTOR(MethodInfo); 166 NO_COPY_OPERATOR(MethodInfo); 167 NO_MOVE_SEMANTIC(MethodInfo); 168 169 private: 170 Method *method_ {nullptr}; 171 const panda_file::File *pf_; 172 panda_file::File::EntityId classId_; 173 uint32_t accessFlags_; 174 panda_file::File::StringData name_; 175 Method::ProtoId proto_; 176 ClassLinkerContext *ctx_ {nullptr}; 177 size_t index_ {0}; 178 bool needsCopy_ {false}; 179 bool isBase_ {false}; 180 panda_file::SourceLang sourceLang_ {panda_file::SourceLang::INVALID}; 181 panda_file::Type returnType_; 182 }; 183 184 template <class SearchPred, class OverridePred> 185 class VTable { 186 public: AddBaseMethod(const MethodInfo & info)187 void AddBaseMethod(const MethodInfo &info) 188 { 189 methods_.insert({info, methods_.size()}); 190 } 191 AddMethod(const MethodInfo & info)192 std::pair<bool, size_t> AddMethod(const MethodInfo &info) 193 { 194 auto [beg_it, end_it] = methods_.equal_range(info); 195 if (beg_it == methods_.cend()) { 196 methods_.insert({info, methods_.size()}); 197 return std::pair<bool, size_t>(true, MethodInfo::INVALID_METHOD_IDX); 198 } 199 200 for (auto it = beg_it; it != end_it; ++it) { 201 std::pair<bool, size_t> res = OverridePred()(it->first, info); 202 if (res.first) { 203 if (res.second == MethodInfo::INVALID_METHOD_IDX) { 204 size_t idx = it->second; 205 methods_.erase(it); 206 methods_.insert({info, idx}); 207 } 208 return res; 209 } 210 } 211 212 return std::pair<bool, size_t>(false, MethodInfo::INVALID_METHOD_IDX); 213 } 214 UpdateClass(Class * klass)215 void UpdateClass(Class *klass) const 216 { 217 auto vtable = klass->GetVTable(); 218 219 for (const auto &[method_info, idx] : methods_) { 220 Method *method = method_info.GetMethod(); 221 if (method == nullptr) { 222 method = &klass->GetVirtualMethods()[method_info.GetIndex()]; 223 method->SetVTableIndex(idx); 224 } else if (method_info.NeedsCopy()) { 225 method = &klass->GetCopiedMethods()[method_info.GetIndex()]; 226 method->SetVTableIndex(idx); 227 } else if (!method_info.IsBase()) { 228 method->SetVTableIndex(idx); 229 } 230 231 vtable[idx] = method; 232 } 233 234 DumpVTable(klass); 235 } 236 DumpVTable(Class * klass)237 static void DumpVTable([[maybe_unused]] Class *klass) 238 { 239 #ifndef NDEBUG 240 LOG(DEBUG, CLASS_LINKER) << "vtable of class " << klass->GetName() << ":"; 241 auto vtable = klass->GetVTable(); 242 size_t idx = 0; 243 for (auto *method : vtable) { 244 LOG(DEBUG, CLASS_LINKER) << "[" << idx++ << "] " << method->GetFullName(); 245 } 246 #endif // NDEBUG 247 } 248 Size()249 size_t Size() const 250 { 251 return methods_.size(); 252 } 253 254 private: 255 struct HashByName { operatorHashByName256 uint32_t operator()(const MethodInfo &methodInfo) const 257 { 258 return GetHash32String(methodInfo.GetName().data); 259 } 260 }; 261 262 PandaUnorderedMultiMap<MethodInfo, size_t, HashByName, SearchPred> methods_; 263 }; 264 265 struct CopiedMethod { CopiedMethodCopiedMethod266 CopiedMethod(Method *cpMethod, bool cpDefaultConflict, bool cpDefaultAbstract) 267 : method(cpMethod), defaultConflict(cpDefaultConflict), defaultAbstract(cpDefaultAbstract) 268 { 269 } 270 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) 271 Method *method; 272 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) 273 bool defaultConflict; // flag indicates whether current methed is judged icce 274 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) 275 bool defaultAbstract; // flag indicates whether current methed is judged ame 276 }; 277 278 class VTableBuilder { 279 public: 280 VTableBuilder() = default; 281 282 virtual void Build(panda_file::ClassDataAccessor *cda, Class *baseClass, ITable itable, 283 ClassLinkerContext *ctx) = 0; 284 285 virtual void Build(Span<Method> methods, Class *baseClass, ITable itable, bool isInterface) = 0; 286 287 virtual void UpdateClass(Class *klass) const = 0; 288 289 virtual size_t GetNumVirtualMethods() const = 0; 290 291 virtual size_t GetVTableSize() const = 0; 292 293 virtual const PandaVector<CopiedMethod> &GetCopiedMethods() const = 0; 294 295 virtual ~VTableBuilder() = default; 296 297 NO_COPY_SEMANTIC(VTableBuilder); 298 NO_MOVE_SEMANTIC(VTableBuilder); 299 }; 300 301 template <class SearchBySignature, class OverridePred> 302 class VTableBuilderImpl : public VTableBuilder { 303 void Build(panda_file::ClassDataAccessor *cda, Class *baseClass, ITable itable, ClassLinkerContext *ctx) override; 304 305 void Build(Span<Method> methods, Class *baseClass, ITable itable, bool isInterface) override; 306 307 void UpdateClass(Class *klass) const override; 308 GetNumVirtualMethods()309 size_t GetNumVirtualMethods() const override 310 { 311 return numVmethods_; 312 } 313 GetVTableSize()314 size_t GetVTableSize() const override 315 { 316 return vtable_.Size(); 317 } 318 GetCopiedMethods()319 const PandaVector<CopiedMethod> &GetCopiedMethods() const override 320 { 321 return copiedMethods_; 322 } 323 324 private: 325 void BuildForInterface(panda_file::ClassDataAccessor *cda); 326 327 void BuildForInterface(Span<Method> methods); 328 329 void AddBaseMethods(Class *baseClass); 330 331 void AddClassMethods(panda_file::ClassDataAccessor *cda, ClassLinkerContext *ctx); 332 333 void AddClassMethods(Span<Method> methods); 334 335 void AddDefaultInterfaceMethods(ITable itable); 336 337 VTable<SearchBySignature, OverridePred> vtable_; 338 size_t numVmethods_ {0}; 339 bool hasDefaultMethods_ {false}; 340 PandaVector<CopiedMethod> copiedMethods_; 341 }; 342 343 } // namespace panda 344 345 #endif // PANDA_RUNTIME_VTABLE_BUILDER_H 346