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
16 #include "ets_itable_builder.h"
17
18 #include "runtime/include/class_linker.h"
19 #include "runtime/include/exceptions.h"
20 #include "runtime/include/mem/panda_containers.h"
21
22 namespace panda::ets {
23
FindMethodInVTable(Class * klass,Method * method)24 static Method *FindMethodInVTable(Class *klass, Method *method)
25 {
26 auto vtable = klass->GetVTable();
27 for (size_t i = vtable.size(); i > 0; i--) {
28 auto idx = i - 1;
29 if (vtable[idx]->GetName() == method->GetName() && vtable[idx]->GetProtoId() == method->GetProtoId()) {
30 return vtable[idx];
31 }
32 }
33
34 return nullptr;
35 }
36
37 // add interfaces
38 // interfaces of a superclass (they are located before others)
39 // self interfaces and interfaces of self interfaces
40 // add methods if it's not an interface (methods are located only for classes)
Build(ClassLinker * classLinker,Class * base,Span<Class * > classInterfaces,bool isInterface)41 void EtsITableBuilder::Build(ClassLinker *classLinker, Class *base, Span<Class *> classInterfaces, bool isInterface)
42 {
43 PandaUnorderedSet<Class *> interfaces;
44
45 if (base != nullptr) {
46 auto superItable = base->GetITable().Get();
47 for (auto item : superItable) {
48 interfaces.insert(item.GetInterface());
49 }
50 }
51
52 for (auto interface : classInterfaces) {
53 auto table = interface->GetITable().Get();
54 for (auto item : table) {
55 interfaces.insert(item.GetInterface());
56 }
57 interfaces.insert(interface);
58 }
59
60 auto allocator = classLinker->GetAllocator();
61 ITable::Entry *array = nullptr;
62 if (!interfaces.empty()) {
63 array = allocator->AllocArray<ITable::Entry>(interfaces.size());
64 }
65
66 Span<ITable::Entry> itable {array, interfaces.size()};
67
68 for (auto &entry : itable) {
69 entry.SetMethods({nullptr, nullptr});
70 }
71
72 if (base != nullptr) {
73 auto superItable = base->GetITable().Get();
74 for (size_t i = 0; i < superItable.size(); i++) {
75 itable[i] = superItable[i].Copy(allocator);
76 interfaces.erase(superItable[i].GetInterface());
77 }
78 }
79
80 size_t superItableSize = 0;
81 if (base != nullptr) {
82 superItableSize = base->GetITable().Size();
83 }
84
85 size_t shift = superItableSize;
86
87 for (auto interface : classInterfaces) {
88 auto table = interface->GetITable().Get();
89 for (auto &item : table) {
90 auto iterator = interfaces.find(item.GetInterface());
91 if (iterator != interfaces.end()) {
92 itable[shift] = item.Copy(allocator);
93 interfaces.erase(item.GetInterface());
94 shift += 1;
95 }
96 }
97 auto iterator = interfaces.find(interface);
98 if (iterator != interfaces.end()) {
99 itable[shift].SetInterface(interface);
100 interfaces.erase(interface);
101 shift += 1;
102 }
103 }
104
105 ASSERT(interfaces.empty());
106
107 if (!isInterface) {
108 for (size_t i = superItableSize; i < itable.Size(); i++) {
109 auto &entry = itable[i];
110 auto methods = entry.GetInterface()->GetVirtualMethods();
111 Method **methodsAlloc = nullptr;
112 if (!methods.Empty()) {
113 methodsAlloc = allocator->AllocArray<Method *>(methods.size());
114 }
115 Span<Method *> methodsArray = {methodsAlloc, methods.size()};
116 entry.SetMethods(methodsArray);
117 }
118 }
119
120 itable_ = ITable(itable);
121 }
122
Resolve(Class * klass)123 void EtsITableBuilder::Resolve(Class *klass)
124 {
125 if (klass->IsInterface()) {
126 return;
127 }
128
129 for (size_t i = itable_.Size(); i > 0; i--) {
130 auto entry = itable_[i - 1];
131 auto methods = entry.GetInterface()->GetVirtualMethods();
132 for (size_t j = 0; j < methods.size(); j++) {
133 auto *res = FindMethodInVTable(klass, &methods[j]);
134 if (res == nullptr) {
135 res = &methods[j];
136 }
137
138 entry.GetMethods()[j] = res;
139 }
140 }
141 }
142
UpdateClass(Class * klass)143 void EtsITableBuilder::UpdateClass(Class *klass)
144 {
145 klass->SetITable(itable_);
146 DumpITable(klass);
147 }
148
DumpITable(Class * klass)149 void EtsITableBuilder::DumpITable([[maybe_unused]] Class *klass)
150 {
151 #ifndef NDEBUG
152 LOG(DEBUG, CLASS_LINKER) << "itable of class " << klass->GetName() << ":";
153 auto itable = klass->GetITable();
154 size_t idxI = 0;
155 size_t idxM = 0;
156 for (size_t i = 0; i < itable.Size(); i++) {
157 auto entry = itable[i];
158 auto interface = entry.GetInterface();
159 LOG(DEBUG, CLASS_LINKER) << "[ interface - " << idxI++ << " ] " << interface->GetName() << ":";
160 auto methods = entry.GetMethods();
161 for (auto *method : methods) {
162 LOG(DEBUG, CLASS_LINKER) << "[ method - " << idxM++ << " ] " << method->GetFullName() << " - "
163 << method->GetFileId().GetOffset();
164 }
165 }
166 #endif // NDEBUG
167 }
168
169 } // namespace panda::ets
170