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