• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022-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_entrypoints.h"
17 
18 #include "include/object_header.h"
19 #include "libpandafile/shorty_iterator.h"
20 #include "plugins/ets/runtime/ets_coroutine.h"
21 #include "plugins/ets/runtime/ets_runtime_interface.h"
22 #include "plugins/ets/runtime/ets_vm.h"
23 #include "plugins/ets/runtime/ets_handle_scope.h"
24 #include "plugins/ets/runtime/ets_handle.h"
25 #include "plugins/ets/runtime/intrinsics/helpers/ets_to_string_cache.h"
26 #include "plugins/ets/runtime/types/ets_promise.h"
27 #include "plugins/ets/runtime/ets_stubs-inl.h"
28 #include "plugins/ets/runtime/ets_exceptions.h"
29 #include "plugins/ets/runtime/types/ets_string_builder.h"
30 #include "runtime/arch/helpers.h"
31 #include "runtime/interpreter/vregister_iterator.h"
32 #include "plugins/ets/runtime/ets_class_linker_extension.h"
33 #include "plugins/ets/runtime/types/ets_box_primitive.h"
34 
35 namespace ark::ets {
36 
37 using TypeId = panda_file::Type::TypeId;
38 
39 #if defined(__clang__)
40 #pragma clang diagnostic push
41 // CC-OFFNXT(warning_suppression) gcc false positive
42 #pragma clang diagnostic ignored "-Wgnu-label-as-value"
43 #elif defined(__GNUC__)
44 #pragma GCC diagnostic push
45 // CC-OFFNXT(warning_suppression) gcc false positive
46 #pragma GCC diagnostic ignored "-Wpedantic"
47 #endif
48 
Launch(EtsCoroutine * currentCoro,Method * method,const EtsHandle<EtsPromise> & promiseHandle,PandaVector<Value> && args)49 static inline bool Launch(EtsCoroutine *currentCoro, Method *method, const EtsHandle<EtsPromise> &promiseHandle,
50                           PandaVector<Value> &&args)
51 {
52     ASSERT(currentCoro != nullptr);
53     PandaEtsVM *etsVm = currentCoro->GetPandaVM();
54     auto *coroManager = currentCoro->GetCoroutineManager();
55     auto promiseRef = etsVm->GetGlobalObjectStorage()->Add(promiseHandle.GetPtr(), mem::Reference::ObjectType::GLOBAL);
56     auto evt = Runtime::GetCurrent()->GetInternalAllocator()->New<CompletionEvent>(promiseRef, coroManager);
57     // create the coro and put it to the ready queue
58     auto *coro = currentCoro->GetCoroutineManager()->Launch(evt, method, std::move(args), CoroutineLaunchMode::DEFAULT);
59     if (UNLIKELY(coro == nullptr)) {
60         // OOM
61         Runtime::GetCurrent()->GetInternalAllocator()->Delete(evt);
62         return false;
63     }
64     return true;
65 }
66 
LaunchCoroutine(Method * method,ObjectHeader * obj,uint64_t * args,ObjectHeader * thisObj)67 void LaunchCoroutine(Method *method, ObjectHeader *obj, uint64_t *args, ObjectHeader *thisObj)
68 {
69     auto *promise = reinterpret_cast<EtsPromise *>(obj);
70     ASSERT(promise != nullptr);
71 
72     PandaVector<Value> values;
73     arch::ArgReaderStack<RUNTIME_ARCH> argReader(reinterpret_cast<uint8_t *>(args));
74     arch::ValueWriter writer(&values);
75     if (thisObj != nullptr) {
76         ASSERT(!method->IsStatic());
77         // Add this for virtual call
78         values.push_back(Value(thisObj));
79     } else {
80         if (!method->IsStatic()) {
81             auto pThisObj = const_cast<ObjectHeader **>((argReader).template ReadPtr<ObjectHeader *>());
82             values.push_back(Value(*pThisObj));
83         }
84     }
85     ARCH_COPY_METHOD_ARGS(method, argReader, writer);
86 
87     auto *currentCoro = EtsCoroutine::GetCurrent();
88     [[maybe_unused]] EtsHandleScope scope(currentCoro);
89     EtsHandle<EtsPromise> promiseHandle(currentCoro, promise);
90     // NOTE(panferovi): should be fixed in #19443
91     ASSERT(promiseHandle->GetMutex(currentCoro) == nullptr);
92     ASSERT(promiseHandle->GetEvent(currentCoro) == nullptr);
93     // NOTE(panferovi): issue with raw args and thisObj??
94     auto *mutex = EtsMutex::Create(currentCoro);
95     promiseHandle->SetMutex(currentCoro, mutex);
96     auto *event = EtsEvent::Create(currentCoro);
97     promiseHandle->SetEvent(currentCoro, event);
98     bool successfulLaunch = Launch(currentCoro, method, promiseHandle, std::move(values));
99     if (UNLIKELY(!successfulLaunch)) {
100         HandlePendingException();
101         UNREACHABLE();
102     }
103 }
104 
CreateLaunchStaticCoroutineEntrypoint(Method * method,ObjectHeader * obj,uint64_t * args)105 extern "C" void CreateLaunchStaticCoroutineEntrypoint(Method *method, ObjectHeader *obj, uint64_t *args)
106 {
107     LaunchCoroutine(method, obj, args, nullptr);
108 }
109 
CreateLaunchVirtualCoroutineEntrypoint(Method * method,ObjectHeader * obj,uint64_t * args,ObjectHeader * thisObj)110 extern "C" void CreateLaunchVirtualCoroutineEntrypoint(Method *method, ObjectHeader *obj, uint64_t *args,
111                                                        ObjectHeader *thisObj)
112 {
113     LaunchCoroutine(method, obj, args, thisObj);
114 }
115 
116 template <BytecodeInstruction::Format FORMAT>
LaunchFromInterpreterImpl(Method * method,Frame * frame,const uint8_t * pc)117 ObjectHeader *LaunchFromInterpreterImpl(Method *method, Frame *frame, const uint8_t *pc)
118 {
119     EtsPromise *promise = EtsPromise::Create();
120     if (UNLIKELY(promise == nullptr)) {
121         return nullptr;
122     }
123 
124     auto numArgs = method->GetNumArgs();
125     auto args = PandaVector<Value> {numArgs};
126     auto frameHandler = StaticFrameHandler(frame);
127     auto vregIter = interpreter::VRegisterIterator<FORMAT> {BytecodeInstruction(pc), frame};
128     for (decltype(numArgs) i = 0; i < numArgs; ++i) {
129         args[i] = Value::FromVReg(frameHandler.GetVReg(vregIter.GetVRegIdx(i)));
130     }
131 
132     auto *currentCoro = EtsCoroutine::GetCurrent();
133     [[maybe_unused]] EtsHandleScope scope(currentCoro);
134     EtsHandle<EtsPromise> promiseHandle(currentCoro, promise);
135     bool successfulLaunch = Launch(currentCoro, method, promiseHandle, std::move(args));
136     if (UNLIKELY(!successfulLaunch)) {
137         return nullptr;
138     }
139     frame->GetAccAsVReg().SetReference(promiseHandle.GetPtr());
140     return promiseHandle.GetPtr();
141 }
142 
LaunchFromInterpreterShort(Method * method,Frame * frame,const uint8_t * pc)143 extern "C" ObjectHeader *LaunchFromInterpreterShort(Method *method, Frame *frame, const uint8_t *pc)
144 {
145     return LaunchFromInterpreterImpl<BytecodeInstruction::Format::PREF_V4_V4_ID16>(method, frame, pc);
146 }
147 
LaunchFromInterpreterLong(Method * method,Frame * frame,const uint8_t * pc)148 extern "C" ObjectHeader *LaunchFromInterpreterLong(Method *method, Frame *frame, const uint8_t *pc)
149 {
150     return LaunchFromInterpreterImpl<BytecodeInstruction::Format::PREF_V4_V4_V4_V4_ID16>(method, frame, pc);
151 }
152 
LaunchFromInterpreterRange(Method * method,Frame * frame,const uint8_t * pc)153 extern "C" ObjectHeader *LaunchFromInterpreterRange(Method *method, Frame *frame, const uint8_t *pc)
154 {
155     return LaunchFromInterpreterImpl<BytecodeInstruction::Format::PREF_V8_ID16>(method, frame, pc);
156 }
157 
LookupFieldByNameEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)158 extern "C" Field *LookupFieldByNameEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
159                                               Method *caller, const uint8_t *pc)
160 {
161     auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
162     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
163     auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
164     auto *field = klass->LookupFieldByName(rawField->GetName());
165     if (field != nullptr) {
166         *entry = {pc, caller, static_cast<void *>(field)};
167     }
168     return field;
169 }
170 
171 constexpr static uint64_t METHOD_FLAG_MASK = 0x00000001;
172 
173 template <panda_file::Type::TypeId FORMAT>
LookupGetterByNameEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)174 Method *LookupGetterByNameEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id, Method *caller,
175                                      const uint8_t *pc)
176 {
177     auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
178     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
179     auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
180     auto *method = klass->LookupGetterByName<FORMAT>(rawField->GetName());
181     auto mUint = reinterpret_cast<uint64_t>(method);
182     if (method != nullptr) {
183         *entry = {pc, caller, reinterpret_cast<Method *>(mUint | METHOD_FLAG_MASK)};
184     }
185     return method;
186 }
187 
188 template <panda_file::Type::TypeId FORMAT>
LookupSetterByNameEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)189 Method *LookupSetterByNameEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id, Method *caller,
190                                      const uint8_t *pc)
191 {
192     auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
193     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
194     auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
195     auto *method = klass->LookupSetterByName<FORMAT>(rawField->GetName());
196     auto mUint = reinterpret_cast<uint64_t>(method);
197     if (method != nullptr) {
198         *entry = {pc, caller, reinterpret_cast<Method *>(mUint | METHOD_FLAG_MASK)};
199     }
200     return method;
201 }
202 
LookupGetterByNameShortEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)203 extern "C" Method *LookupGetterByNameShortEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
204                                                      Method *caller, const uint8_t *pc)
205 {
206     return LookupGetterByNameEntrypoint<panda_file::Type::TypeId::I32>(entry, obj, id, caller, pc);
207 }
208 
LookupGetterByNameLongEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)209 extern "C" Method *LookupGetterByNameLongEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
210                                                     Method *caller, const uint8_t *pc)
211 {
212     return LookupGetterByNameEntrypoint<panda_file::Type::TypeId::I64>(entry, obj, id, caller, pc);
213 }
214 
LookupGetterByNameObjEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)215 extern "C" Method *LookupGetterByNameObjEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
216                                                    Method *caller, const uint8_t *pc)
217 {
218     return LookupGetterByNameEntrypoint<panda_file::Type::TypeId::REFERENCE>(entry, obj, id, caller, pc);
219 }
220 
LookupSetterByNameShortEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)221 extern "C" Method *LookupSetterByNameShortEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
222                                                      Method *caller, const uint8_t *pc)
223 {
224     return LookupSetterByNameEntrypoint<panda_file::Type::TypeId::I32>(entry, obj, id, caller, pc);
225 }
226 
LookupSetterByNameLongEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)227 extern "C" Method *LookupSetterByNameLongEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
228                                                     Method *caller, const uint8_t *pc)
229 {
230     return LookupSetterByNameEntrypoint<panda_file::Type::TypeId::I64>(entry, obj, id, caller, pc);
231 }
232 
LookupSetterByNameObjEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)233 extern "C" Method *LookupSetterByNameObjEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
234                                                    Method *caller, const uint8_t *pc)
235 {
236     return LookupSetterByNameEntrypoint<panda_file::Type::TypeId::REFERENCE>(entry, obj, id, caller, pc);
237 }
238 
ThrowEtsExceptionNoSuchGetterEntrypoint(ObjectHeader * obj,uint32_t id,Method * caller)239 extern "C" void ThrowEtsExceptionNoSuchGetterEntrypoint(ObjectHeader *obj, uint32_t id, Method *caller)
240 {
241     auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
242     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
243     auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
244     auto errorMsg = "Class " + ark::ConvertToString(klass->GetName()) + " does not have field and getter with name " +
245                     utf::Mutf8AsCString(rawField->GetName().data);
246     ThrowEtsException(
247         EtsCoroutine::GetCurrent(),
248         utf::Mutf8AsCString(
249             Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS).GetNoSuchFieldErrorDescriptor()),
250         errorMsg);
251 }
252 
ThrowEtsExceptionNoSuchSetterEntrypoint(ObjectHeader * obj,uint32_t id,Method * caller)253 extern "C" void ThrowEtsExceptionNoSuchSetterEntrypoint(ObjectHeader *obj, uint32_t id, Method *caller)
254 {
255     auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
256     auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
257     auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
258     auto errorMsg = "Class " + ark::ConvertToString(klass->GetName()) + " does not have field and setter with name " +
259                     utf::Mutf8AsCString(rawField->GetName().data);
260     ThrowEtsException(
261         EtsCoroutine::GetCurrent(),
262         utf::Mutf8AsCString(
263             Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS).GetNoSuchFieldErrorDescriptor()),
264         errorMsg);
265 }
266 
StringBuilderAppendLongEntrypoint(ObjectHeader * sb,int64_t v)267 extern "C" ObjectHeader *StringBuilderAppendLongEntrypoint(ObjectHeader *sb, int64_t v)
268 {
269     ASSERT(sb != nullptr);
270     return StringBuilderAppendLong(sb, v);
271 }
272 
StringBuilderAppendCharEntrypoint(ObjectHeader * sb,uint16_t ch)273 extern "C" ObjectHeader *StringBuilderAppendCharEntrypoint(ObjectHeader *sb, uint16_t ch)
274 {
275     ASSERT(sb != nullptr);
276     return StringBuilderAppendChar(sb, ch);
277 }
278 
StringBuilderAppendBoolEntrypoint(ObjectHeader * sb,uint8_t v)279 extern "C" ObjectHeader *StringBuilderAppendBoolEntrypoint(ObjectHeader *sb, uint8_t v)
280 {
281     ASSERT(sb != nullptr);
282     return StringBuilderAppendBool(sb, ToEtsBoolean(static_cast<bool>(v)));
283 }
284 
StringBuilderAppendStringEntrypoint(ObjectHeader * sb,ObjectHeader * str)285 extern "C" ObjectHeader *StringBuilderAppendStringEntrypoint(ObjectHeader *sb, ObjectHeader *str)
286 {
287     ASSERT(sb != nullptr);
288     return StringBuilderAppendString(sb, reinterpret_cast<EtsString *>(str));
289 }
290 
StringBuilderAppendString2Entrypoint(ObjectHeader * sb,ObjectHeader * str0,ObjectHeader * str1)291 extern "C" ObjectHeader *StringBuilderAppendString2Entrypoint(ObjectHeader *sb, ObjectHeader *str0, ObjectHeader *str1)
292 {
293     ASSERT(sb != nullptr);
294     return StringBuilderAppendStrings(sb, reinterpret_cast<EtsString *>(str0), reinterpret_cast<EtsString *>(str1));
295 }
296 
StringBuilderAppendString3Entrypoint(ObjectHeader * sb,ObjectHeader * str0,ObjectHeader * str1,ObjectHeader * str2)297 extern "C" ObjectHeader *StringBuilderAppendString3Entrypoint(ObjectHeader *sb, ObjectHeader *str0, ObjectHeader *str1,
298                                                               ObjectHeader *str2)
299 {
300     ASSERT(sb != nullptr);
301     return StringBuilderAppendStrings(sb, reinterpret_cast<EtsString *>(str0), reinterpret_cast<EtsString *>(str1),
302                                       reinterpret_cast<EtsString *>(str2));
303 }
304 
StringBuilderAppendString4Entrypoint(ObjectHeader * sb,ObjectHeader * str0,ObjectHeader * str1,ObjectHeader * str2,ObjectHeader * str3)305 extern "C" ObjectHeader *StringBuilderAppendString4Entrypoint(ObjectHeader *sb, ObjectHeader *str0, ObjectHeader *str1,
306                                                               ObjectHeader *str2, ObjectHeader *str3)
307 {
308     ASSERT(sb != nullptr);
309     return StringBuilderAppendStrings(sb, reinterpret_cast<EtsString *>(str0), reinterpret_cast<EtsString *>(str1),
310                                       reinterpret_cast<EtsString *>(str2), reinterpret_cast<EtsString *>(str3));
311 }
312 
StringBuilderAppendNullStringEntrypoint(ObjectHeader * sb)313 extern "C" ObjectHeader *StringBuilderAppendNullStringEntrypoint(ObjectHeader *sb)
314 {
315     ASSERT(sb != nullptr);
316     return StringBuilderAppendNullString(sb);
317 }
318 
IsClassValueTypedEntrypoint(Class * cls)319 extern "C" bool IsClassValueTypedEntrypoint(Class *cls)
320 {
321     return EtsClass::FromRuntimeClass(cls)->IsValueTyped();
322 }
323 
CompareETSValueTypedEntrypoint(ManagedThread * thread,ObjectHeader * obj1,ObjectHeader * obj2)324 extern "C" bool CompareETSValueTypedEntrypoint(ManagedThread *thread, ObjectHeader *obj1, ObjectHeader *obj2)
325 {
326     auto coro = EtsCoroutine::CastFromThread(thread);
327     auto eobj1 = EtsObject::FromCoreType(obj1);
328     auto eobj2 = EtsObject::FromCoreType(obj2);
329     return EtsValueTypedEquals(coro, eobj1, eobj2);
330 }
331 
StringBuilderToStringEntrypoint(ObjectHeader * sb)332 extern "C" ObjectHeader *StringBuilderToStringEntrypoint(ObjectHeader *sb)
333 {
334     ASSERT(sb != nullptr);
335     return StringBuilderToString(sb)->GetCoreType();
336 }
337 
DoubleToStringDecimalEntrypoint(ObjectHeader * cache,uint64_t number)338 extern "C" ObjectHeader *DoubleToStringDecimalEntrypoint(ObjectHeader *cache, uint64_t number)
339 {
340     ASSERT(cache != nullptr);
341     return DoubleToStringCache::FromCoreType(cache)
342         ->GetOrCache(EtsCoroutine::GetCurrent(), bit_cast<double>(number))
343         ->GetCoreType();
344 }
345 
DoubleToStringDecimalStoreEntrypoint(ObjectHeader * elem,uint64_t number,uint64_t cached)346 extern "C" ObjectHeader *DoubleToStringDecimalStoreEntrypoint(ObjectHeader *elem, uint64_t number, uint64_t cached)
347 {
348     auto *cache = PandaEtsVM::GetCurrent()->GetDoubleToStringCache();
349     ASSERT(cache != nullptr);
350     return cache->CacheAndGetNoCheck(EtsCoroutine::GetCurrent(), bit_cast<double>(number), elem, cached)->GetCoreType();
351 }
352 
DoubleToStringDecimalNoCacheEntrypoint(uint64_t number)353 extern "C" ObjectHeader *DoubleToStringDecimalNoCacheEntrypoint(uint64_t number)
354 {
355     return DoubleToStringCache::GetNoCache(bit_cast<double>(number))->GetCoreType();
356 }
357 
358 }  // namespace ark::ets
359