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