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