1 /**
2 * Copyright (c) 2021-2022 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/napi/ets_napi_helpers.h"
17
18 #include "libpandafile/shorty_iterator.h"
19 #include "macros.h"
20 #include "plugins/ets/runtime/ets_coroutine.h"
21 #include "plugins/ets/runtime/mem/ets_reference.h"
22 #include "plugins/ets/runtime/napi/ets_napi.h"
23 #include "plugins/ets/runtime/types/ets_method.h"
24 #include "plugins/ets/runtime/types/ets_object.h"
25 #include "plugins/ets/runtime/types/ets_promise.h"
26 #include "plugins/ets/runtime/ets_handle_scope.h"
27 #include "plugins/ets/runtime/ets_handle.h"
28 #include "plugins/ets/runtime/ets_class_linker_extension.h"
29 #include "runtime/arch/helpers.h"
30 #include "runtime/include/managed_thread.h"
31 #include "runtime/include/method.h"
32 #include "runtime/include/runtime.h"
33 #include "runtime/include/runtime_notification.h"
34
35 #include <cstdint>
36
37 namespace panda::ets::napi {
38 namespace {
39 using Type = panda_file::Type;
40 using TypeId = panda_file::Type::TypeId;
41
42 using ExtArchTraits = arch::ExtArchTraits<RUNTIME_ARCH>;
43 using ArgReader = arch::ArgReader<RUNTIME_ARCH>;
44 using ArgCounter = arch::ArgCounter<RUNTIME_ARCH>;
45
46 class ArgWriter : public arch::ArgWriter<RUNTIME_ARCH> {
47 public:
ArgWriter(Span<uint8_t> * gprArgs,Span<uint8_t> * fprArgs,uint8_t * stackArgs)48 ArgWriter(Span<uint8_t> *gprArgs, Span<uint8_t> *fprArgs, uint8_t *stackArgs)
49 : arch::ArgWriter<RUNTIME_ARCH>(gprArgs, fprArgs, stackArgs)
50 {
51 }
52 ~ArgWriter() = default;
53
54 template <class T>
Write(T v)55 ALWAYS_INLINE typename std::enable_if_t<std::is_same<T, ObjectHeader **>::value, void> Write(T v)
56 {
57 arch::ArgWriter<RUNTIME_ARCH>::Write<EtsReference *>(
58 EtsReferenceStorage::NewEtsStackRef(reinterpret_cast<EtsObject **>(v)));
59 }
60
61 template <class T>
Write(T v)62 ALWAYS_INLINE typename std::enable_if_t<!std::is_same<T, ObjectHeader **>::value, void> Write(T v)
63 {
64 // Check T is not some kind of pointer to ObjectHeader
65 static_assert(!std::is_same_v<ObjectHeader, std::remove_cv_t<typename helpers::RemoveAllPointers<T>::type>>);
66 arch::ArgWriter<RUNTIME_ARCH>::Write(v);
67 }
68
69 NO_COPY_SEMANTIC(ArgWriter);
70 NO_MOVE_SEMANTIC(ArgWriter);
71 };
72 } // namespace
73
74 extern "C" void EtsNapiEntryPoint();
75
GetEtsNapiEntryPoint()76 const void *GetEtsNapiEntryPoint()
77 {
78 return reinterpret_cast<const void *>(EtsNapiEntryPoint);
79 }
80
81 extern "C" void EtsNapiCriticalNativeEntryPoint();
82
GetEtsNapiCriticalEntryPoint()83 const void *GetEtsNapiCriticalEntryPoint()
84 {
85 return reinterpret_cast<const void *>(EtsNapiCriticalNativeEntryPoint);
86 }
87
EtsNapiCalcStackArgsSpaceSize(Method * method,bool isCritical)88 extern "C" uint32_t EtsNapiCalcStackArgsSpaceSize(Method *method, bool isCritical)
89 {
90 ASSERT(method != nullptr);
91
92 ArgCounter counter;
93 if (!isCritical) {
94 counter.Count<EtsEnv *>(); // EtsEnv arg
95 counter.Count<ObjectHeader *>(); // class or this arg
96 }
97
98 panda_file::ShortyIterator it(method->GetShorty());
99 ++it; // Skip the return type
100 panda_file::ShortyIterator end;
101 while (it != end) {
102 Type type = *it++;
103 switch (type.GetId()) {
104 case TypeId::VOID:
105 case TypeId::INVALID:
106 case TypeId::TAGGED:
107 UNREACHABLE();
108 case TypeId::U1:
109 counter.Count<bool>();
110 break;
111 case TypeId::I8:
112 case TypeId::U8:
113 counter.Count<uint8_t>();
114 break;
115 case TypeId::I16:
116 case TypeId::U16:
117 counter.Count<uint16_t>();
118 break;
119 case TypeId::I32:
120 case TypeId::U32:
121 counter.Count<uint32_t>();
122 break;
123 case TypeId::F32:
124 counter.Count<float>();
125 break;
126 case TypeId::F64:
127 counter.Count<double>();
128 break;
129 case TypeId::I64:
130 case TypeId::U64:
131 counter.Count<uint64_t>();
132 break;
133 case TypeId::REFERENCE:
134 counter.Count<ObjectHeader *>();
135 break;
136 default:
137 UNREACHABLE();
138 break;
139 }
140 }
141
142 return counter.GetStackSpaceSize();
143 }
144
145 // Disable warning because the function uses ARCH_COPY_METHOD_ARGS macro.
146 // The macro uses computed goto
147 #if defined(__clang__)
148 #pragma clang diagnostic push
149 #pragma clang diagnostic ignored "-Wgnu-label-as-value"
150 #elif defined(__GNUC__)
151 #pragma GCC diagnostic push
152 #pragma GCC diagnostic ignored "-Wpedantic"
153 #endif
154
155 // input stack structure output stack structure
156 // +-------+ <- out_args +-------+
157 // | ... | | x0-x7 |
158 // +-------+ <- in_reg_args +-------+
159 // | x0-x7 | | d0-d7 |
160 // +-------+ +-------+
161 // | d0-d7 | | stack |
162 // +-------+ | arg 0 |
163 // | ... | +-------+
164 // +-------+ <- in_stack_args | ... |
165 // | stack | +-------+
166 // | arg 0 | | stack |
167 // +-------+ | arg N |
168 // | ... | +-------+
EtsNapiBeginCritical(Method * method,uint8_t * inRegsArgs,uint8_t * inStackArgs,uint8_t * outArgs,ManagedThread * thread)169 extern "C" void EtsNapiBeginCritical(Method *method, uint8_t *inRegsArgs, uint8_t *inStackArgs, uint8_t *outArgs,
170 ManagedThread *thread)
171 {
172 ASSERT(method->IsStatic());
173 ASSERT(thread == ManagedThread::GetCurrent());
174
175 Span<uint8_t> inGprArgs(inRegsArgs, ExtArchTraits::GP_ARG_NUM_BYTES);
176 Span<uint8_t> inFprArgs(inGprArgs.end(), ExtArchTraits::FP_ARG_NUM_BYTES);
177 ArgReader argReader(inGprArgs, inFprArgs, inStackArgs);
178
179 Span<uint8_t> outGprArgs(outArgs, ExtArchTraits::GP_ARG_NUM_BYTES);
180 Span<uint8_t> outFprArgs(outGprArgs.end(), ExtArchTraits::FP_ARG_NUM_BYTES);
181 auto outStackArgs = outFprArgs.end();
182 ArgWriter argWriter(&outGprArgs, &outFprArgs, outStackArgs);
183
184 argReader.Read<Method *>(); // Skip method
185
186 if (UNLIKELY(method->GetNativePointer() == nullptr)) {
187 auto coroutine = EtsCoroutine::CastFromThread(thread);
188 coroutine->GetPandaVM()->ResolveNativeMethod(method);
189 }
190
191 ARCH_COPY_METHOD_ARGS(method, argReader, argWriter);
192
193 Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, method);
194 }
195
196 // Input stack =======> Output stack
197 // 0xFFFF
198 // | | | |
199 // | Prev frame | | Prev frame |
200 // | ... | | ... |
201 // +------------------------+ +------------------------+
202 // | ... | | ... |
203 // | stack args | | stack args | <--------+
204 // | ... | | ... | |
205 // +---+---+----------------+ <- in_stack_args --> +----------------+---+---+ |
206 // | | | LR | | LR | | | |
207 // | | | FP | | FP | | | |
208 // | | | Method * | | Method * | | | |
209 // | | c | FLAGS | | FLAGS | c | | |
210 // | | f +----------------+ +----------------+ f | | |
211 // | | r | ... | | ... | r | | |
212 // | | a | locals | | locals | a | | |
213 // | | m | ... | | ... | m | | |
214 // | | e +----------------+ +----------------+ e | | |
215 // | N | | ... | | ... | | N | |
216 // | A | | callee saved | | callee saved | | A | |
217 // | P | | ... | | ... | | P | |
218 // | I +---+----------------+ +----------------+---+ I | |
219 // | | ... | | ... | | |
220 // | | float args | | float args | | |
221 // | f | ... | | ... | f | |
222 // | r +--------------------+ +--------------------+ r | |
223 // | a | ... | | ... | a | |
224 // | m | general args | | general args | m | <----+ |
225 // | e | ... | | ... | e | | |
226 // | | arg0|Method* | | arg0|class(opt) | | | |
227 // | +--------------------+ <-- in_regs_args --> +--------------------+ | | | References
228 // | | | | ... | | | | to ObjectHeader *s
229 // | | | | NAPI float args | | | | on the stack
230 // | | | | (on regs) | | | |
231 // | | | | ... | | | |
232 // | | | +--------------------+ | | |
233 // | | | | ... | | | |
234 // | | space for | | NAPI general args | | -----+ |
235 // | | NAPI args | | (on regs) | | |
236 // | | | | ... | | |
237 // | | | +--------------------+ | |
238 // | | | | ... | | |
239 // | | | | NAPI args | | ---------+
240 // | | | | (on stack) | |
241 // | | | | ... | |
242 // +---+--------------------+ <- out_stack_args -> +--------------------+---+
243 // | | | |
244 // 0x0000
EtsNapiBegin(Method * method,uint8_t * inRegsArgs,uint8_t * inStackArgs,uint8_t * outStackArgs,ManagedThread * thread)245 extern "C" uint8_t *EtsNapiBegin(Method *method, uint8_t *inRegsArgs, uint8_t *inStackArgs, uint8_t *outStackArgs,
246 ManagedThread *thread)
247 {
248 ASSERT(!method->IsSynchronized());
249 ASSERT(thread == ManagedThread::GetCurrent());
250
251 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
252 auto outRegsArgs = inRegsArgs - ExtArchTraits ::FP_ARG_NUM_BYTES - ExtArchTraits ::GP_ARG_NUM_BYTES;
253
254 ASSERT(outStackArgs <= outRegsArgs);
255 ASSERT(outRegsArgs < inRegsArgs);
256 ASSERT(inRegsArgs < inStackArgs);
257
258 Span<uint8_t> inGprArgs(inRegsArgs, ExtArchTraits::GP_ARG_NUM_BYTES);
259 Span<uint8_t> inFprArgs(inGprArgs.end(), ExtArchTraits::FP_ARG_NUM_BYTES);
260 ArgReader argReader(inGprArgs, inFprArgs, inStackArgs);
261
262 Span<uint8_t> outGprArgs(outRegsArgs, ExtArchTraits::GP_ARG_NUM_BYTES);
263 Span<uint8_t> outFprArgs(outGprArgs.end(), ExtArchTraits::FP_ARG_NUM_BYTES);
264 ArgWriter argWriter(&outGprArgs, &outFprArgs, outStackArgs);
265
266 argReader.Read<Method *>(); // Skip method
267
268 EtsReference *classOrThisRef = nullptr;
269 if (method->IsStatic()) {
270 // Handle class object
271 auto classObj = EtsClass::FromRuntimeClass(method->GetClass())->AsObject();
272 ASSERT(classObj != nullptr);
273
274 // Replace the method pointer (Method *) with a pointer to the class object
275 auto classPtr = reinterpret_cast<EtsObject **>(inRegsArgs);
276 *classPtr = classObj;
277
278 classOrThisRef = EtsReferenceStorage::NewEtsStackRef(classPtr);
279 } else {
280 ASSERT(method->GetNumArgs() != 0);
281 ASSERT(!method->GetArgType(0).IsPrimitive());
282
283 // Handle this arg
284 auto thisPtr = const_cast<EtsObject **>(argReader.ReadPtr<EtsObject *>());
285 classOrThisRef = EtsReferenceStorage::NewEtsStackRef(thisPtr);
286 }
287
288 auto coroutine = EtsCoroutine::CastFromThread(thread);
289 auto etsNapiEnv = coroutine->GetEtsNapiEnv();
290
291 // Prepare eTS NAPI args
292 argWriter.Write(static_cast<EtsEnv *>(etsNapiEnv));
293 argWriter.Write(classOrThisRef);
294 ARCH_COPY_METHOD_ARGS(method, argReader, argWriter);
295
296 // Completed the preparation of eTS NAPI arguments on the stack
297
298 // ATTENTION!!!
299 // Don't move the following code above, because only from this point on,
300 // the stack of the current frame is correct, so we can safely walk it
301 // (e.g. stop at a safepoint and walk the stack args of NAPI frame)
302
303 if (UNLIKELY(method->GetNativePointer() == nullptr)) {
304 coroutine->GetPandaVM()->ResolveNativeMethod(method);
305 }
306
307 Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, method);
308
309 constexpr uint32_t MAX_LOCAL_REF = 4096;
310 if (UNLIKELY(!etsNapiEnv->GetEtsReferenceStorage()->PushLocalEtsFrame(MAX_LOCAL_REF))) {
311 LOG(FATAL, RUNTIME) << "eTS NAPI push local frame failed";
312 }
313
314 auto etsMethod = EtsMethod::FromRuntimeMethod(method);
315 if (!etsMethod->IsFastNative()) {
316 thread->NativeCodeBegin();
317 }
318
319 return outRegsArgs;
320 }
321
322 #if defined(__clang__)
323 #pragma clang diagnostic pop
324 #elif defined(__GNUC__)
325 #pragma GCC diagnostic pop
326 #endif
327
EtsNapiEnd(Method * method,ManagedThread * thread,bool isFastNative)328 extern "C" void EtsNapiEnd(Method *method, ManagedThread *thread, bool isFastNative)
329 {
330 ASSERT(method != nullptr);
331 ASSERT(!method->IsSynchronized());
332 ASSERT(thread == ManagedThread::GetCurrent());
333
334 if (!isFastNative) {
335 thread->NativeCodeEnd();
336 }
337
338 Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, method);
339
340 auto coroutine = EtsCoroutine::CastFromThread(thread);
341 auto storage = coroutine->GetEtsNapiEnv()->GetEtsReferenceStorage();
342 storage->PopLocalEtsFrame(nullptr);
343 }
344
EtsNapiObjEnd(Method * method,EtsReference * etsRef,ManagedThread * thread,bool isFastNative)345 extern "C" EtsObject *EtsNapiObjEnd(Method *method, EtsReference *etsRef, ManagedThread *thread, bool isFastNative)
346 {
347 ASSERT(method != nullptr);
348 ASSERT(!method->IsSynchronized());
349 ASSERT(thread == ManagedThread::GetCurrent());
350
351 // End native scope first to get into managed scope for object manipulation
352 if (!isFastNative) {
353 thread->NativeCodeEnd();
354 }
355
356 Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, method);
357
358 EtsObject *ret = nullptr;
359
360 auto coroutine = EtsCoroutine::CastFromThread(thread);
361 auto storage = coroutine->GetEtsNapiEnv()->GetEtsReferenceStorage();
362 if (etsRef != nullptr) {
363 ret = storage->GetEtsObject(etsRef);
364 }
365
366 storage->PopLocalEtsFrame(nullptr);
367
368 return ret;
369 }
370
IsEtsMethodFastNative(Method * method)371 extern "C" bool IsEtsMethodFastNative(Method *method)
372 {
373 return EtsMethod::FromRuntimeMethod(method)->IsFastNative();
374 }
375
376 // Disable warning because the function uses ARCH_COPY_METHOD_ARGS macro.
377 // The macro uses computed goto
378 #if defined(__clang__)
379 #pragma clang diagnostic push
380 #pragma clang diagnostic ignored "-Wgnu-label-as-value"
381 #elif defined(__GNUC__)
382 #pragma GCC diagnostic push
383 #pragma GCC diagnostic ignored "-Wpedantic"
384 #endif
EtsAsyncCall(Method * method,EtsCoroutine * currentCoro,uint8_t * regArgs,uint8_t * stackArgs)385 extern "C" ObjectPointerType EtsAsyncCall(Method *method, EtsCoroutine *currentCoro, uint8_t *regArgs,
386 uint8_t *stackArgs)
387 {
388 PandaEtsVM *vm = currentCoro->GetPandaVM();
389 EtsClassLinker *etsClassLinker = vm->GetClassLinker();
390 Method *impl = etsClassLinker->GetAsyncImplMethod(method, currentCoro);
391 if (impl == nullptr) {
392 ASSERT(currentCoro->HasPendingException());
393 // Exception is thrown by GetAsyncImplMethod
394 return 0;
395 }
396 ASSERT(!currentCoro->HasPendingException());
397
398 EtsPromise *promise = EtsPromise::Create(currentCoro);
399 if (UNLIKELY(promise == nullptr)) {
400 ThrowOutOfMemoryError(currentCoro, "Cannot allocate Promise");
401 return 0;
402 }
403 auto promiseRef = vm->GetGlobalObjectStorage()->Add(promise, mem::Reference::ObjectType::WEAK);
404 auto evt = Runtime::GetCurrent()->GetInternalAllocator()->New<CompletionEvent>(promiseRef);
405 promise->SetEventPtr(evt);
406
407 auto *cm = currentCoro->GetCoroutineManager();
408
409 PandaVector<Value> args;
410 args.reserve(method->GetNumArgs());
411 Span<uint8_t> gprArgs(regArgs, ExtArchTraits::GP_ARG_NUM_BYTES);
412 Span<uint8_t> fprArgs(gprArgs.end(), ExtArchTraits::FP_ARG_NUM_BYTES);
413 ArgReader argReader(gprArgs, fprArgs, stackArgs);
414 argReader.Read<Method *>(); // Skip method
415 if (method->IsStatic()) {
416 // Replace the method pointer (Method *) by the pointer to the object class
417 // to satisfy stack walker
418 auto classObj = EtsClass::FromRuntimeClass(method->GetClass())->AsObject();
419 ASSERT(classObj != nullptr);
420 auto classPtr = reinterpret_cast<EtsObject **>(regArgs);
421 *classPtr = classObj;
422 } else {
423 // Handle this arg
424 ASSERT(method->GetNumArgs() != 0);
425 ASSERT(!method->GetArgType(0).IsPrimitive());
426 args.push_back(Value(*const_cast<ObjectHeader **>(argReader.ReadPtr<ObjectHeader *>())));
427 }
428
429 arch::ValueWriter writer(&args);
430 ARCH_COPY_METHOD_ARGS(method, argReader, writer);
431
432 [[maybe_unused]] EtsHandleScope scope(currentCoro);
433 EtsHandle<EtsPromise> promiseHandle(currentCoro, promise);
434 auto *coro = cm->Launch(evt, impl, std::move(args), CoroutineAffinity::SAME_WORKER);
435 if (UNLIKELY(coro == nullptr)) {
436 ASSERT(currentCoro->HasPendingException());
437 // OOM is thrown by Launch
438 promiseHandle.GetPtr()->SetEventPtr(nullptr);
439 Runtime::GetCurrent()->GetInternalAllocator()->Delete(evt);
440 return 0;
441 }
442 cm->Schedule();
443 return ToObjPtr(promiseHandle.GetPtr());
444 }
445 #if defined(__clang__)
446 #pragma clang diagnostic pop
447 #elif defined(__GNUC__)
448 #pragma GCC diagnostic pop
449 #endif
450 } // namespace panda::ets::napi
451