• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023 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/interop_js/ets_proxy/ets_method_wrapper.h"
17 
18 #include "plugins/ets/runtime/interop_js/interop_context.h"
19 #include "plugins/ets/runtime/interop_js/js_refconvert.h"
20 #include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h"
21 #include "plugins/ets/runtime/interop_js/js_value_call.h"
22 #include "plugins/ets/runtime/interop_js/napi_env_scope.h"
23 
24 #include "runtime/mem/vm_handle-inl.h"
25 
26 namespace panda::ets::interop::js::ets_proxy {
27 
28 /*static*/
CreateMethod(EtsMethod * etsMethod,EtsClassWrapper * owner)29 std::unique_ptr<EtsMethodWrapper> EtsMethodWrapper::CreateMethod(EtsMethod *etsMethod, EtsClassWrapper *owner)
30 {
31     return std::unique_ptr<EtsMethodWrapper>(new EtsMethodWrapper(etsMethod, owner));
32 }
33 
34 /*static*/
CreateFunction(InteropCtx * ctx,EtsMethod * etsMethod)35 std::unique_ptr<EtsMethodWrapper> EtsMethodWrapper::CreateFunction(InteropCtx *ctx, EtsMethod *etsMethod)
36 {
37     ASSERT(etsMethod->IsStatic());
38     auto env = ctx->GetJSEnv();
39     auto wrapper = CreateMethod(etsMethod, nullptr);
40 
41     napi_value jsValue;
42     NAPI_CHECK_FATAL(napi_create_function(env, wrapper->etsMethod_->GetName(), NAPI_AUTO_LENGTH,
43                                           &EtsMethodCallHandler<true, true>, wrapper.get(), &jsValue));
44     NAPI_CHECK_FATAL(napi_create_reference(env, jsValue, 1, &wrapper->jsRef_));
45     NAPI_CHECK_FATAL(NapiObjectSeal(env, jsValue));
46 
47     return wrapper;
48 }
49 
50 /*static*/
GetMethod(InteropCtx * ctx,EtsMethod * etsMethod)51 EtsMethodWrapper *EtsMethodWrapper::GetMethod(InteropCtx *ctx, EtsMethod *etsMethod)
52 {
53     EtsMethodWrappersCache *cache = ctx->GetEtsMethodWrappersCache();
54     EtsMethodWrapper *wrapper = cache->Lookup(etsMethod);
55     if (LIKELY(wrapper != nullptr)) {
56         return wrapper;
57     }
58 
59     auto owner = ctx->GetEtsClassWrappersCache()->Lookup(etsMethod->GetClass());
60     ASSERT(owner != nullptr);
61 
62     std::unique_ptr<EtsMethodWrapper> etsMethodWrapper = EtsMethodWrapper::CreateMethod(etsMethod, owner);
63     return cache->Insert(etsMethod, std::move(etsMethodWrapper));
64 }
65 
GetFunction(InteropCtx * ctx,EtsMethod * etsMethod)66 EtsMethodWrapper *EtsMethodWrapper::GetFunction(InteropCtx *ctx, EtsMethod *etsMethod)
67 {
68     EtsMethodWrappersCache *cache = ctx->GetEtsMethodWrappersCache();
69     EtsMethodWrapper *wrapper = cache->Lookup(etsMethod);
70     if (LIKELY(wrapper != nullptr)) {
71         return wrapper;
72     }
73 
74     std::unique_ptr<EtsMethodWrapper> etsFuncWrapper = EtsMethodWrapper::CreateFunction(ctx, etsMethod);
75     return cache->Insert(etsMethod, std::move(etsFuncWrapper));
76 }
77 
78 /* static */
MakeNapiProperty(Method * method,LazyEtsMethodWrapperLink * lazyLink)79 napi_property_descriptor EtsMethodWrapper::MakeNapiProperty(Method *method, LazyEtsMethodWrapperLink *lazyLink)
80 {
81     napi_callback callback {};
82     if (method->IsStatic()) {
83         callback = EtsMethodWrapper::EtsMethodCallHandler<true, false>;
84     } else {
85         callback = EtsMethodWrapper::EtsMethodCallHandler<false, false>;
86     }
87 
88     napi_property_descriptor prop {};
89     prop.utf8name = utf::Mutf8AsCString(method->GetName().data);
90     prop.method = callback;
91     prop.attributes = method->IsStatic() ? EtsClassWrapper::STATIC_METHOD_ATTR : EtsClassWrapper::METHOD_ATTR;
92     prop.data = lazyLink;
93 
94     return prop;
95 }
96 
97 template <bool IS_STATIC, bool IS_FUNC>
98 /*static*/
EtsMethodCallHandler(napi_env env,napi_callback_info cinfo)99 napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env env, napi_callback_info cinfo)
100 {
101     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
102     InteropCtx *ctx = InteropCtx::Current(coro);
103 
104     [[maybe_unused]] EtsJSNapiEnvScope envscope(ctx, env);
105     size_t argc;
106     napi_value jsThis;
107     void *data;
108     NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, nullptr, nullptr));
109     auto jsArgs = ctx->GetTempArgs<napi_value>(argc);
110     NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, jsArgs->data(), &jsThis, &data));
111 
112     EtsMethodWrapper *_this;  // NOLINT(readability-identifier-naming)
113 
114     if constexpr (IS_FUNC) {
115         _this = reinterpret_cast<EtsMethodWrapper *>(data);
116     } else {
117         auto lazyLink = reinterpret_cast<LazyEtsMethodWrapperLink *>(data);
118         _this = EtsMethodWrapper::ResolveLazyLink(ctx, *lazyLink);
119         if (UNLIKELY(_this == nullptr)) {
120             return nullptr;
121         }
122     }
123 
124     Method *method = _this->etsMethod_->GetPandaMethod();
125 
126     ScopedManagedCodeThread managedScope(coro);
127     if constexpr (IS_STATIC) {
128         EtsClass *etsClass = _this->etsMethod_->GetClass();
129         if (UNLIKELY(!coro->GetPandaVM()->GetClassLinker()->InitializeClass(coro, etsClass))) {
130             ctx->ForwardEtsException(coro);
131             return nullptr;
132         }
133         return EtsCallImplStatic(coro, ctx, method, *jsArgs);
134     }
135 
136     if (UNLIKELY(IsNullOrUndefined(env, jsThis))) {
137         ctx->ThrowJSTypeError(env, "ets this in instance method cannot be null or undefined");
138         return nullptr;
139     }
140 
141     EtsObject *etsThis = _this->owner_->UnwrapEtsProxy(ctx, jsThis);
142     if (UNLIKELY(etsThis == nullptr)) {
143         if (coro->HasPendingException()) {
144             ctx->ForwardEtsException(coro);
145         }
146         ASSERT(ctx->SanityJSExceptionPending());
147         return nullptr;
148     }
149 
150     return EtsCallImplInstance(coro, ctx, method, *jsArgs, etsThis);
151 }
152 
153 // Explicit instantiation
154 template napi_value EtsMethodWrapper::EtsMethodCallHandler<false, false>(napi_env, napi_callback_info);
155 template napi_value EtsMethodWrapper::EtsMethodCallHandler<false, true>(napi_env, napi_callback_info);
156 template napi_value EtsMethodWrapper::EtsMethodCallHandler<true, false>(napi_env, napi_callback_info);
157 template napi_value EtsMethodWrapper::EtsMethodCallHandler<true, true>(napi_env, napi_callback_info);
158 
159 }  // namespace panda::ets::interop::js::ets_proxy
160