1 /**
2 * Copyright (c) 2023-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 "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/call/call.h"
22 #include "plugins/ets/runtime/interop_js/code_scopes.h"
23
24 #include "runtime/mem/vm_handle-inl.h"
25
26 namespace ark::ets::interop::js::ets_proxy {
27
28 /*static*/
CreateMethod(EtsMethodSet * etsMethodSet,EtsClassWrapper * owner)29 std::unique_ptr<EtsMethodWrapper> EtsMethodWrapper::CreateMethod(EtsMethodSet *etsMethodSet, EtsClassWrapper *owner)
30 {
31 auto wrapper = new EtsMethodWrapper(etsMethodSet, owner);
32 if (UNLIKELY(nullptr == wrapper)) {
33 return nullptr;
34 }
35 return std::unique_ptr<EtsMethodWrapper>(wrapper);
36 }
37
38 /*static*/
GetMethod(InteropCtx * ctx,EtsMethodSet * etsMethodSet)39 EtsMethodWrapper *EtsMethodWrapper::GetMethod(InteropCtx *ctx, EtsMethodSet *etsMethodSet)
40 {
41 EtsMethodWrappersCache *cache = ctx->GetEtsMethodWrappersCache();
42 EtsMethodWrapper *wrapper = cache->Lookup(etsMethodSet);
43 if (LIKELY(wrapper != nullptr)) {
44 return wrapper;
45 }
46
47 auto owner = ctx->GetEtsClassWrappersCache()->Lookup(etsMethodSet->GetEnclosingClass());
48 ASSERT(owner != nullptr);
49
50 std::unique_ptr<EtsMethodWrapper> etsMethodWrapper = EtsMethodWrapper::CreateMethod(etsMethodSet, owner);
51 return cache->Insert(etsMethodSet, std::move(etsMethodWrapper));
52 }
53
54 /* static */
MakeNapiProperty(EtsMethodSet * method,LazyEtsMethodWrapperLink * lazyLink)55 napi_property_descriptor EtsMethodWrapper::MakeNapiProperty(EtsMethodSet *method, LazyEtsMethodWrapperLink *lazyLink)
56 {
57 napi_callback callback {};
58 if (method->IsStatic()) {
59 callback = EtsMethodWrapper::EtsMethodCallHandler<true>;
60 } else {
61 callback = EtsMethodWrapper::EtsMethodCallHandler<false>;
62 }
63
64 napi_property_descriptor prop {};
65 prop.utf8name = method->GetName();
66 prop.method = callback;
67 prop.attributes = method->IsStatic() ? EtsClassWrapper::STATIC_METHOD_ATTR : EtsClassWrapper::METHOD_ATTR;
68 prop.data = lazyLink;
69
70 return prop;
71 }
72
FindSuitableMethod(const EtsMethodSet * methodSet,uint32_t parametersNum)73 static std::tuple<EtsMethod *, const char *> FindSuitableMethod(const EtsMethodSet *methodSet, uint32_t parametersNum)
74 {
75 if (nullptr == methodSet) {
76 return {nullptr, "no method found"};
77 }
78
79 EtsMethod *etsMethod = methodSet->GetMethod(parametersNum);
80
81 // Methods with matched number of arguments might present in base classes
82 while (nullptr == etsMethod && nullptr != methodSet) {
83 methodSet = methodSet->GetBaseMethodSet();
84 if (nullptr != methodSet) {
85 etsMethod = methodSet->GetMethod(parametersNum);
86 }
87 }
88
89 if (UNLIKELY(nullptr != methodSet && !methodSet->IsValid())) {
90 return {nullptr, "method has unsupported overloads"};
91 }
92
93 if (UNLIKELY(nullptr == etsMethod)) {
94 return {nullptr, "no suitable method found for this number of arguments"};
95 }
96
97 return {etsMethod, nullptr};
98 }
99
100 template <bool IS_STATIC>
101 /*static*/
EtsMethodCallHandler(napi_env env,napi_callback_info cinfo)102 napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env env, napi_callback_info cinfo)
103 {
104 EtsCoroutine *coro = EtsCoroutine::GetCurrent();
105 InteropCtx *ctx = InteropCtx::Current(coro);
106
107 INTEROP_CODE_SCOPE_JS(coro, env);
108 size_t argc;
109 napi_value jsThis;
110 void *data;
111 NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, nullptr, nullptr));
112 auto jsArgs = ctx->GetTempArgs<napi_value>(argc);
113 NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, jsArgs->data(), &jsThis, &data));
114
115 EtsMethodWrapper *_this; // NOLINT(readability-identifier-naming)
116
117 auto lazyLink = reinterpret_cast<LazyEtsMethodWrapperLink *>(data);
118 _this = EtsMethodWrapper::ResolveLazyLink(ctx, *lazyLink);
119 if (UNLIKELY(_this == nullptr)) {
120 return nullptr;
121 }
122
123 auto [etsMethod, errorMessage] = FindSuitableMethod(_this->etsMethodSet_, argc);
124 ASSERT(nullptr != etsMethod || nullptr != errorMessage);
125 if (UNLIKELY(nullptr == etsMethod)) {
126 ctx->ThrowJSTypeError(env, errorMessage);
127 return nullptr;
128 }
129
130 Method *method = etsMethod->GetPandaMethod();
131
132 if constexpr (IS_STATIC) {
133 EtsClass *etsClass = etsMethod->GetClass();
134 ASSERT(method->GetClass() == etsClass->GetRuntimeClass());
135 if (UNLIKELY(!coro->GetPandaVM()->GetClassLinker()->InitializeClass(coro, etsClass))) {
136 ctx->ForwardEtsException(coro);
137 return nullptr;
138 }
139 return CallETSStatic(coro, ctx, method, *jsArgs);
140 }
141
142 if (UNLIKELY(IsNullOrUndefined(env, jsThis))) {
143 ctx->ThrowJSTypeError(env, "ets this in instance method cannot be null or undefined");
144 return nullptr;
145 }
146
147 EtsObject *etsThis = _this->owner_->UnwrapEtsProxy(ctx, jsThis);
148 if (UNLIKELY(etsThis == nullptr)) {
149 if (coro->HasPendingException()) {
150 ctx->ForwardEtsException(coro);
151 }
152 ASSERT(ctx->SanityJSExceptionPending());
153 return nullptr;
154 }
155
156 return CallETSInstance(coro, ctx, method, *jsArgs, etsThis);
157 }
158
159 // Explicit instantiation
160 template napi_value EtsMethodWrapper::EtsMethodCallHandler<false>(napi_env, napi_callback_info);
161 template napi_value EtsMethodWrapper::EtsMethodCallHandler<true>(napi_env, napi_callback_info);
162
163 } // namespace ark::ets::interop::js::ets_proxy
164