• 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 #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