• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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/ets_coroutine.h"
17 #include "plugins/ets/runtime/ets_exceptions.h"
18 #include "runtime/include/runtime.h"
19 #include "source_lang_enum.h"
20 #include "types/ets_array.h"
21 #include "types/ets_class.h"
22 #include "types/ets_field.h"
23 #include "types/ets_method.h"
24 #include "types/ets_type.h"
25 #include "types/ets_job.h"
26 #include "types/ets_promise.h"
27 #include "types/ets_typeapi_create.h"
28 #include "types/ets_type_comptime_traits.h"
29 #include "mem/vm_handle.h"
30 
31 #include <type_traits>
32 
33 namespace ark::ets::intrinsics {
ResolveInvokeMethod(EtsCoroutine * coro,VMHandle<EtsObject> func)34 static EtsMethod *ResolveInvokeMethod(EtsCoroutine *coro, VMHandle<EtsObject> func)
35 {
36     if (func.GetPtr() == nullptr) {
37         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
38         ThrowNullPointerException(ctx, coro);
39         return nullptr;
40     }
41 
42     EtsMethod *method = func->GetClass()->GetInstanceMethod(INVOKE_METHOD_NAME, nullptr);
43     ASSERT(method != nullptr);
44 
45     if (method->IsAbstract()) {
46         method = func->GetClass()->ResolveVirtualMethod(method);
47     }
48 
49     if (UNLIKELY(!method->GetPandaMethod()->Verify())) {
50         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
51         ThrowVerificationException(ctx, method->GetPandaMethod()->GetFullName());
52         return nullptr;
53     }
54 
55     return method;
56 }
57 
CreateArgsVector(VMHandle<EtsObject> func,EtsMethod * method,VMHandle<EtsArray> arr)58 static PandaVector<Value> CreateArgsVector(VMHandle<EtsObject> func, EtsMethod *method, VMHandle<EtsArray> arr)
59 {
60     size_t methArgsCount = method->GetNumArgs();
61     PandaVector<Value> realArgs {methArgsCount};
62     size_t firstRealArg = 0;
63 
64     if (!method->IsStatic()) {
65         realArgs[0] = Value(func->GetCoreType());
66         firstRealArg = 1;
67     }
68 
69     size_t numArgs = arr->GetLength();
70     if (methArgsCount - firstRealArg != numArgs) {
71         UNREACHABLE();
72     }
73 
74     for (size_t i = 0; i < numArgs; i++) {
75         auto arg = arr->GetCoreType()->Get<ObjectHeader *>(i);
76         auto argType = method->GetArgType(firstRealArg + i);
77         if (argType == EtsType::OBJECT) {
78             realArgs[firstRealArg + i] = Value(arg);
79             continue;
80         }
81         EtsPrimitiveTypeEnumToComptimeConstant(argType, [&](auto type) -> void {
82             using T = EtsTypeEnumToCppType<decltype(type)::value>;
83             realArgs[firstRealArg + i] =
84                 Value(EtsBoxPrimitive<T>::FromCoreType(EtsObject::FromCoreType(arg))->GetValue());
85         });
86     }
87 
88     return realArgs;
89 }
90 
91 template <typename CoroResult>
92 // CC-OFFNXT(huge_method) solid logic
Launch(EtsObject * func,EtsArray * arr,bool abortFlag,bool postToMain=false)93 ObjectHeader *Launch(EtsObject *func, EtsArray *arr, bool abortFlag, bool postToMain = false)
94 {
95     static_assert(std::is_same<CoroResult, EtsJob>::value || std::is_same<CoroResult, EtsPromise>::value);
96 
97     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
98     ASSERT(coro != nullptr);
99     if (func == nullptr) {
100         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
101         ThrowNullPointerException(ctx, coro);
102         return nullptr;
103     }
104     if (coro->GetCoroutineManager()->IsCoroutineSwitchDisabled()) {
105         ThrowEtsException(coro, panda_file_items::class_descriptors::INVALID_COROUTINE_OPERATION_ERROR,
106                           "Cannot launch coroutines in the current context!");
107         return nullptr;
108     }
109     if (arr == nullptr) {
110         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
111         ThrowNullPointerException(ctx, coro);
112         return nullptr;
113     }
114     [[maybe_unused]] EtsHandleScope scope(coro);
115     VMHandle<EtsArray> array(coro, arr->GetCoreType());
116     VMHandle<EtsObject> function(coro, func->GetCoreType());
117     EtsMethod *method = ResolveInvokeMethod(coro, function);
118     if (method == nullptr) {
119         return nullptr;
120     }
121 
122     // create the coro and put it to the ready queue
123     CoroResult *coroResult = CoroResult::Create(coro);
124     if (UNLIKELY(coroResult == nullptr)) {
125         return nullptr;
126     }
127     EtsHandle<CoroResult> coroResultHandle(coro, coroResult);
128 
129     PandaEtsVM *etsVm = coro->GetPandaVM();
130     auto *coroManager = coro->GetCoroutineManager();
131     auto ref = etsVm->GetGlobalObjectStorage()->Add(coroResultHandle.GetPtr(), mem::Reference::ObjectType::GLOBAL);
132     auto evt = Runtime::GetCurrent()->GetInternalAllocator()->New<CompletionEvent>(ref, coroManager);
133 
134     // since transferring arguments from frame registers (which are local roots for GC) to a C++ vector
135     // introduces the potential risk of pointer invalidation in case GC moves the referenced objects,
136     // we would like to do this transfer below all potential GC invocation points
137     PandaVector<Value> realArgs = CreateArgsVector(function, method, array);
138     auto mode = postToMain ? CoroutineLaunchMode::MAIN_WORKER : CoroutineLaunchMode::DEFAULT;
139     bool launchResult = coro->GetCoroutineManager()->Launch(evt, method->GetPandaMethod(), std::move(realArgs), mode,
140                                                             EtsCoroutine::LAUNCH, abortFlag);
141     if (UNLIKELY(!launchResult)) {
142         // Launch failed. The exception in the current coro should be already set by Launch(),
143         // just return null as the result and clean up the allocated resources.
144         ASSERT(coro->HasPendingException());
145         Runtime::GetCurrent()->GetInternalAllocator()->Delete(evt);
146         return nullptr;
147     }
148 
149     return coroResultHandle.GetPtr();
150 }
151 
152 extern "C" {
PostToMainThread(EtsObject * func,EtsArray * arr,EtsBoolean abortFlag)153 EtsJob *PostToMainThread(EtsObject *func, EtsArray *arr, EtsBoolean abortFlag)
154 {
155     return static_cast<EtsJob *>(Launch<EtsJob>(func, arr, abortFlag != 0U, true));
156 }
157 }
158 
159 extern "C" {
EtsLaunchInternalJobNative(EtsObject * func,EtsArray * arr,EtsBoolean abortFlag)160 EtsJob *EtsLaunchInternalJobNative(EtsObject *func, EtsArray *arr, EtsBoolean abortFlag)
161 {
162     return static_cast<EtsJob *>(Launch<EtsJob>(func, arr, abortFlag != 0U));
163 }
164 
EtsLaunchSameWorker(EtsObject * callback)165 void EtsLaunchSameWorker(EtsObject *callback)
166 {
167     auto *coro = EtsCoroutine::GetCurrent();
168     EtsHandleScope scope(coro);
169     VMHandle<EtsObject> hCallback(coro, callback->GetCoreType());
170     ASSERT(hCallback.GetPtr() != nullptr);
171     PandaVector<Value> args = {Value(hCallback->GetCoreType())};
172     auto *method = ResolveInvokeMethod(coro, hCallback);
173     auto *coroMan = coro->GetCoroutineManager();
174     auto evt = Runtime::GetCurrent()->GetInternalAllocator()->New<CompletionEvent>(nullptr, coroMan);
175     [[maybe_unused]] auto launched =
176         coroMan->Launch(evt, method->GetPandaMethod(), std::move(args), CoroutineLaunchMode::SAME_WORKER,
177                         EtsCoroutine::TIMER_CALLBACK, true);
178     ASSERT(launched);
179 }
180 }
181 }  // namespace ark::ets::intrinsics
182