• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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