• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 <node_api.h>
17 #include "runtime/handle_scope-inl.h"
18 #include "runtime/include/mem/panda_smart_pointers.h"
19 #include "runtime/mem/refstorage/reference.h"
20 #include "plugins/ets/runtime/ets_vm.h"
21 #include "plugins/ets/runtime/ets_utils.h"
22 #include "plugins/ets/runtime/types/ets_method.h"
23 #include "plugins/ets/runtime/interop_js/js_job_queue.h"
24 #include "plugins/ets/runtime/interop_js/interop_common.h"
25 #include "plugins/ets/runtime/interop_js/interop_context.h"
26 #include "plugins/ets/runtime/interop_js/js_value.h"
27 #include "plugins/ets/runtime/types/ets_promise.h"
28 #include "plugins/ets/runtime/ets_handle_scope.h"
29 #include "plugins/ets/runtime/ets_handle.h"
30 #include "plugins/ets/runtime/interop_js/code_scopes.h"
31 #include "runtime/coroutines/stackful_coroutine.h"
32 #include "intrinsics.h"
33 
34 namespace ark::ets::interop::js {
ThenCallback(napi_env env,napi_callback_info info)35 static napi_value ThenCallback(napi_env env, napi_callback_info info)
36 {
37     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
38     INTEROP_CODE_SCOPE_JS(coro, env);
39 
40     JsJobQueue::JsCallback *jsCallback = nullptr;
41     [[maybe_unused]] napi_status status =
42         napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&jsCallback));
43     ASSERT(status == napi_ok);
44 
45     jsCallback->Run();
46     Runtime::GetCurrent()->GetInternalAllocator()->Delete(jsCallback);
47 
48     if (coro->HasPendingException()) {
49         napi_throw_error(env, nullptr, "EtsVM internal error");
50     }
51     napi_value undefined;
52     napi_get_undefined(env, &undefined);
53     return undefined;
54 }
55 
Post(EtsObject * callback)56 void JsJobQueue::Post(EtsObject *callback)
57 {
58     auto postProc = [callback] {
59         EtsCoroutine *coro = EtsCoroutine::GetCurrent();
60         napi_env env = InteropCtx::Current(coro)->GetJSEnv();
61         napi_deferred deferred;
62         napi_value undefined;
63         napi_value jsPromise;
64         napi_value thenFn;
65 
66         napi_get_undefined(env, &undefined);
67         napi_status status = napi_create_promise(env, &deferred, &jsPromise);
68         if (status != napi_ok) {
69             InteropCtx::Fatal("Cannot allocate a Promise instance");
70         }
71         status = napi_get_named_property(env, jsPromise, "then", &thenFn);
72         ASSERT(status == napi_ok);
73         (void)status;
74 
75         auto *jsCallback = JsCallback::Create(coro, callback);
76         napi_value thenCallback;
77         status = napi_create_function(env, nullptr, 0, ThenCallback, jsCallback, &thenCallback);
78         if (status != napi_ok) {
79             InteropCtx::Fatal("Cannot create a function");
80         }
81 
82         napi_value thenPromise;
83         status = napi_call_function(env, jsPromise, thenFn, 1, &thenCallback, &thenPromise);
84         ASSERT(status == napi_ok);
85         (void)status;
86 
87         napi_resolve_deferred(env, deferred, undefined);
88     };
89 
90     auto *mainT = EtsCoroutine::GetCurrent()->GetPandaVM()->GetCoroutineManager()->GetMainThread();
91     Coroutine *mainCoro = Coroutine::CastFromThread(mainT);
92     if (Coroutine::GetCurrent() != mainCoro) {
93         // NOTE(konstanting, #I67QXC): figure out if we need to ExecuteOnThisContext() for OHOS
94         mainCoro->GetContext<StackfulCoroutineContext>()->ExecuteOnThisContext(
95             &postProc, EtsCoroutine::GetCurrent()->GetContext<StackfulCoroutineContext>());
96     } else {
97         postProc();
98     }
99 }
100 
OnJsPromiseResolved(napi_env env,napi_callback_info info)101 static napi_value OnJsPromiseResolved(napi_env env, [[maybe_unused]] napi_callback_info info)
102 {
103     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
104     PandaEtsVM *vm = coro->GetPandaVM();
105     auto ctx = InteropCtx::Current(coro);
106     INTEROP_CODE_SCOPE_JS(coro, env);
107 
108     mem::Reference *promiseRef = nullptr;
109     size_t argc = 1;
110     napi_value value;
111     napi_status status = napi_get_cb_info(env, info, &argc, &value, nullptr, reinterpret_cast<void **>(&promiseRef));
112     if (status != napi_ok) {
113         InteropCtx::Fatal("Cannot call napi_get_cb_info!");
114     }
115 
116     EtsHandleScope hScope(coro);
117     EtsHandle<EtsPromise> promiseHandle(coro, EtsPromise::FromCoreType(vm->GetGlobalObjectStorage()->Get(promiseRef)));
118     vm->GetGlobalObjectStorage()->Remove(promiseRef);
119 
120     auto jsval = JSValue::Create(coro, ctx, value);
121     ark::ets::intrinsics::EtsPromiseResolve(promiseHandle.GetPtr(), jsval->AsObject());
122 
123     vm->GetCoroutineManager()->Schedule();
124 
125     napi_value undefined;
126     napi_get_undefined(env, &undefined);
127     return undefined;
128 }
129 
CreatePromiseLink(EtsObject * jsObject,EtsPromise * etsPromise)130 void JsJobQueue::CreatePromiseLink(EtsObject *jsObject, EtsPromise *etsPromise)
131 {
132     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
133     PandaEtsVM *vm = coro->GetPandaVM();
134     InteropCtx *ctx = InteropCtx::Current(coro);
135     napi_env env = ctx->GetJSEnv();
136     ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
137     napi_value jsPromise = storage->GetReference(jsObject)->GetJsObject(env);
138 
139     napi_value thenFn;
140     napi_status status = napi_get_named_property(env, jsPromise, "then", &thenFn);
141     if (status != napi_ok) {
142         InteropCtx::Fatal("Cannot call then() from a JS promise");
143     }
144 
145     mem::Reference *promiseRef = vm->GetGlobalObjectStorage()->Add(etsPromise, mem::Reference::ObjectType::GLOBAL);
146 
147     // NOTE(konstanting, #I67QXC): OnJsPromiseRejected
148     napi_value thenCallback;
149     status = napi_create_function(env, nullptr, 0, OnJsPromiseResolved, promiseRef, &thenCallback);
150     if (status != napi_ok) {
151         InteropCtx::Fatal("Cannot create a function");
152     }
153 
154     napi_value thenResult;
155     status = napi_call_function(env, jsPromise, thenFn, 1, &thenCallback, &thenResult);
156     if (status != napi_ok) {
157         InteropCtx::Fatal("Cannot call then() from a JS Promise");
158     }
159 }
160 
CreateLink(EtsObject * source,EtsObject * target)161 void JsJobQueue::CreateLink(EtsObject *source, EtsObject *target)
162 {
163     auto addLinkProc = [&]() { CreatePromiseLink(source, EtsPromise::FromEtsObject(target)); };
164 
165     auto *mainT = EtsCoroutine::GetCurrent()->GetPandaVM()->GetCoroutineManager()->GetMainThread();
166     Coroutine *mainCoro = Coroutine::CastFromThread(mainT);
167     if (Coroutine::GetCurrent() != mainCoro) {
168         // NOTE(konstanting, #I67QXC): figure out if we need to ExecuteOnThisContext() for OHOS
169         mainCoro->GetContext<StackfulCoroutineContext>()->ExecuteOnThisContext(
170             &addLinkProc, EtsCoroutine::GetCurrent()->GetContext<StackfulCoroutineContext>());
171     } else {
172         addLinkProc();
173     }
174 }
175 
176 /* static */
Create(EtsCoroutine * coro,const EtsObject * callback)177 JsJobQueue::JsCallback *JsJobQueue::JsCallback::Create(EtsCoroutine *coro, const EtsObject *callback)
178 {
179     auto *refStorage = coro->GetPandaVM()->GetGlobalObjectStorage();
180     auto *jsCallbackRef = refStorage->Add(callback->GetCoreType(), mem::Reference::ObjectType::GLOBAL);
181     ASSERT(jsCallbackRef != nullptr);
182     auto *jsCallback = Runtime::GetCurrent()->GetInternalAllocator()->New<JsCallback>(jsCallbackRef);
183     return jsCallback;
184 }
185 
Run()186 void JsJobQueue::JsCallback::Run()
187 {
188     auto *coro = EtsCoroutine::GetCurrent();
189     auto *refStorage = coro->GetPandaVM()->GetGlobalObjectStorage();
190     auto *callback = EtsObject::FromCoreType(refStorage->Get(jsCallbackRef_));
191     LambdaUtils::InvokeVoid(coro, callback);
192 }
193 
~JsCallback()194 JsJobQueue::JsCallback::~JsCallback()
195 {
196     auto *refStorage = EtsCoroutine::GetCurrent()->GetPandaVM()->GetGlobalObjectStorage();
197     refStorage->Remove(jsCallbackRef_);
198 }
199 
200 }  // namespace ark::ets::interop::js
201