1 /**
2 * Copyright (c) 2024 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_BASE_INL_H
16 #define PANDA_RUNTIME_VTABLE_BUILDER_BASE_INL_H
17
18 #include "runtime/include/vtable_builder_base.h"
19
20 namespace ark {
21
22 void OnVTableConflict(ClassLinkerErrorHandler *errHandler, ClassLinker::Error error, std::string_view msg,
23 MethodInfo const *info1, MethodInfo const *info2);
24
25 void OnVTableConflict(ClassLinkerErrorHandler *errHandler, ClassLinker::Error error, std::string_view msg,
26 Method const *info1, Method const *info2);
27
AddEntry(const MethodInfo * info)28 inline void VTableInfo::AddEntry(const MethodInfo *info)
29 {
30 [[maybe_unused]] auto res = vmethods_.insert({info, MethodEntry(vmethods_.size())});
31 ASSERT(res.second);
32 }
33
AddCopiedEntry(const MethodInfo * info)34 inline VTableInfo::CopiedMethodEntry &VTableInfo::AddCopiedEntry(const MethodInfo *info)
35 {
36 auto res = copiedMethods_.insert({info, CopiedMethodEntry(copiedMethods_.size())});
37 ASSERT(res.second);
38 return res.first->second;
39 }
40
UpdateCopiedEntry(const MethodInfo * orig,const MethodInfo * repl)41 inline VTableInfo::CopiedMethodEntry &VTableInfo::UpdateCopiedEntry(const MethodInfo *orig, const MethodInfo *repl)
42 {
43 auto it = copiedMethods_.find(orig);
44 ASSERT(it != copiedMethods_.end());
45 CopiedMethodEntry entry = it->second;
46 copiedMethods_.erase(it);
47 return copiedMethods_.emplace_hint(it, repl, entry)->second;
48 }
49
50 template <bool VISIT_SUPERITABLE>
BuildForInterface(panda_file::ClassDataAccessor * cda)51 void VTableBuilderBase<VISIT_SUPERITABLE>::BuildForInterface(panda_file::ClassDataAccessor *cda)
52 {
53 ASSERT(cda->IsInterface());
54 cda->EnumerateMethods([this](panda_file::MethodDataAccessor &mda) {
55 if (mda.IsStatic()) {
56 return;
57 }
58
59 if (!mda.IsAbstract()) {
60 hasDefaultMethods_ = true;
61 }
62
63 ++numVmethods_;
64 });
65 }
66
67 template <bool VISIT_SUPERITABLE>
BuildForInterface(Span<Method> methods)68 void VTableBuilderBase<VISIT_SUPERITABLE>::BuildForInterface(Span<Method> methods)
69 {
70 for (const auto &method : methods) {
71 if (method.IsStatic()) {
72 continue;
73 }
74
75 if (!method.IsAbstract()) {
76 hasDefaultMethods_ = true;
77 }
78
79 ++numVmethods_;
80 }
81 }
82
83 template <bool VISIT_SUPERITABLE>
AddBaseMethods(Class * baseClass)84 void VTableBuilderBase<VISIT_SUPERITABLE>::AddBaseMethods(Class *baseClass)
85 {
86 auto baseMethods = allocator_.New<ArenaForwardList<MethodInfo>>(allocator_.Adapter());
87
88 if (baseClass != nullptr) {
89 for (auto const &method : baseClass->GetVTable()) {
90 vtable_.AddEntry(&baseMethods->emplace_front(method, 0, true));
91 }
92 }
93 }
94
95 template <bool VISIT_SUPERITABLE>
AddClassMethods(panda_file::ClassDataAccessor * cda,ClassLinkerContext * ctx)96 bool VTableBuilderBase<VISIT_SUPERITABLE>::AddClassMethods(panda_file::ClassDataAccessor *cda, ClassLinkerContext *ctx)
97 {
98 auto classMethods = allocator_.New<ArenaForwardList<MethodInfo>>(allocator_.Adapter());
99
100 cda->EnumerateMethods([this, ctx, &classMethods](panda_file::MethodDataAccessor &mda) {
101 if (!mda.IsStatic()) {
102 classMethods->emplace_front(mda, numVmethods_++, ctx);
103 }
104 });
105
106 for (auto &info : *classMethods) {
107 if (!ProcessClassMethod(&info)) {
108 return false;
109 }
110 }
111 return true;
112 }
113
114 template <bool VISIT_SUPERITABLE>
AddClassMethods(Span<Method> methods)115 bool VTableBuilderBase<VISIT_SUPERITABLE>::AddClassMethods(Span<Method> methods)
116 {
117 auto classMethods = allocator_.New<ArenaForwardList<MethodInfo>>(allocator_.Adapter());
118
119 for (auto &method : methods) {
120 if (!method.IsStatic()) {
121 classMethods->emplace_front(&method, numVmethods_++);
122 }
123 }
124
125 for (auto const &info : *classMethods) {
126 if (!ProcessClassMethod(&info)) {
127 return false;
128 }
129 }
130 return true;
131 }
132
133 template <bool VISIT_SUPERITABLE>
AddDefaultInterfaceMethods(ITable itable,size_t superItableSize)134 bool VTableBuilderBase<VISIT_SUPERITABLE>::AddDefaultInterfaceMethods(ITable itable, size_t superItableSize)
135 {
136 auto defaultMethods = allocator_.New<ArenaForwardList<MethodInfo>>(allocator_.Adapter());
137
138 // NOTE(vpukhov): avoid traversing the whole itable and handle conflicting super vtable methods in a separate pass
139 size_t const traverseUpTo = VISIT_SUPERITABLE ? 0 : superItableSize;
140
141 for (size_t i = itable.Size(); i != traverseUpTo;) {
142 i--;
143 auto iface = itable[i].GetInterface();
144 if (!iface->HasDefaultMethods()) {
145 continue;
146 }
147
148 auto methods = iface->GetVirtualMethods();
149 for (auto &method : methods) {
150 if (method.IsAbstract()) {
151 continue;
152 }
153 MethodInfo *info = &defaultMethods->emplace_front(&method);
154 if (!ProcessDefaultMethod(itable, i, info)) {
155 return false;
156 }
157 }
158 }
159
160 ASSERT(orderedCopiedMethods_.empty());
161 orderedCopiedMethods_.resize(vtable_.CopiedMethods().size());
162
163 for (auto const &[info, entry] : vtable_.CopiedMethods()) {
164 CopiedMethod copied(info->GetMethod());
165 copied.SetStatus(entry.GetStatus());
166 orderedCopiedMethods_[entry.GetIndex()] = copied;
167 }
168 return true;
169 }
170
171 template <bool VISIT_SUPERITABLE>
Build(panda_file::ClassDataAccessor * cda,Class * baseClass,ITable itable,ClassLinkerContext * ctx)172 bool VTableBuilderBase<VISIT_SUPERITABLE>::Build(panda_file::ClassDataAccessor *cda, Class *baseClass, ITable itable,
173 ClassLinkerContext *ctx)
174 {
175 if (cda->IsInterface()) {
176 BuildForInterface(cda);
177 return true;
178 }
179
180 AddBaseMethods(baseClass);
181 if (!AddClassMethods(cda, ctx)) {
182 return false;
183 }
184 if (!AddDefaultInterfaceMethods(itable, baseClass != nullptr ? baseClass->GetITable().Size() : 0)) {
185 return false;
186 }
187 return true;
188 }
189
190 template <bool VISIT_SUPERITABLE>
Build(Span<Method> methods,Class * baseClass,ITable itable,bool isInterface)191 bool VTableBuilderBase<VISIT_SUPERITABLE>::Build(Span<Method> methods, Class *baseClass, ITable itable,
192 bool isInterface)
193 {
194 if (isInterface) {
195 BuildForInterface(methods);
196 return true;
197 }
198
199 AddBaseMethods(baseClass);
200 if (!AddClassMethods(methods)) {
201 return false;
202 }
203 if (!AddDefaultInterfaceMethods(itable, baseClass != nullptr ? baseClass->GetITable().Size() : 0)) {
204 return false;
205 }
206 return true;
207 }
208
209 template <bool VISIT_SUPERITABLE>
UpdateClass(Class * klass)210 void VTableBuilderBase<VISIT_SUPERITABLE>::UpdateClass(Class *klass) const
211 {
212 if (klass->IsInterface()) {
213 if (hasDefaultMethods_) {
214 klass->SetHasDefaultMethods();
215 }
216
217 size_t idx = 0;
218 for (auto &method : klass->GetVirtualMethods()) {
219 method.SetVTableIndex(idx++);
220 }
221 }
222
223 vtable_.UpdateClass(klass);
224 }
225
226 } // namespace ark
227
228 #endif // PANDA_RUNTIME_VTABLE_BUILDER_BASE_H
229