• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-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 #include "plugins/ets/runtime/interop_js/js_proxy/js_proxy.h"
16 #include "plugins/ets/runtime/interop_js/js_refconvert_function.h"
17 #include "plugins/ets/runtime/interop_js/code_scopes.h"
18 #include "plugins/ets/runtime/types/ets_type.h"
19 namespace ark::ets::interop::js {
20 
EtsLambdaProxyInvoke(napi_env env,napi_callback_info cbinfo)21 napi_value EtsLambdaProxyInvoke(napi_env env, napi_callback_info cbinfo)
22 {
23     auto coro = EtsCoroutine::GetCurrent();
24     auto ctx = InteropCtx::Current(coro);
25     if (ctx == nullptr) {
26         ThrowNoInteropContextException();
27         return nullptr;
28     }
29     INTEROP_CODE_SCOPE_JS(coro);
30 
31     size_t argc;
32     napi_value athis;
33     void *data;
34     NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, nullptr, &athis, &data));
35     auto jsArgs = ctx->GetTempArgs<napi_value>(argc);
36     NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, jsArgs->data(), &athis, &data));
37 
38     // Atomic with acquire order reason: load visibility after shared reference initialization
39     auto *sharedRef = AtomicLoad(static_cast<ets_proxy::SharedReference **>(data), std::memory_order_acquire);
40     ASSERT(sharedRef != nullptr);
41 
42     ScopedManagedCodeThread managedScope(coro);
43     auto *etsThis = sharedRef->GetEtsObject();
44     ASSERT(etsThis != nullptr);
45     EtsMethod *method = etsThis->GetClass()->GetInstanceMethod(INVOKE_METHOD_NAME, nullptr);
46     method = method == nullptr ? etsThis->GetClass()->GetInstanceMethod(STD_CORE_FUNCTION_UNSAFECALL_METHOD, nullptr)
47                                : method;
48     ASSERT(method != nullptr);
49 
50     return CallETSInstance(coro, ctx, method->GetPandaMethod(), *jsArgs, etsThis);
51 }
52 
WrapImpl(InteropCtx * ctx,EtsObject * obj)53 napi_value JSRefConvertFunction::WrapImpl(InteropCtx *ctx, EtsObject *obj)
54 {
55     auto coro = EtsCoroutine::GetCurrent();
56     ASSERT(ctx == InteropCtx::Current(coro));
57     auto env = ctx->GetJSEnv();
58 
59     ASSERT(obj->GetClass() == klass_);
60 
61     JSValue *jsValue;
62     {
63         NapiScope jsHandleScope(env);
64 
65         ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
66         if (LIKELY(storage->HasReference(obj, env))) {
67             jsValue = JSValue::CreateRefValue(coro, ctx, storage->GetJsObject(obj, env), napi_function);
68         } else {
69             napi_value jsFn;
70             auto preInitCallback = [&env, &jsFn](ets_proxy::SharedReference **uninitializedRef) {
71                 ASSERT(uninitializedRef != nullptr);
72                 NAPI_CHECK_FATAL(napi_create_function(env, ark::ets::INVOKE_METHOD_NAME, NAPI_AUTO_LENGTH,
73                                                       EtsLambdaProxyInvoke, uninitializedRef, &jsFn));
74                 return jsFn;
75             };
76             ets_proxy::SharedReference *sharedRef = storage->CreateETSObjectRef(ctx, obj, jsFn, preInitCallback);
77             if (UNLIKELY(sharedRef == nullptr)) {
78                 ASSERT(InteropCtx::SanityJSExceptionPending());
79                 return nullptr;
80             }
81             jsValue = JSValue::CreateRefValue(coro, ctx, jsFn, napi_function);
82         }
83     }
84     if (UNLIKELY(jsValue == nullptr)) {
85         return nullptr;
86     }
87     return jsValue->GetRefValue(env);
88 }
89 
UnwrapImpl(InteropCtx * ctx,napi_value jsFun)90 EtsObject *JSRefConvertFunction::UnwrapImpl(InteropCtx *ctx, napi_value jsFun)
91 {
92     // Check if object has SharedReference
93     ets_proxy::SharedReference *sharedRef = ctx->GetSharedRefStorage()->GetReference(ctx->GetJSEnv(), jsFun);
94     if (LIKELY(sharedRef != nullptr)) {
95         EtsObject *jsFunctionProxy = sharedRef->GetEtsObject();
96         return jsFunctionProxy;
97     }
98 
99     return this->CreateJSFunctionProxy(ctx, jsFun);
100 }
101 
LazyInitJsFunctionProxyWrapper(InteropCtx * ctx)102 void JSRefConvertFunction::LazyInitJsFunctionProxyWrapper(InteropCtx *ctx)
103 {
104     // register the function interface
105     auto etsClass = EtsClass::FromRuntimeClass(this->klass_->GetRuntimeClass());
106 
107     // create a JSProxy wrapper for the function
108     this->jsFunctionProxyWrapper_ = js_proxy::JSProxy::CreateFunctionProxy(etsClass);
109     ctx->SetJsProxyInstance(etsClass, this->jsFunctionProxyWrapper_);
110 }
111 
CreateJSFunctionProxy(InteropCtx * ctx,napi_value jsFun)112 EtsObject *JSRefConvertFunction::CreateJSFunctionProxy(InteropCtx *ctx, napi_value jsFun)
113 {
114     // lazy init the function proxy wrapper
115     this->LazyInitJsFunctionProxyWrapper(ctx);
116 
117     // JS Function => ETS Function Object
118     ASSERT(this->jsFunctionProxyWrapper_ != nullptr);
119     auto *storage = ctx->GetSharedRefStorage();
120     ASSERT(storage->GetReference(ctx->GetJSEnv(), jsFun) == nullptr);
121 
122     EtsObject *etsObject = EtsObject::Create(jsFunctionProxyWrapper_->GetProxyClass());
123     if (UNLIKELY(etsObject == nullptr)) {
124         ctx->ForwardEtsException(EtsCoroutine::GetCurrent());
125         return nullptr;
126     }
127 
128     auto *sharedRef = storage->CreateJSObjectRefwithWrap(ctx, etsObject, jsFun);
129     if (UNLIKELY(sharedRef == nullptr)) {
130         ASSERT(InteropCtx::SanityJSExceptionPending());
131         return nullptr;
132     }
133     return sharedRef->GetEtsObject();  // fetch again after gc
134 }
135 }  // namespace ark::ets::interop::js
136