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 #pragma clang diagnostic ignored "-Wgnu-label-as-value"
42 #elif defined(__GNUC__)
43 #pragma GCC diagnostic push
44 #pragma GCC diagnostic ignored "-Wpedantic"
45 #endif
46
Launch(EtsCoroutine * currentCoro,Method * method,const EtsHandle<EtsPromise> & promiseHandle,PandaVector<Value> && args)47 static inline bool Launch(EtsCoroutine *currentCoro, Method *method, const EtsHandle<EtsPromise> &promiseHandle,
48 PandaVector<Value> &&args)
49 {
50 ASSERT(currentCoro != nullptr);
51 PandaEtsVM *etsVm = currentCoro->GetPandaVM();
52 auto *coroManager = currentCoro->GetCoroutineManager();
53 auto promiseRef = etsVm->GetGlobalObjectStorage()->Add(promiseHandle.GetPtr(), mem::Reference::ObjectType::WEAK);
54 auto evt = Runtime::GetCurrent()->GetInternalAllocator()->New<CompletionEvent>(promiseRef, coroManager);
55 promiseHandle.GetPtr()->SetEventPtr(evt);
56 // create the coro and put it to the ready queue
57 auto *coro = currentCoro->GetCoroutineManager()->Launch(evt, method, std::move(args), CoroutineLaunchMode::DEFAULT);
58 if (UNLIKELY(coro == nullptr)) {
59 // OOM
60 promiseHandle.GetPtr()->SetEventPtr(nullptr);
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 if (thisObj != nullptr) {
74 ASSERT(!method->IsStatic());
75 // Add this for virtual call
76 values.push_back(Value(thisObj));
77 } else {
78 ASSERT(method->IsStatic());
79 }
80 arch::ArgReaderStack<RUNTIME_ARCH> argReader(reinterpret_cast<uint8_t *>(args));
81 arch::ValueWriter writer(&values);
82 ARCH_COPY_METHOD_ARGS(method, argReader, writer);
83
84 auto *currentCoro = EtsCoroutine::GetCurrent();
85 [[maybe_unused]] EtsHandleScope scope(currentCoro);
86 EtsHandle<EtsPromise> promiseHandle(currentCoro, promise);
87 bool successfulLaunch = Launch(currentCoro, method, promiseHandle, std::move(values));
88 if (UNLIKELY(!successfulLaunch)) {
89 HandlePendingException();
90 UNREACHABLE();
91 }
92 }
93
CreateLaunchStaticCoroutineEntrypoint(Method * method,ObjectHeader * obj,uint64_t * args)94 extern "C" void CreateLaunchStaticCoroutineEntrypoint(Method *method, ObjectHeader *obj, uint64_t *args)
95 {
96 LaunchCoroutine(method, obj, args, nullptr);
97 }
98
CreateLaunchVirtualCoroutineEntrypoint(Method * method,ObjectHeader * obj,uint64_t * args,ObjectHeader * thisObj)99 extern "C" void CreateLaunchVirtualCoroutineEntrypoint(Method *method, ObjectHeader *obj, uint64_t *args,
100 ObjectHeader *thisObj)
101 {
102 LaunchCoroutine(method, obj, args, thisObj);
103 }
104
105 template <BytecodeInstruction::Format FORMAT>
LaunchFromInterpreterImpl(Method * method,Frame * frame,const uint8_t * pc)106 ObjectHeader *LaunchFromInterpreterImpl(Method *method, Frame *frame, const uint8_t *pc)
107 {
108 EtsPromise *promise = EtsPromise::Create();
109 if (UNLIKELY(promise == nullptr)) {
110 return nullptr;
111 }
112
113 auto numArgs = method->GetNumArgs();
114 auto args = PandaVector<Value> {numArgs};
115 auto frameHandler = StaticFrameHandler(frame);
116 auto vregIter = interpreter::VRegisterIterator<FORMAT> {BytecodeInstruction(pc), frame};
117 for (decltype(numArgs) i = 0; i < numArgs; ++i) {
118 args[i] = Value::FromVReg(frameHandler.GetVReg(vregIter.GetVRegIdx(i)));
119 }
120
121 auto *currentCoro = EtsCoroutine::GetCurrent();
122 [[maybe_unused]] EtsHandleScope scope(currentCoro);
123 EtsHandle<EtsPromise> promiseHandle(currentCoro, promise);
124 bool successfulLaunch = Launch(currentCoro, method, promiseHandle, std::move(args));
125 if (UNLIKELY(!successfulLaunch)) {
126 return nullptr;
127 }
128 frame->GetAccAsVReg().SetReference(promiseHandle.GetPtr());
129 return promiseHandle.GetPtr();
130 }
131
LaunchFromInterpreterShort(Method * method,Frame * frame,const uint8_t * pc)132 extern "C" ObjectHeader *LaunchFromInterpreterShort(Method *method, Frame *frame, const uint8_t *pc)
133 {
134 return LaunchFromInterpreterImpl<BytecodeInstruction::Format::PREF_V4_V4_ID16>(method, frame, pc);
135 }
136
LaunchFromInterpreterLong(Method * method,Frame * frame,const uint8_t * pc)137 extern "C" ObjectHeader *LaunchFromInterpreterLong(Method *method, Frame *frame, const uint8_t *pc)
138 {
139 return LaunchFromInterpreterImpl<BytecodeInstruction::Format::PREF_V4_V4_V4_V4_ID16>(method, frame, pc);
140 }
141
LaunchFromInterpreterRange(Method * method,Frame * frame,const uint8_t * pc)142 extern "C" ObjectHeader *LaunchFromInterpreterRange(Method *method, Frame *frame, const uint8_t *pc)
143 {
144 return LaunchFromInterpreterImpl<BytecodeInstruction::Format::PREF_V8_ID16>(method, frame, pc);
145 }
146
LookupFieldByNameEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)147 extern "C" Field *LookupFieldByNameEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
148 Method *caller, const uint8_t *pc)
149 {
150 auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
151 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
152 auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
153 auto *field = klass->LookupFieldByName(rawField->GetName());
154 if (field != nullptr) {
155 *entry = {pc, caller, static_cast<void *>(field)};
156 }
157 return field;
158 }
159
160 constexpr static uint64_t METHOD_FLAG_MASK = 0x00000001;
161
162 template <panda_file::Type::TypeId FORMAT>
LookupGetterByNameEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)163 Method *LookupGetterByNameEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id, Method *caller,
164 const uint8_t *pc)
165 {
166 auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
167 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
168 auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
169 auto *method = klass->LookupGetterByName<FORMAT>(rawField->GetName());
170 auto mUint = reinterpret_cast<uint64_t>(method);
171 if (method != nullptr) {
172 *entry = {pc, caller, reinterpret_cast<Method *>(mUint | METHOD_FLAG_MASK)};
173 }
174 return method;
175 }
176
177 template <panda_file::Type::TypeId FORMAT>
LookupSetterByNameEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)178 Method *LookupSetterByNameEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id, Method *caller,
179 const uint8_t *pc)
180 {
181 auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
182 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
183 auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
184 auto *method = klass->LookupSetterByName<FORMAT>(rawField->GetName());
185 auto mUint = reinterpret_cast<uint64_t>(method);
186 if (method != nullptr) {
187 *entry = {pc, caller, reinterpret_cast<Method *>(mUint | METHOD_FLAG_MASK)};
188 }
189 return method;
190 }
191
LookupGetterByNameShortEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)192 extern "C" Method *LookupGetterByNameShortEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
193 Method *caller, const uint8_t *pc)
194 {
195 return LookupGetterByNameEntrypoint<panda_file::Type::TypeId::I32>(entry, obj, id, caller, pc);
196 }
197
LookupGetterByNameLongEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)198 extern "C" Method *LookupGetterByNameLongEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
199 Method *caller, const uint8_t *pc)
200 {
201 return LookupGetterByNameEntrypoint<panda_file::Type::TypeId::I64>(entry, obj, id, caller, pc);
202 }
203
LookupGetterByNameObjEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)204 extern "C" Method *LookupGetterByNameObjEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
205 Method *caller, const uint8_t *pc)
206 {
207 return LookupGetterByNameEntrypoint<panda_file::Type::TypeId::REFERENCE>(entry, obj, id, caller, pc);
208 }
209
LookupSetterByNameShortEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)210 extern "C" Method *LookupSetterByNameShortEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
211 Method *caller, const uint8_t *pc)
212 {
213 return LookupSetterByNameEntrypoint<panda_file::Type::TypeId::I32>(entry, obj, id, caller, pc);
214 }
215
LookupSetterByNameLongEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)216 extern "C" Method *LookupSetterByNameLongEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
217 Method *caller, const uint8_t *pc)
218 {
219 return LookupSetterByNameEntrypoint<panda_file::Type::TypeId::I64>(entry, obj, id, caller, pc);
220 }
221
LookupSetterByNameObjEntrypoint(InterpreterCache::Entry * entry,ObjectHeader * obj,uint32_t id,Method * caller,const uint8_t * pc)222 extern "C" Method *LookupSetterByNameObjEntrypoint(InterpreterCache::Entry *entry, ObjectHeader *obj, uint32_t id,
223 Method *caller, const uint8_t *pc)
224 {
225 return LookupSetterByNameEntrypoint<panda_file::Type::TypeId::REFERENCE>(entry, obj, id, caller, pc);
226 }
227
ThrowEtsExceptionNoSuchGetterEntrypoint(ObjectHeader * obj,uint32_t id,Method * caller)228 extern "C" void ThrowEtsExceptionNoSuchGetterEntrypoint(ObjectHeader *obj, uint32_t id, Method *caller)
229 {
230 auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
231 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
232 auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
233 auto errorMsg = "Class " + ark::ConvertToString(klass->GetName()) + " does not have field and getter with name " +
234 utf::Mutf8AsCString(rawField->GetName().data);
235 ThrowEtsException(
236 EtsCoroutine::GetCurrent(),
237 utf::Mutf8AsCString(
238 Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS).GetNoSuchFieldErrorDescriptor()),
239 errorMsg);
240 }
241
ThrowEtsExceptionNoSuchSetterEntrypoint(ObjectHeader * obj,uint32_t id,Method * caller)242 extern "C" void ThrowEtsExceptionNoSuchSetterEntrypoint(ObjectHeader *obj, uint32_t id, Method *caller)
243 {
244 auto klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
245 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
246 auto rawField = classLinker->GetField(*caller, caller->GetClass()->ResolveFieldIndex(id));
247 auto errorMsg = "Class " + ark::ConvertToString(klass->GetName()) + " does not have field and setter with name " +
248 utf::Mutf8AsCString(rawField->GetName().data);
249 ThrowEtsException(
250 EtsCoroutine::GetCurrent(),
251 utf::Mutf8AsCString(
252 Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS).GetNoSuchFieldErrorDescriptor()),
253 errorMsg);
254 }
255
StringBuilderAppendLongEntrypoint(ObjectHeader * sb,int64_t v)256 extern "C" ObjectHeader *StringBuilderAppendLongEntrypoint(ObjectHeader *sb, int64_t v)
257 {
258 ASSERT(sb != nullptr);
259 return StringBuilderAppendLong(sb, v);
260 }
261
StringBuilderAppendCharEntrypoint(ObjectHeader * sb,uint16_t ch)262 extern "C" ObjectHeader *StringBuilderAppendCharEntrypoint(ObjectHeader *sb, uint16_t ch)
263 {
264 ASSERT(sb != nullptr);
265 return StringBuilderAppendChar(sb, ch);
266 }
267
StringBuilderAppendBoolEntrypoint(ObjectHeader * sb,uint8_t v)268 extern "C" ObjectHeader *StringBuilderAppendBoolEntrypoint(ObjectHeader *sb, uint8_t v)
269 {
270 ASSERT(sb != nullptr);
271 return StringBuilderAppendBool(sb, ToEtsBoolean(static_cast<bool>(v)));
272 }
273
StringBuilderAppendStringEntrypoint(ObjectHeader * sb,ObjectHeader * str)274 extern "C" ObjectHeader *StringBuilderAppendStringEntrypoint(ObjectHeader *sb, ObjectHeader *str)
275 {
276 ASSERT(sb != nullptr);
277 return StringBuilderAppendString(sb, reinterpret_cast<EtsString *>(str));
278 }
279
StringBuilderAppendNullStringEntrypoint(ObjectHeader * sb)280 extern "C" ObjectHeader *StringBuilderAppendNullStringEntrypoint(ObjectHeader *sb)
281 {
282 ASSERT(sb != nullptr);
283 return StringBuilderAppendNullString(sb);
284 }
285
IsClassValueTypedEntrypoint(Class * cls)286 extern "C" bool IsClassValueTypedEntrypoint(Class *cls)
287 {
288 return EtsClass::FromRuntimeClass(cls)->IsValueTyped();
289 }
290
CompareETSValueTypedEntrypoint(ManagedThread * thread,ObjectHeader * obj1,ObjectHeader * obj2)291 extern "C" bool CompareETSValueTypedEntrypoint(ManagedThread *thread, ObjectHeader *obj1, ObjectHeader *obj2)
292 {
293 auto coro = EtsCoroutine::CastFromThread(thread);
294 auto eobj1 = EtsObject::FromCoreType(obj1);
295 auto eobj2 = EtsObject::FromCoreType(obj2);
296 return EtsValueTypedEquals(coro, eobj1, eobj2);
297 }
298
StringBuilderToStringEntrypoint(ObjectHeader * sb)299 extern "C" ObjectHeader *StringBuilderToStringEntrypoint(ObjectHeader *sb)
300 {
301 ASSERT(sb != nullptr);
302 return StringBuilderToString(sb)->GetCoreType();
303 }
304
DoubleToStringDecimalEntrypoint(ObjectHeader * cache,uint64_t number)305 extern "C" ObjectHeader *DoubleToStringDecimalEntrypoint(ObjectHeader *cache, uint64_t number)
306 {
307 ASSERT(cache != nullptr);
308 return DoubleToStringCache::FromCoreType(cache)
309 ->GetOrCache(EtsCoroutine::GetCurrent(), bit_cast<double>(number))
310 ->GetCoreType();
311 }
312
DoubleToStringDecimalStoreEntrypoint(ObjectHeader * elem,uint64_t number,uint64_t cached)313 extern "C" ObjectHeader *DoubleToStringDecimalStoreEntrypoint(ObjectHeader *elem, uint64_t number, uint64_t cached)
314 {
315 auto *cache = PandaEtsVM::GetCurrent()->GetDoubleToStringCache();
316 ASSERT(cache != nullptr);
317 return cache->CacheAndGetNoCheck(EtsCoroutine::GetCurrent(), bit_cast<double>(number), elem, cached)->GetCoreType();
318 }
319
DoubleToStringDecimalNoCacheEntrypoint(uint64_t number)320 extern "C" ObjectHeader *DoubleToStringDecimalNoCacheEntrypoint(uint64_t number)
321 {
322 return DoubleToStringCache::GetNoCache(bit_cast<double>(number))->GetCoreType();
323 }
324
325 } // namespace ark::ets
326