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 class_id_(mda.GetClassId()), 43 access_flags_(mda.GetAccessFlags()), 44 name_(pf_->GetStringData(mda.GetNameId())), 45 proto_(*pf_, mda.GetProtoId()), 46 ctx_(ctx), 47 index_(index) 48 { 49 } 50 51 explicit MethodInfo(Method *method, size_t index = 0, bool is_base = false, bool needs_copy = false) method_(method)52 : method_(method), 53 pf_(method->GetPandaFile()), 54 class_id_(method->GetClass()->GetFileId()), 55 access_flags_(method->GetAccessFlags()), 56 name_(method->GetName()), 57 proto_(method->GetProtoId()), 58 ctx_(method->GetClass()->GetLoadContext()), 59 index_(index), 60 needs_copy_(needs_copy), 61 is_base_(is_base) 62 { 63 } 64 IsEqualByNameAndSignature(const MethodInfo & other)65 bool IsEqualByNameAndSignature(const MethodInfo &other) const 66 { 67 return GetName() == other.GetName() && GetProtoId() == other.GetProtoId(); 68 } 69 GetName()70 const panda_file::File::StringData &GetName() const 71 { 72 return name_; 73 } 74 GetClassName()75 const uint8_t *GetClassName() const 76 { 77 return method_ != nullptr ? method_->GetClass()->GetDescriptor() : pf_->GetStringData(class_id_).data; 78 } 79 GetProtoId()80 const Method::ProtoId &GetProtoId() const 81 { 82 return proto_; 83 } 84 GetMethod()85 Method *GetMethod() const 86 { 87 return method_; 88 } 89 GetIndex()90 size_t GetIndex() const 91 { 92 return index_; 93 } 94 IsAbstract()95 bool IsAbstract() const 96 { 97 return (access_flags_ & ACC_ABSTRACT) != 0; 98 } 99 IsPublic()100 bool IsPublic() const 101 { 102 return (access_flags_ & ACC_PUBLIC) != 0; 103 } 104 IsProtected()105 bool IsProtected() const 106 { 107 return (access_flags_ & ACC_PROTECTED) != 0; 108 } 109 IsPrivate()110 bool IsPrivate() const 111 { 112 return (access_flags_ & ACC_PRIVATE) != 0; 113 } 114 IsInterfaceMethod()115 bool IsInterfaceMethod() const 116 { 117 if (method_ != nullptr) { 118 return method_->GetClass()->IsInterface(); 119 } 120 121 panda_file::ClassDataAccessor cda(*pf_, class_id_); 122 return cda.IsInterface(); 123 } 124 NeedsCopy()125 bool NeedsCopy() const 126 { 127 return needs_copy_; 128 } 129 IsBase()130 bool IsBase() const 131 { 132 return is_base_; 133 } 134 GetLoadContext()135 ClassLinkerContext *GetLoadContext() const 136 { 137 return ctx_; 138 } 139 IsCopied()140 bool IsCopied() const 141 { 142 if (method_ == nullptr) { 143 return false; 144 } 145 146 return method_->IsDefaultInterfaceMethod(); 147 } 148 149 ~MethodInfo() = default; 150 151 DEFAULT_COPY_CTOR(MethodInfo) 152 NO_COPY_OPERATOR(MethodInfo); 153 NO_MOVE_SEMANTIC(MethodInfo); 154 155 private: 156 Method *method_ {nullptr}; 157 const panda_file::File *pf_; 158 panda_file::File::EntityId class_id_; 159 uint32_t access_flags_; 160 panda_file::File::StringData name_; 161 Method::ProtoId proto_; 162 ClassLinkerContext *ctx_ {nullptr}; 163 size_t index_ {0}; 164 bool needs_copy_ {false}; 165 bool is_base_ {false}; 166 }; 167 168 template <class SearchPred, class OverridePred> 169 class VTable { 170 public: AddBaseMethod(const MethodInfo & info)171 void AddBaseMethod(const MethodInfo &info) 172 { 173 methods_.insert({info, methods_.size()}); 174 } 175 AddMethod(const MethodInfo & info)176 std::pair<bool, size_t> AddMethod(const MethodInfo &info) 177 { 178 auto [beg_it, end_it] = methods_.equal_range(info); 179 if (beg_it == methods_.cend()) { 180 methods_.insert({info, methods_.size()}); 181 return std::pair<bool, size_t>(true, MethodInfo::INVALID_METHOD_IDX); 182 } 183 184 for (auto it = beg_it; it != end_it; ++it) { 185 std::pair<bool, size_t> res = OverridePred()(it->first, info); 186 if (res.first) { 187 if (res.second == MethodInfo::INVALID_METHOD_IDX) { 188 size_t idx = it->second; 189 methods_.erase(it); 190 methods_.insert({info, idx}); 191 } 192 return res; 193 } 194 } 195 196 return std::pair<bool, size_t>(false, MethodInfo::INVALID_METHOD_IDX); 197 } 198 UpdateClass(Class * klass)199 void UpdateClass(Class *klass) const 200 { 201 auto vtable = klass->GetVTable(); 202 203 for (const auto &[method_info, idx] : methods_) { 204 Method *method = method_info.GetMethod(); 205 if (method == nullptr) { 206 method = &klass->GetVirtualMethods()[method_info.GetIndex()]; 207 method->SetVTableIndex(idx); 208 } else if (method_info.NeedsCopy()) { 209 method = &klass->GetCopiedMethods()[method_info.GetIndex()]; 210 method->SetVTableIndex(idx); 211 } else if (!method_info.IsBase()) { 212 method->SetVTableIndex(idx); 213 } 214 215 vtable[idx] = method; 216 } 217 218 DumpVTable(klass); 219 } 220 DumpVTable(Class * klass)221 static void DumpVTable([[maybe_unused]] Class *klass) 222 { 223 #ifndef NDEBUG 224 LOG(DEBUG, CLASS_LINKER) << "vtable of class " << klass->GetName() << ":"; 225 auto vtable = klass->GetVTable(); 226 size_t idx = 0; 227 for (auto *method : vtable) { 228 LOG(DEBUG, CLASS_LINKER) << "[" << idx++ << "] " << method->GetFullName(); 229 } 230 #endif // NDEBUG 231 } 232 Size()233 size_t Size() const 234 { 235 return methods_.size(); 236 } 237 238 private: 239 struct HashByName { operatorHashByName240 uint32_t operator()(const MethodInfo &method_info) const 241 { 242 return GetHash32String(method_info.GetName().data); 243 } 244 }; 245 246 PandaUnorderedMultiMap<MethodInfo, size_t, HashByName, SearchPred> methods_; 247 }; 248 249 struct CopiedMethod { CopiedMethodCopiedMethod250 CopiedMethod(Method *method, bool default_conflict, bool default_abstract) 251 : method_(method), default_conflict_(default_conflict), default_abstract_(default_abstract) 252 { 253 } 254 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) 255 Method *method_; 256 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) 257 bool default_conflict_; // flag indicates whether current methed is judged icce 258 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) 259 bool default_abstract_; // flag indicates whether current methed is judged ame 260 }; 261 262 class VTableBuilder { 263 public: 264 VTableBuilder() = default; 265 266 virtual void Build(panda_file::ClassDataAccessor *cda, Class *base_class, ITable itable, 267 ClassLinkerContext *ctx) = 0; 268 269 virtual void Build(Span<Method> methods, Class *base_class, ITable itable, bool is_interface) = 0; 270 271 virtual void UpdateClass(Class *klass) const = 0; 272 273 virtual size_t GetNumVirtualMethods() const = 0; 274 275 virtual size_t GetVTableSize() const = 0; 276 277 virtual const PandaVector<CopiedMethod> &GetCopiedMethods() const = 0; 278 279 virtual ~VTableBuilder() = default; 280 281 NO_COPY_SEMANTIC(VTableBuilder); 282 NO_MOVE_SEMANTIC(VTableBuilder); 283 }; 284 285 template <class SearchBySignature, class OverridePred> 286 class VTableBuilderImpl : public VTableBuilder { 287 void Build(panda_file::ClassDataAccessor *cda, Class *base_class, ITable itable, ClassLinkerContext *ctx) override; 288 289 void Build(Span<Method> methods, Class *base_class, ITable itable, bool is_interface) override; 290 291 void UpdateClass(Class *klass) const override; 292 GetNumVirtualMethods()293 size_t GetNumVirtualMethods() const override 294 { 295 return num_vmethods_; 296 } 297 GetVTableSize()298 size_t GetVTableSize() const override 299 { 300 return vtable_.Size(); 301 } 302 GetCopiedMethods()303 const PandaVector<CopiedMethod> &GetCopiedMethods() const override 304 { 305 return copied_methods_; 306 } 307 308 private: 309 void BuildForInterface(panda_file::ClassDataAccessor *cda); 310 311 void BuildForInterface(Span<Method> methods); 312 313 void AddBaseMethods(Class *base_class); 314 315 void AddClassMethods(panda_file::ClassDataAccessor *cda, ClassLinkerContext *ctx); 316 317 void AddClassMethods(Span<Method> methods); 318 319 void AddDefaultInterfaceMethods(ITable itable); 320 321 VTable<SearchBySignature, OverridePred> vtable_; 322 size_t num_vmethods_ {0}; 323 bool has_default_methods_ {false}; 324 PandaVector<CopiedMethod> copied_methods_; 325 }; 326 327 } // namespace panda 328 329 #endif // PANDA_RUNTIME_VTABLE_BUILDER_H_ 330