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