1 /**
2 * Copyright (c) 2021-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
16 #include "plugins/ets/runtime/ets_vtable_builder.h"
17
18 #include "ets_class_linker_extension.h"
19 #include "runtime/include/class_linker.h"
20
21 namespace ark::ets {
22
IsInSamePackage(const MethodInfo & info1,const MethodInfo & info2) const23 bool EtsVTableOverridePred::IsInSamePackage(const MethodInfo &info1, const MethodInfo &info2) const
24 {
25 if (info1.GetLoadContext() != info2.GetLoadContext()) {
26 return false;
27 }
28
29 auto *desc1 = info1.GetClassName();
30 auto *desc2 = info2.GetClassName();
31
32 while (ClassHelper::IsArrayDescriptor(desc1)) {
33 desc1 = ClassHelper::GetComponentDescriptor(desc1);
34 }
35
36 while (ClassHelper::IsArrayDescriptor(desc2)) {
37 desc2 = ClassHelper::GetComponentDescriptor(desc2);
38 }
39
40 Span sp1(desc1, 1);
41 Span sp2(desc2, 1);
42 while (sp1[0] == sp2[0] && sp1[0] != 0 && sp2[0] != 0) {
43 sp1 = Span(sp1.cend(), 1);
44 sp2 = Span(sp2.cend(), 1);
45 }
46
47 bool isSamePackage = true;
48 while (sp1[0] != 0 && isSamePackage) {
49 isSamePackage = sp1[0] != '/';
50 sp1 = Span(sp1.cend(), 1);
51 }
52
53 while (sp2[0] != 0 && isSamePackage) {
54 isSamePackage = sp2[0] != '/';
55 sp2 = Span(sp2.cend(), 1);
56 }
57
58 return isSamePackage;
59 }
60
61 class RefTypeLink {
62 public:
RefTypeLink(uint8_t const * descr)63 explicit RefTypeLink(uint8_t const *descr) : descriptor_(descr) {}
RefTypeLink(panda_file::File const * pf,panda_file::File::EntityId idx)64 RefTypeLink(panda_file::File const *pf, panda_file::File::EntityId idx)
65 : pf_(pf), id_(idx), descriptor_(pf->GetStringData(idx).data)
66 {
67 }
68
InPDA(panda_file::ProtoDataAccessor & pda,uint32_t idx)69 static RefTypeLink InPDA(panda_file::ProtoDataAccessor &pda, uint32_t idx)
70 {
71 return RefTypeLink(&pda.GetPandaFile(), pda.GetReferenceType(idx));
72 }
73
GetDescriptor()74 uint8_t const *GetDescriptor()
75 {
76 return descriptor_;
77 }
78
AreEqual(RefTypeLink const & a,RefTypeLink const & b)79 ALWAYS_INLINE static bool AreEqual(RefTypeLink const &a, RefTypeLink const &b)
80 {
81 if (LIKELY(a.pf_ == b.pf_ && a.pf_ != nullptr)) {
82 return a.id_ == b.id_;
83 }
84 return utf::IsEqual(a.descriptor_, b.descriptor_);
85 }
86
CreateCDA()87 ALWAYS_INLINE std::optional<panda_file::ClassDataAccessor> CreateCDA()
88 {
89 if (UNLIKELY(!Resolve())) {
90 return std::nullopt;
91 }
92 return panda_file::ClassDataAccessor(*pf_, id_);
93 }
94
95 private:
Resolve()96 bool Resolve()
97 {
98 if (pf_ != nullptr && !pf_->IsExternal(id_)) {
99 return true;
100 }
101
102 auto linker = Runtime::GetCurrent()->GetClassLinker();
103
104 // NOTE(vpukhov): speedup lookup with tls cache
105 linker->EnumeratePandaFiles([this](panda_file::File const &itpf) {
106 auto itClassId = itpf.GetClassId(descriptor_);
107 if (itClassId.IsValid() && !itpf.IsExternal(itClassId)) {
108 pf_ = &itpf;
109 id_ = itClassId;
110 return false;
111 }
112 return true;
113 });
114 return pf_ != nullptr;
115 }
116
117 panda_file::File const *pf_ {};
118 panda_file::File::EntityId id_ {};
119 uint8_t const *descriptor_ {};
120 };
121
IsPrimitveDescriptor(uint8_t const * descr)122 static inline bool IsPrimitveDescriptor(uint8_t const *descr)
123 {
124 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
125 return descr[0] != 0 && descr[1] == 0;
126 }
127
128 static bool RefIsAssignableToImpl(RefTypeLink sub, RefTypeLink super, uint32_t depth);
129
RefExtendsOrImplements(RefTypeLink sub,RefTypeLink super,uint32_t depth)130 static bool RefExtendsOrImplements(RefTypeLink sub, RefTypeLink super, uint32_t depth)
131 {
132 auto superCDAOpt = super.CreateCDA();
133 if (!superCDAOpt.has_value()) {
134 return false;
135 }
136 panda_file::ClassDataAccessor const &superCDA = superCDAOpt.value();
137 if (superCDA.IsFinal()) {
138 return false;
139 }
140 auto subCDAOpt = sub.CreateCDA();
141 if (!subCDAOpt.has_value()) {
142 return false;
143 }
144 panda_file::ClassDataAccessor const &subCDA = subCDAOpt.value();
145
146 if (superCDA.IsInterface()) {
147 for (size_t i = 0; i < subCDA.GetIfacesNumber(); ++i) {
148 auto iface = RefTypeLink(&subCDA.GetPandaFile(), subCDA.GetInterfaceId(i));
149 if (RefIsAssignableToImpl(iface, super, depth)) {
150 return true;
151 }
152 }
153 }
154 // subtype is interface (thus has no base class) or subtype is Object
155 if (subCDA.IsInterface() || subCDA.GetSuperClassId().GetOffset() == 0) {
156 return false;
157 }
158 return RefIsAssignableToImpl(RefTypeLink(&subCDA.GetPandaFile(), subCDA.GetSuperClassId()), super, depth);
159 }
160
RefIsAssignableToImpl(RefTypeLink sub,RefTypeLink super,uint32_t depth)161 static bool RefIsAssignableToImpl(RefTypeLink sub, RefTypeLink super, uint32_t depth)
162 {
163 if (UNLIKELY(depth-- == 0)) {
164 LOG(ERROR, CLASS_LINKER) << "Max class assignability test depth reached";
165 return false;
166 }
167 if (RefTypeLink::AreEqual(sub, super)) {
168 return true;
169 }
170 if (IsPrimitveDescriptor(sub.GetDescriptor()) || IsPrimitveDescriptor(super.GetDescriptor())) {
171 return false;
172 }
173 if (utf::IsEqual(super.GetDescriptor(), utf::CStringAsMutf8(panda_file_items::class_descriptors::OBJECT.data()))) {
174 return true;
175 }
176 if (ClassHelper::IsArrayDescriptor(super.GetDescriptor())) {
177 if (!ClassHelper::IsArrayDescriptor(sub.GetDescriptor())) {
178 return false;
179 }
180 RefTypeLink subComp(ClassHelper::GetComponentDescriptor(sub.GetDescriptor()));
181 RefTypeLink superComp(ClassHelper::GetComponentDescriptor(super.GetDescriptor()));
182 return RefIsAssignableToImpl(subComp, superComp, depth);
183 }
184 // Assume array does not implement interfaces
185 if (ClassHelper::IsArrayDescriptor(sub.GetDescriptor())) {
186 return false;
187 }
188
189 return RefExtendsOrImplements(sub, super, depth);
190 }
191
RefIsAssignableTo(RefTypeLink sub,RefTypeLink super)192 static inline bool RefIsAssignableTo(RefTypeLink sub, RefTypeLink super)
193 {
194 static constexpr uint32_t ASSIGNABILITY_MAX_DEPTH = 256U;
195 return RefIsAssignableToImpl(sub, super, ASSIGNABILITY_MAX_DEPTH);
196 }
197
ETSProtoIsOverriddenBy(Method::ProtoId const & base,Method::ProtoId const & derv)198 bool ETSProtoIsOverriddenBy(Method::ProtoId const &base, Method::ProtoId const &derv)
199 {
200 auto basePDA = panda_file::ProtoDataAccessor(base.GetPandaFile(), base.GetEntityId());
201 auto dervPDA = panda_file::ProtoDataAccessor(derv.GetPandaFile(), derv.GetEntityId());
202 if (dervPDA.GetNumElements() != basePDA.GetNumElements()) {
203 return false;
204 }
205 uint32_t numElems = basePDA.GetNumElements();
206
207 for (uint32_t i = 0, refIdx = 0; i < numElems; ++i) {
208 if (dervPDA.GetType(i) != basePDA.GetType(i)) {
209 return false;
210 }
211 if (dervPDA.GetType(i).IsReference()) {
212 auto dervRef = RefTypeLink::InPDA(dervPDA, refIdx);
213 auto baseRef = RefTypeLink::InPDA(basePDA, refIdx);
214 auto res = i == 0 ? RefIsAssignableTo(dervRef, baseRef) : RefIsAssignableTo(baseRef, dervRef);
215 if (!res) {
216 return false;
217 }
218 refIdx++;
219 }
220 }
221 return true;
222 }
223
224 } // namespace ark::ets
225