• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 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_class_linker_context.h"
17 
18 #include "ets_platform_types.h"
19 #include "include/class.h"
20 #include "plugins/ets/runtime/ets_coroutine.h"
21 #include "plugins/ets/runtime/ets_vm.h"
22 #include "plugins/ets/runtime/types/ets_abc_file.h"
23 #include "plugins/ets/runtime/types/ets_abc_runtime_linker.h"
24 #include "plugins/ets/runtime/types/ets_method.h"
25 #include "plugins/ets/runtime/types/ets_string.h"
26 
27 namespace ark::ets {
28 
29 namespace {
30 
31 class DecoratorErrorHandler final : public ClassLinkerErrorHandler {
32 public:
DecoratorErrorHandler(ClassLinkerErrorHandler * errorHandler)33     explicit DecoratorErrorHandler(ClassLinkerErrorHandler *errorHandler) : errorHandler_(errorHandler) {}
34 
OnError(ClassLinker::Error error,const PandaString & message)35     void OnError(ClassLinker::Error error, const PandaString &message) override
36     {
37         lastError_.emplace(error, message);
38     }
39 
HasError() const40     bool HasError() const
41     {
42         return lastError_.has_value();
43     }
44 
GetErrorCode() const45     ClassLinker::Error GetErrorCode() const
46     {
47         ASSERT(HasError());
48         return lastError_->first;
49     }
50 
PropagateError()51     void PropagateError()
52     {
53         ASSERT(HasError());
54         if (errorHandler_ != nullptr) {
55             errorHandler_->OnError(lastError_->first, lastError_->second);
56         }
57     }
58 
ClearError()59     void ClearError()
60     {
61         lastError_.reset();
62     }
63 
64 private:
65     ClassLinkerErrorHandler *errorHandler_ {nullptr};
66     std::optional<std::pair<ClassLinker::Error, PandaString>> lastError_;
67 };
68 
ReportClassNotFound(const uint8_t * descriptor,ClassLinkerErrorHandler * errorHandler)69 void ReportClassNotFound(const uint8_t *descriptor, ClassLinkerErrorHandler *errorHandler)
70 {
71     if (errorHandler != nullptr) {
72         PandaStringStream ss;
73         ss << "Cannot find class " << descriptor;
74         errorHandler->OnError(ClassLinker::Error::CLASS_NOT_FOUND, ss.str());
75     }
76 }
77 
LoadFromBootContext(const uint8_t * descriptor,DecoratorErrorHandler & errorHandler,ClassLinkerContext * ctx)78 Class *LoadFromBootContext(const uint8_t *descriptor, DecoratorErrorHandler &errorHandler, ClassLinkerContext *ctx)
79 {
80     ASSERT(ctx->IsBootContext());
81     auto *klass = Runtime::GetCurrent()->GetClassLinker()->GetClass(descriptor, true, ctx, &errorHandler);
82     if (errorHandler.HasError()) {
83         if (errorHandler.GetErrorCode() == ClassLinker::Error::CLASS_NOT_FOUND) {
84             // Clear the error in order to delegate resolution.
85             errorHandler.ClearError();
86         } else {
87             // Report errors occurred during class loading.
88             errorHandler.PropagateError();
89         }
90     }
91     return klass;
92 }
93 
TryLoadingClassInChain(const uint8_t * descriptor,DecoratorErrorHandler & errorHandler,ClassLinkerContext * ctx,Class ** klass)94 bool TryLoadingClassInChain(const uint8_t *descriptor, DecoratorErrorHandler &errorHandler, ClassLinkerContext *ctx,
95                             Class **klass)
96 {
97     ASSERT(ctx != nullptr);
98     ASSERT(klass != nullptr);
99     ASSERT(*klass == nullptr);
100 
101     if (ctx->IsBootContext()) {
102         // No need to load by managed code, even if error occurred during loading.
103         *klass = LoadFromBootContext(descriptor, errorHandler, ctx);
104         return true;
105     }
106 
107     // All non-boot contexts are represented by EtsClassLinkerContext.
108     auto *etsLinkerContext = reinterpret_cast<EtsClassLinkerContext *>(ctx);
109     auto *runtimeLinker = etsLinkerContext->GetRuntimeLinker();
110     ASSERT(runtimeLinker != nullptr);
111     if (runtimeLinker->GetClass() != PlatformTypes()->coreAbcRuntimeLinker &&
112         runtimeLinker->GetClass() != PlatformTypes()->memoryRuntimeLinker) {
113         // Must call managed implementation.
114         return false;
115     }
116 
117     auto *abcRuntimeLinker = EtsAbcRuntimeLinker::FromEtsObject(runtimeLinker);
118     auto *parentLinker = abcRuntimeLinker->GetParentLinker();
119     ASSERT(parentLinker != nullptr);
120     auto *parentContext = parentLinker->GetClassLinkerContext();
121     if (!TryLoadingClassInChain(descriptor, errorHandler, parentContext, klass)) {
122         // Chain resolution failed, must call managed implementation.
123         ASSERT(*klass == nullptr);
124         return false;
125     }
126 
127     // No need to load by managed code, even if error occurred during loading.
128     if (*klass == nullptr && !errorHandler.HasError()) {
129         *klass = etsLinkerContext->FindAndLoadClass(descriptor, &errorHandler);
130     }
131     return true;
132 }
133 
134 /// @brief Walks through RuntimeLinker chain and enumerates all panda files
TryEnumeratePandaFilesInChain(const ClassLinkerContext * ctx,const std::function<bool (const panda_file::File &)> & cb)135 bool TryEnumeratePandaFilesInChain(const ClassLinkerContext *ctx,
136                                    const std::function<bool(const panda_file::File &)> &cb)
137 {
138     ASSERT(ctx != nullptr);
139 
140     if (ctx->IsBootContext()) {
141         ctx->EnumeratePandaFiles(cb);
142         return true;
143     }
144 
145     // All non-boot contexts are represented by EtsClassLinkerContext
146     auto *etsLinkerContext = reinterpret_cast<const EtsClassLinkerContext *>(ctx);
147     auto *runtimeLinker = etsLinkerContext->GetRuntimeLinker();
148     ASSERT(runtimeLinker != nullptr);
149     if (!runtimeLinker->IsInstanceOf(PlatformTypes()->coreAbcRuntimeLinker)) {
150         // Unexpected behavior, cannot walk through chain
151         return false;
152     }
153 
154     auto *abcRuntimeLinker = EtsAbcRuntimeLinker::FromEtsObject(runtimeLinker);
155     auto *parentLinker = abcRuntimeLinker->GetParentLinker();
156     ASSERT(parentLinker != nullptr);
157     auto *parentContext = parentLinker->GetClassLinkerContext();
158     if (!TryEnumeratePandaFilesInChain(parentContext, cb)) {
159         return false;
160     }
161 
162     parentContext->EnumeratePandaFiles(cb);
163     return true;
164 }
165 
166 }  // namespace
167 
LoadClass(const uint8_t * descriptor,bool needCopyDescriptor,ClassLinkerErrorHandler * errorHandler)168 Class *EtsClassLinkerContext::LoadClass(const uint8_t *descriptor, [[maybe_unused]] bool needCopyDescriptor,
169                                         ClassLinkerErrorHandler *errorHandler)
170 {
171     auto *klass = FindClass(descriptor);
172     if (klass != nullptr) {
173         return klass;
174     }
175 
176     ASSERT(!PandaVM::GetCurrent()->GetGC()->IsGCRunning() || PandaVM::GetCurrent()->GetMutatorLock()->HasLock());
177 
178     // Try loading the class without invoking managed code.
179     auto succeeded = TryLoadingClassFromNative(descriptor, errorHandler, &klass);
180     if (succeeded) {
181         return klass;
182     }
183 
184     auto *coro = EtsCoroutine::GetCurrent();
185     if (coro == nullptr || !coro->IsManagedCode()) {
186         // Do not invoke managed code from non-managed threads (e.g. JIT or AOT).
187         ReportClassNotFound(descriptor, errorHandler);
188         return nullptr;
189     }
190 
191     auto clsName = ClassHelper::GetName(descriptor);
192     auto etsClsName = EtsString::CreateFromMUtf8(clsName.c_str());
193     const auto *runtimeLinker = GetRuntimeLinker();
194     ASSERT(runtimeLinker != nullptr);
195     ASSERT(etsClsName != nullptr);
196     std::array args {Value(runtimeLinker->GetCoreType()), Value(etsClsName->GetCoreType()), Value(ETS_TRUE)};
197 
198     auto *loadClass = runtimeLinker->GetClass()->GetInstanceMethod("loadClass", "Lstd/core/String;Z:Lstd/core/Class;");
199     ASSERT(loadClass != nullptr);
200     auto res = loadClass->GetPandaMethod()->Invoke(coro, args.data());
201     if (!coro->HasPendingException()) {
202         return Class::FromClassObject(res.GetAs<ObjectHeader *>());
203     }
204 
205     ReportClassNotFound(descriptor, errorHandler);
206     return nullptr;
207 }
208 
EnumeratePandaFilesImpl(const std::function<bool (const panda_file::File &)> & cb) const209 void EtsClassLinkerContext::EnumeratePandaFilesImpl(const std::function<bool(const panda_file::File &)> &cb) const
210 {
211     ASSERT(PandaEtsVM::GetCurrent()->GetMutatorLock()->HasLock());
212     auto *runtimeLinker = GetRuntimeLinker();
213     ASSERT(runtimeLinker != nullptr);
214     if (!runtimeLinker->IsInstanceOf(PlatformTypes()->coreAbcRuntimeLinker)) {
215         return;
216     }
217     auto *contextAbcFiles = EtsAbcRuntimeLinker::FromEtsObject(runtimeLinker)->GetAbcFiles();
218     ASSERT(contextAbcFiles != nullptr);
219     for (size_t i = 0, end = contextAbcFiles->GetLength(); i < end; ++i) {
220         auto *pf = EtsAbcFile::FromEtsObject(contextAbcFiles->Get(i))->GetPandaFile();
221         if (!cb(*pf)) {
222             break;
223         }
224     }
225 }
226 
EnumeratePandaFiles(const std::function<bool (const panda_file::File &)> & cb) const227 void EtsClassLinkerContext::EnumeratePandaFiles(const std::function<bool(const panda_file::File &)> &cb) const
228 {
229     EnumeratePandaFilesImpl(cb);
230 }
231 
EnumeratePandaFilesInChain(const std::function<bool (const panda_file::File &)> & cb) const232 void EtsClassLinkerContext::EnumeratePandaFilesInChain(const std::function<bool(const panda_file::File &)> &cb) const
233 {
234     [[maybe_unused]] bool succeeded = TryEnumeratePandaFilesInChain(this, cb);
235     // `RuntimeLinker` chain must be always traversed correctly,
236     // because `AbcRuntimeLinker` is the only base class for all user-defined linkers
237     ASSERT(succeeded);
238 }
239 
FindAndLoadClass(const uint8_t * descriptor,ClassLinkerErrorHandler * errorHandler)240 Class *EtsClassLinkerContext::FindAndLoadClass(const uint8_t *descriptor, ClassLinkerErrorHandler *errorHandler)
241 {
242     Class *klass = nullptr;
243     EnumeratePandaFilesImpl([this, &klass, descriptor, errorHandler](const auto &pf) {
244         auto classId = pf.GetClassId(descriptor);
245         if (!classId.IsValid() || pf.IsExternal(classId)) {
246             return true;
247         }
248         klass = Runtime::GetCurrent()->GetClassLinker()->LoadClass(pf, classId, this, errorHandler);
249         return false;
250     });
251     return klass;
252 }
253 
TryLoadingClassFromNative(const uint8_t * descriptor,ClassLinkerErrorHandler * errorHandler,Class ** klass)254 bool EtsClassLinkerContext::TryLoadingClassFromNative(const uint8_t *descriptor, ClassLinkerErrorHandler *errorHandler,
255                                                       Class **klass)
256 {
257     ASSERT(klass != nullptr);
258     ASSERT(*klass == nullptr);
259 
260     DecoratorErrorHandler handler(errorHandler);
261     auto succeeded = TryLoadingClassInChain(descriptor, handler, this, klass);
262     if (handler.HasError()) {
263         // Report errors occurred during class loading
264         ASSERT(*klass == nullptr);
265         handler.PropagateError();
266     } else if (succeeded && *klass == nullptr) {
267         ReportClassNotFound(descriptor, errorHandler);
268     }
269     return succeeded;
270 }
271 
272 }  // namespace ark::ets
273