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