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