• 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 "runtime/include/class_linker_extension.h"
17 
18 #include "libpandabase/utils/utf.h"
19 #include "runtime/include/class_linker-inl.h"
20 #include "runtime/include/class_linker.h"
21 #include "runtime/include/coretypes/class.h"
22 #include "runtime/include/runtime.h"
23 #include "runtime/include/thread.h"
24 
25 namespace ark {
26 
~ClassLinkerExtension()27 ClassLinkerExtension::~ClassLinkerExtension()
28 {
29     os::memory::LockHolder lock(contextsLock_);
30     for (auto *ctx : contexts_) {
31         classLinker_->GetAllocator()->Delete(ctx);
32     }
33 }
34 
LoadClass(const uint8_t * descriptor,bool needCopyDescriptor,ClassLinkerErrorHandler * errorHandler)35 Class *ClassLinkerExtension::BootContext::LoadClass(const uint8_t *descriptor, bool needCopyDescriptor,
36                                                     ClassLinkerErrorHandler *errorHandler)
37 {
38     ASSERT(extension_->IsInitialized());
39 
40     return extension_->GetClassLinker()->GetClass(descriptor, needCopyDescriptor, this, errorHandler);
41 }
42 
EnumeratePandaFiles(const std::function<bool (const panda_file::File &)> & cb) const43 void ClassLinkerExtension::BootContext::EnumeratePandaFiles(
44     const std::function<bool(const panda_file::File &)> &cb) const
45 {
46     extension_->GetClassLinker()->EnumerateBootPandaFiles(cb);
47 }
48 
49 class SuppressErrorHandler : public ClassLinkerErrorHandler {
OnError(ClassLinker::Error error,const PandaString & message)50     void OnError([[maybe_unused]] ClassLinker::Error error, [[maybe_unused]] const PandaString &message) override {}
51 };
52 
LoadClass(const uint8_t * descriptor,bool needCopyDescriptor,ClassLinkerErrorHandler * errorHandler)53 Class *ClassLinkerExtension::AppContext::LoadClass(const uint8_t *descriptor, bool needCopyDescriptor,
54                                                    ClassLinkerErrorHandler *errorHandler)
55 {
56     ASSERT(extension_->IsInitialized());
57 
58     SuppressErrorHandler handler;
59     auto *cls = extension_->GetClass(descriptor, needCopyDescriptor, nullptr, &handler);
60     if (cls != nullptr) {
61         return cls;
62     }
63 
64     for (auto &pf : pfs_) {
65         auto classId = pf->GetClassId(descriptor);
66         if (!classId.IsValid() || pf->IsExternal(classId)) {
67             continue;
68         }
69         return extension_->GetClassLinker()->LoadClass(*pf, classId, this, errorHandler);
70     }
71 
72     if (errorHandler != nullptr) {
73         PandaStringStream ss;
74         ss << "Cannot find class " << descriptor << " in all app panda files";
75         errorHandler->OnError(ClassLinker::Error::CLASS_NOT_FOUND, ss.str());
76     }
77     return nullptr;
78 }
79 
InitializeArrayClassRoot(ClassRoot root,ClassRoot componentRoot,const char * descriptor)80 void ClassLinkerExtension::InitializeArrayClassRoot(ClassRoot root, ClassRoot componentRoot, const char *descriptor)
81 {
82     ASSERT(IsInitialized());
83 
84     auto *arrayClass = CreateClass(utf::CStringAsMutf8(descriptor), GetClassVTableSize(root), GetClassIMTSize(root),
85                                    GetClassSize(root));
86     ASSERT(arrayClass != nullptr);
87     arrayClass->SetLoadContext(&bootContext_);
88     auto *componentClass = GetClassRoot(componentRoot);
89     if (!InitializeArrayClass(arrayClass, componentClass)) {
90         LOG(FATAL, CLASS_LINKER) << "Failed to initialize array class root '" << arrayClass->GetName() << "'";
91         return;
92     }
93 
94     AddClass(arrayClass);
95     SetClassRoot(root, arrayClass);
96 }
97 
InitializePrimitiveClassRoot(ClassRoot root,panda_file::Type::TypeId typeId,const char * descriptor)98 void ClassLinkerExtension::InitializePrimitiveClassRoot(ClassRoot root, panda_file::Type::TypeId typeId,
99                                                         const char *descriptor)
100 {
101     ASSERT(IsInitialized());
102 
103     auto *primitiveClass = CreateClass(utf::CStringAsMutf8(descriptor), GetClassVTableSize(root), GetClassIMTSize(root),
104                                        GetClassSize(root));
105     ASSERT(primitiveClass != nullptr);
106     primitiveClass->SetType(panda_file::Type(typeId));
107     primitiveClass->SetLoadContext(&bootContext_);
108     InitializePrimitiveClass(primitiveClass);
109     AddClass(primitiveClass);
110     SetClassRoot(root, primitiveClass);
111 }
112 
Initialize(ClassLinker * classLinker,bool compressedStringEnabled)113 bool ClassLinkerExtension::Initialize(ClassLinker *classLinker, bool compressedStringEnabled)
114 {
115     classLinker_ = classLinker;
116     InitializeImpl(compressedStringEnabled);
117 
118     canInitializeClasses_ = true;
119     // Copy classes to separate container as ClassLinkerExtension::InitializeClass
120     // can load more classes and modify boot context
121     PandaVector<Class *> klasses;
122     bootContext_.EnumerateClasses([&klasses](Class *klass) {
123         if (!klass->IsLoaded()) {
124             klasses.push_back(klass);
125         }
126         return true;
127     });
128 
129     for (auto *klass : klasses) {
130         if (klass->IsLoaded()) {
131             continue;
132         }
133 
134         InitializeClass(klass);
135         klass->SetState(Class::State::LOADED);
136     }
137     return true;
138 }
139 
InitializeRoots(ManagedThread * thread)140 bool ClassLinkerExtension::InitializeRoots(ManagedThread *thread)
141 {
142     ASSERT(IsInitialized());
143 
144     for (auto *klass : classRoots_) {
145         if (klass == nullptr) {
146             continue;
147         }
148 
149         if (!classLinker_->InitializeClass(thread, klass)) {
150             LOG(FATAL, CLASS_LINKER) << "Failed to initialize class '" << klass->GetName() << "'";
151             return false;
152         }
153     }
154 
155     return true;
156 }
157 
FindLoadedClass(const uint8_t * descriptor,ClassLinkerContext * context)158 Class *ClassLinkerExtension::FindLoadedClass(const uint8_t *descriptor, ClassLinkerContext *context /* = nullptr */)
159 {
160     return classLinker_->FindLoadedClass(descriptor, ResolveContext(context));
161 }
162 
GetClass(const uint8_t * descriptor,bool needCopyDescriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)163 Class *ClassLinkerExtension::GetClass(const uint8_t *descriptor, bool needCopyDescriptor /* = true */,
164                                       ClassLinkerContext *context /* = nullptr */,
165                                       ClassLinkerErrorHandler *errorHandler /* = nullptr */)
166 {
167     ASSERT(IsInitialized());
168 
169     return classLinker_->GetClass(descriptor, needCopyDescriptor, ResolveContext(context),
170                                   ResolveErrorHandler(errorHandler));
171 }
172 
WrapClassNotFoundExceptionIfNeeded(ClassLinker * classLinker,const uint8_t * descriptor,const LanguageContext & ctx)173 static void WrapClassNotFoundExceptionIfNeeded(ClassLinker *classLinker, const uint8_t *descriptor,
174                                                const LanguageContext &ctx)
175 {
176     auto *thread = ManagedThread::GetCurrent();
177     if (thread == nullptr || !thread->HasPendingException()) {
178         return;
179     }
180 
181     auto *classNotFoundExceptionClass =
182         classLinker->GetExtension(ctx)->GetClass(ctx.GetClassNotFoundExceptionDescriptor());
183     if (classNotFoundExceptionClass == nullptr) {
184         // We've got OOM
185         ASSERT(thread->GetVM()->GetOOMErrorObject() == nullptr ||
186                thread->GetException()->ClassAddr<Class>() == thread->GetVM()->GetOOMErrorObject()->ClassAddr<Class>());
187         return;
188     }
189     ASSERT(classNotFoundExceptionClass != nullptr);
190 
191     auto *cause = thread->GetException();
192     if (cause->IsInstanceOf(classNotFoundExceptionClass)) {
193         auto name = ClassHelper::GetName(descriptor);
194         ark::ThrowException(ctx, thread, ctx.GetNoClassDefFoundErrorDescriptor(), utf::CStringAsMutf8(name.c_str()));
195     }
196 }
197 
GetClass(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)198 Class *ClassLinkerExtension::GetClass(const panda_file::File &pf, panda_file::File::EntityId id,
199                                       ClassLinkerContext *context /* = nullptr */,
200                                       ClassLinkerErrorHandler *errorHandler /* = nullptr */)
201 {
202     ASSERT(IsInitialized());
203 
204     auto *cls = classLinker_->GetClass(pf, id, ResolveContext(context), ResolveErrorHandler(errorHandler));
205 
206     if (UNLIKELY(cls == nullptr)) {
207         auto *descriptor = pf.GetStringData(id).data;
208         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(GetLanguage());
209         WrapClassNotFoundExceptionIfNeeded(classLinker_, descriptor, ctx);
210     }
211 
212     return cls;
213 }
214 
AddClass(Class * klass)215 Class *ClassLinkerExtension::AddClass(Class *klass)
216 {
217     ASSERT(IsInitialized());
218 
219     auto *context = klass->GetLoadContext();
220 
221     auto *otherKlass = ResolveContext(context)->InsertClass(klass);
222     if (otherKlass != nullptr) {
223         classLinker_->FreeClass(klass);
224         return otherKlass;
225     }
226     OnClassPrepared(klass);
227 
228     return klass;
229 }
230 
NumLoadedClasses()231 size_t ClassLinkerExtension::NumLoadedClasses()
232 {
233     ASSERT(IsInitialized());
234 
235     size_t sum = bootContext_.NumLoadedClasses();
236 
237     {
238         os::memory::LockHolder lock(contextsLock_);
239         for (auto *ctx : contexts_) {
240             sum += ctx->NumLoadedClasses();
241         }
242     }
243     return sum;
244 }
245 
VisitLoadedClasses(size_t flag)246 void ClassLinkerExtension::VisitLoadedClasses(size_t flag)
247 {
248     bootContext_.VisitLoadedClasses(flag);
249     {
250         os::memory::LockHolder lock(contextsLock_);
251         for (auto *ctx : contexts_) {
252             ctx->VisitLoadedClasses(flag);
253         }
254     }
255 }
256 
FreeLoadedClasses()257 void ClassLinkerExtension::FreeLoadedClasses()
258 {
259     ASSERT(IsInitialized());
260 
261     bootContext_.EnumerateClasses([this](Class *klass) {
262         FreeClass(klass);
263         classLinker_->FreeClassData(klass);
264         return true;
265     });
266     {
267         os::memory::LockHolder lock(contextsLock_);
268         for (auto *ctx : contexts_) {
269             ctx->EnumerateClasses([this](Class *klass) {
270                 FreeClass(klass);
271                 classLinker_->FreeClassData(klass);
272                 return true;
273             });
274         }
275     }
276 }
277 
CreateApplicationClassLinkerContext(const PandaVector<PandaString> & path)278 ClassLinkerContext *ClassLinkerExtension::CreateApplicationClassLinkerContext(const PandaVector<PandaString> &path)
279 {
280     PandaVector<PandaFilePtr> appFiles;
281     for (auto &pfPath : path) {
282         auto pf = panda_file::OpenPandaFileOrZip(pfPath);
283         if (pf == nullptr) {
284             return nullptr;
285         }
286         appFiles.push_back(std::move(pf));
287     }
288     ClassLinkerContext *ctx = CreateApplicationClassLinkerContext(std::move(appFiles));
289     return ctx;
290 }
291 
CreateApplicationClassLinkerContext(PandaVector<PandaFilePtr> && appFiles)292 ClassLinkerContext *ClassLinkerExtension::CreateApplicationClassLinkerContext(PandaVector<PandaFilePtr> &&appFiles)
293 {
294     PandaVector<const panda_file::File *> appFilePtrs;
295     appFilePtrs.reserve(appFiles.size());
296     for (auto &pf : appFiles) {
297         appFilePtrs.emplace_back(pf.get());
298     }
299 
300     auto allocator = classLinker_->GetAllocator();
301     auto *ctx = allocator->New<AppContext>(this, std::move(appFilePtrs));
302     RegisterContext([ctx]() { return ctx; });
303     for (auto &pf : appFiles) {
304         classLinker_->AddPandaFile(std::move(pf), ctx);
305     }
306     return ctx;
307 }
308 
AddCreatedClass(Class * klass)309 void ClassLinkerExtension::AddCreatedClass(Class *klass)
310 {
311     os::memory::LockHolder lock(createdClassesLock_);
312     createdClasses_.push_back(klass);
313 }
314 
RemoveCreatedClass(Class * klass)315 void ClassLinkerExtension::RemoveCreatedClass(Class *klass)
316 {
317     os::memory::LockHolder lock(createdClassesLock_);
318     auto it = find(createdClasses_.begin(), createdClasses_.end(), klass);
319     if (it != createdClasses_.end()) {
320         createdClasses_.erase(it);
321     }
322 }
323 
OnClassPrepared(Class * klass)324 void ClassLinkerExtension::OnClassPrepared(Class *klass)
325 {
326     // Atomic with seq_cst order reason: data race with record_new_class_ with requirement for sequentially
327     // consistent order where threads observe all modifications in the same order
328     if (recordNewClass_.load(std::memory_order_seq_cst)) {
329         os::memory::LockHolder newClassesLock(newClassesLock_);
330         newClasses_.push_back(klass);
331     }
332 
333     RemoveCreatedClass(klass);
334 }
335 
FromClassObject(ObjectHeader * obj)336 Class *ClassLinkerExtension::FromClassObject(ObjectHeader *obj)
337 {
338     return (obj != nullptr) ? ((reinterpret_cast<ark::coretypes::Class *>(obj))->GetRuntimeClass()) : nullptr;
339 }
340 
GetClassObjectSizeFromClassSize(uint32_t size)341 size_t ClassLinkerExtension::GetClassObjectSizeFromClassSize(uint32_t size)
342 {
343     return ark::coretypes::Class::GetSize(size);
344 }
345 
FreeObsoleteData()346 void ClassLinkerExtension::FreeObsoleteData()
347 {
348     os::memory::LockHolder lock(obsoleteClassesLock_);
349     for (auto &cls : obsoleteClasses_) {
350         ASSERT(cls != nullptr);
351         GetClassLinker()->FreeClass(cls);
352     }
353 }
354 
AddObsoleteClass(const PandaVector<Class * > & classes)355 void ClassLinkerExtension::AddObsoleteClass(const PandaVector<Class *> &classes)
356 {
357     if (classes.empty()) {
358         return;
359     }
360     os::memory::LockHolder lock(obsoleteClassesLock_);
361     obsoleteClasses_.insert(obsoleteClasses_.end(), classes.begin(), classes.end());
362 }
363 
364 }  // namespace ark
365