• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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