• 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/ets_callback.h"
31 #include "plugins/ets/runtime/interop_js/code_scopes.h"
32 #include "runtime/coroutines/stackful_coroutine.h"
33 #include "intrinsics.h"
34 
35 namespace ark::ets::interop::js {
ThenCallback(napi_env env,napi_callback_info info)36 static napi_value ThenCallback(napi_env env, napi_callback_info info)
37 {
38     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
39     INTEROP_CODE_SCOPE_JS(coro, env);
40 
41     EtsCallback *callback = nullptr;
42     [[maybe_unused]] napi_status status =
43         napi_get_cb_info(env, info, nullptr, nullptr, nullptr, reinterpret_cast<void **>(&callback));
44     ASSERT(status == napi_ok);
45 
46     callback->Run();
47     Runtime::GetCurrent()->GetInternalAllocator()->Delete(callback);
48 
49     if (coro->HasPendingException()) {
50         napi_throw_error(env, nullptr, "EtsVM internal error");
51     }
52     napi_value undefined;
53     napi_get_undefined(env, &undefined);
54     return undefined;
55 }
56 
Post(PandaUniquePtr<Callback> callback)57 void JsJobQueue::Post(PandaUniquePtr<Callback> callback)
58 {
59     auto *callbackPtr = callback.release();
60     auto postProc = [callbackPtr] {
61         EtsCoroutine *coro = EtsCoroutine::GetCurrent();
62         napi_env env = InteropCtx::Current(coro)->GetJSEnv();
63         napi_deferred deferred;
64         napi_value undefined;
65         napi_value jsPromise;
66         napi_value thenFn;
67 
68         napi_get_undefined(env, &undefined);
69         napi_status status = napi_create_promise(env, &deferred, &jsPromise);
70         if (status != napi_ok) {
71             InteropCtx::Fatal("Cannot allocate a Promise instance");
72         }
73         status = napi_get_named_property(env, jsPromise, "then", &thenFn);
74         ASSERT(status == napi_ok);
75         (void)status;
76 
77         napi_value thenCallback;
78         status = napi_create_function(env, nullptr, 0, ThenCallback, callbackPtr, &thenCallback);
79         if (status != napi_ok) {
80             InteropCtx::Fatal("Cannot create a function");
81         }
82 
83         napi_value thenPromise;
84         status = napi_call_function(env, jsPromise, thenFn, 1, &thenCallback, &thenPromise);
85         ASSERT(status == napi_ok);
86         (void)status;
87 
88         napi_resolve_deferred(env, deferred, undefined);
89     };
90 
91     auto *mainT = EtsCoroutine::GetCurrent()->GetPandaVM()->GetCoroutineManager()->GetMainThread();
92     Coroutine *mainCoro = Coroutine::CastFromThread(mainT);
93     if (Coroutine::GetCurrent() != mainCoro) {
94         // NOTE(konstanting, #I67QXC): figure out if we need to ExecuteOnThisContext() for OHOS
95         mainCoro->GetContext<StackfulCoroutineContext>()->ExecuteOnThisContext(
96             &postProc, EtsCoroutine::GetCurrent()->GetContext<StackfulCoroutineContext>());
97     } else {
98         postProc();
99     }
100 }
101 
OnJsPromiseResolved(napi_env env,napi_callback_info info)102 static napi_value OnJsPromiseResolved(napi_env env, [[maybe_unused]] napi_callback_info info)
103 {
104     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
105     PandaEtsVM *vm = coro->GetPandaVM();
106     auto ctx = InteropCtx::Current(coro);
107     INTEROP_CODE_SCOPE_JS(coro, env);
108 
109     mem::Reference *promiseRef = nullptr;
110     size_t argc = 1;
111     napi_value value;
112     napi_status status = napi_get_cb_info(env, info, &argc, &value, nullptr, reinterpret_cast<void **>(&promiseRef));
113     if (status != napi_ok) {
114         InteropCtx::Fatal("Cannot call napi_get_cb_info!");
115     }
116 
117     EtsHandleScope hScope(coro);
118     EtsHandle<EtsPromise> promiseHandle(coro, EtsPromise::FromCoreType(vm->GetGlobalObjectStorage()->Get(promiseRef)));
119     vm->GetGlobalObjectStorage()->Remove(promiseRef);
120 
121     auto jsval = JSValue::Create(coro, ctx, value);
122     ark::ets::intrinsics::EtsPromiseResolve(promiseHandle.GetPtr(), jsval->AsObject());
123 
124     vm->GetCoroutineManager()->Schedule();
125 
126     napi_value undefined;
127     napi_get_undefined(env, &undefined);
128     return undefined;
129 }
130 
CreatePromiseLink(EtsObject * jsObject,EtsPromise * etsPromise)131 void JsJobQueue::CreatePromiseLink(EtsObject *jsObject, EtsPromise *etsPromise)
132 {
133     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
134     PandaEtsVM *vm = coro->GetPandaVM();
135     InteropCtx *ctx = InteropCtx::Current(coro);
136     napi_env env = ctx->GetJSEnv();
137     ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
138     napi_value jsPromise = storage->GetReference(jsObject)->GetJsObject(env);
139 
140     napi_value thenFn;
141     napi_status status = napi_get_named_property(env, jsPromise, "then", &thenFn);
142     if (status != napi_ok) {
143         InteropCtx::Fatal("Cannot call then() from a JS promise");
144     }
145 
146     mem::Reference *promiseRef = vm->GetGlobalObjectStorage()->Add(etsPromise, mem::Reference::ObjectType::GLOBAL);
147 
148     // NOTE(konstanting, #I67QXC): OnJsPromiseRejected
149     napi_value thenCallback;
150     status = napi_create_function(env, nullptr, 0, OnJsPromiseResolved, promiseRef, &thenCallback);
151     if (status != napi_ok) {
152         InteropCtx::Fatal("Cannot create a function");
153     }
154 
155     napi_value thenResult;
156     status = napi_call_function(env, jsPromise, thenFn, 1, &thenCallback, &thenResult);
157     if (status != napi_ok) {
158         InteropCtx::Fatal("Cannot call then() from a JS Promise");
159     }
160 }
161 
CreateLink(EtsObject * source,EtsObject * target)162 void JsJobQueue::CreateLink(EtsObject *source, EtsObject *target)
163 {
164     auto addLinkProc = [&]() { CreatePromiseLink(source, EtsPromise::FromEtsObject(target)); };
165 
166     auto *mainT = EtsCoroutine::GetCurrent()->GetPandaVM()->GetCoroutineManager()->GetMainThread();
167     Coroutine *mainCoro = Coroutine::CastFromThread(mainT);
168     if (Coroutine::GetCurrent() != mainCoro) {
169         // NOTE(konstanting, #I67QXC): figure out if we need to ExecuteOnThisContext() for OHOS
170         mainCoro->GetContext<StackfulCoroutineContext>()->ExecuteOnThisContext(
171             &addLinkProc, EtsCoroutine::GetCurrent()->GetContext<StackfulCoroutineContext>());
172     } else {
173         addLinkProc();
174     }
175 }
176 }  // namespace ark::ets::interop::js
177