1 /**
2 * Copyright (c) 2021-2025 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(const ClassLinkerContext * ctx,uint8_t const * descr)63 explicit RefTypeLink(const ClassLinkerContext *ctx, uint8_t const *descr) : ctx_(ctx), descriptor_(descr) {}
RefTypeLink(const ClassLinkerContext * ctx,panda_file::File const * pf,panda_file::File::EntityId idx)64 RefTypeLink(const ClassLinkerContext *ctx, panda_file::File const *pf, panda_file::File::EntityId idx)
65 : ctx_(ctx), pf_(pf), id_(idx), descriptor_(pf->GetStringData(idx).data)
66 {
67 }
68
InPDA(const ClassLinkerContext * ctx,panda_file::ProtoDataAccessor & pda,uint32_t idx)69 static RefTypeLink InPDA(const ClassLinkerContext *ctx, panda_file::ProtoDataAccessor &pda, uint32_t idx)
70 {
71 return RefTypeLink(ctx, &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 (IsResolved()) {
99 return true;
100 }
101
102 // Need to traverse `RuntimeLinker` chain, which is why `EnumeratePandaFilesInChain` is used
103 // NOTE(vpukhov): speedup lookup with tls cache
104 ctx_->EnumeratePandaFilesInChain([this](panda_file::File const &itpf) {
105 auto itClassId = itpf.GetClassId(descriptor_);
106 if (itClassId.IsValid() && !itpf.IsExternal(itClassId)) {
107 pf_ = &itpf;
108 id_ = itClassId;
109 return false;
110 }
111 return true;
112 });
113 return IsResolved();
114 }
115
IsResolved() const116 bool IsResolved() const
117 {
118 return (pf_ != nullptr) && !pf_->IsExternal(id_);
119 }
120
121 const ClassLinkerContext *ctx_ {};
122 panda_file::File const *pf_ {};
123 panda_file::File::EntityId id_ {};
124 uint8_t const *descriptor_ {};
125 };
126
IsPrimitveDescriptor(uint8_t const * descr)127 static inline bool IsPrimitveDescriptor(uint8_t const *descr)
128 {
129 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
130 return descr[0] != 0 && descr[1] == 0;
131 }
132
133 static bool RefIsAssignableToImpl(const ClassLinkerContext *ctx, RefTypeLink sub, RefTypeLink super, uint32_t depth);
134
RefExtendsOrImplements(const ClassLinkerContext * ctx,RefTypeLink sub,RefTypeLink super,uint32_t depth)135 static bool RefExtendsOrImplements(const ClassLinkerContext *ctx, RefTypeLink sub, RefTypeLink super, uint32_t depth)
136 {
137 auto superCDAOpt = super.CreateCDA();
138 if (!superCDAOpt.has_value()) {
139 return false;
140 }
141 panda_file::ClassDataAccessor const &superCDA = superCDAOpt.value();
142 if (superCDA.IsFinal()) {
143 return false;
144 }
145 auto subCDAOpt = sub.CreateCDA();
146 if (!subCDAOpt.has_value()) {
147 return false;
148 }
149 panda_file::ClassDataAccessor const &subCDA = subCDAOpt.value();
150
151 if (superCDA.IsInterface()) {
152 for (size_t i = 0; i < subCDA.GetIfacesNumber(); ++i) {
153 auto iface = RefTypeLink(ctx, &subCDA.GetPandaFile(), subCDA.GetInterfaceId(i));
154 if (RefIsAssignableToImpl(ctx, iface, super, depth)) {
155 return true;
156 }
157 }
158 }
159 // subtype is interface (thus has no base class) or subtype is Object
160 if (subCDA.IsInterface() || subCDA.GetSuperClassId().GetOffset() == 0) {
161 return false;
162 }
163 return RefIsAssignableToImpl(ctx, RefTypeLink(ctx, &subCDA.GetPandaFile(), subCDA.GetSuperClassId()), super, depth);
164 }
165
RefIsAssignableToImpl(const ClassLinkerContext * ctx,RefTypeLink sub,RefTypeLink super,uint32_t depth)166 static bool RefIsAssignableToImpl(const ClassLinkerContext *ctx, RefTypeLink sub, RefTypeLink super, uint32_t depth)
167 {
168 if (UNLIKELY(depth-- == 0)) {
169 LOG(ERROR, CLASS_LINKER) << "Max class assignability test depth reached";
170 return false;
171 }
172 if (RefTypeLink::AreEqual(sub, super)) {
173 return true;
174 }
175 if (IsPrimitveDescriptor(sub.GetDescriptor()) || IsPrimitveDescriptor(super.GetDescriptor())) {
176 return false;
177 }
178 if (utf::IsEqual(super.GetDescriptor(), utf::CStringAsMutf8(panda_file_items::class_descriptors::OBJECT.data()))) {
179 return true;
180 }
181 if (ClassHelper::IsArrayDescriptor(super.GetDescriptor())) {
182 if (!ClassHelper::IsArrayDescriptor(sub.GetDescriptor())) {
183 return false;
184 }
185 RefTypeLink subComp(ctx, ClassHelper::GetComponentDescriptor(sub.GetDescriptor()));
186 RefTypeLink superComp(ctx, ClassHelper::GetComponentDescriptor(super.GetDescriptor()));
187 return RefIsAssignableToImpl(ctx, subComp, superComp, depth);
188 }
189 // Assume array does not implement interfaces
190 if (ClassHelper::IsArrayDescriptor(sub.GetDescriptor())) {
191 return false;
192 }
193
194 return RefExtendsOrImplements(ctx, sub, super, depth);
195 }
196
RefIsAssignableTo(const ClassLinkerContext * ctx,RefTypeLink sub,RefTypeLink super)197 static inline bool RefIsAssignableTo(const ClassLinkerContext *ctx, RefTypeLink sub, RefTypeLink super)
198 {
199 static constexpr uint32_t ASSIGNABILITY_MAX_DEPTH = 256U;
200 return RefIsAssignableToImpl(ctx, sub, super, ASSIGNABILITY_MAX_DEPTH);
201 }
202
ETSProtoIsOverriddenBy(const ClassLinkerContext * ctx,Method::ProtoId const & base,Method::ProtoId const & derv)203 bool ETSProtoIsOverriddenBy(const ClassLinkerContext *ctx, Method::ProtoId const &base, Method::ProtoId const &derv)
204 {
205 ASSERT(ctx != nullptr);
206 auto basePDA = panda_file::ProtoDataAccessor(base.GetPandaFile(), base.GetEntityId());
207 auto dervPDA = panda_file::ProtoDataAccessor(derv.GetPandaFile(), derv.GetEntityId());
208 if (dervPDA.GetNumElements() != basePDA.GetNumElements()) {
209 return false;
210 }
211 uint32_t numElems = basePDA.GetNumElements();
212
213 for (uint32_t i = 0, refIdx = 0; i < numElems; ++i) {
214 if (dervPDA.GetType(i) != basePDA.GetType(i)) {
215 return false;
216 }
217 if (dervPDA.GetType(i).IsReference()) {
218 auto dervRef = RefTypeLink::InPDA(ctx, dervPDA, refIdx);
219 auto baseRef = RefTypeLink::InPDA(ctx, basePDA, refIdx);
220 auto res = i == 0 ? RefIsAssignableTo(ctx, dervRef, baseRef) : RefIsAssignableTo(ctx, baseRef, dervRef);
221 if (!res) {
222 return false;
223 }
224 refIdx++;
225 }
226 }
227 return true;
228 }
229
230 } // namespace ark::ets
231