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