• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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