1 /**
2 * Copyright (c) 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
16 #include "plugins/ets/runtime/interop_js/code_scopes.h"
17 #include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h"
18 #include "plugins/ets/runtime/interop_js/js_refconvert_record.h"
19 #include "plugins/ets/runtime/types/ets_type.h"
20
21 namespace ark::ets::interop::js {
22
WrapImpl(InteropCtx * ctx,EtsObject * obj)23 napi_value JSRefConvertRecord::WrapImpl(InteropCtx *ctx, EtsObject *obj)
24 {
25 auto env = ctx->GetJSEnv();
26
27 ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
28 if (LIKELY(storage->HasReference(obj, env))) {
29 return storage->GetJsObject(obj, env);
30 }
31
32 napi_value handlerObj = GetReferenceValue(env, handleObjCache_);
33
34 napi_value targetObj;
35 NAPI_CHECK_FATAL(napi_create_object(env, &targetObj));
36
37 std::array<napi_value, 2U> args = {targetObj, handlerObj};
38 napi_value proxy = ctx->GetCommonJSObjectCache()->GetProxy();
39 napi_value proxyObj;
40 NAPI_CHECK_FATAL(napi_new_instance(env, proxy, args.size(), args.data(), &proxyObj));
41
42 storage->CreateETSObjectRef(ctx, obj, proxyObj);
43
44 return proxyObj;
45 }
46
RecordGetHandler(napi_env env,napi_callback_info cbinfo)47 napi_value JSRefConvertRecord::RecordGetHandler(napi_env env, napi_callback_info cbinfo)
48 {
49 auto coro = EtsCoroutine::GetCurrent();
50 auto ctx = InteropCtx::Current(coro);
51 INTEROP_CODE_SCOPE_JS(coro);
52
53 size_t argc;
54 NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, nullptr, nullptr, nullptr));
55 auto jsArgs = ctx->GetTempArgs<napi_value>(argc);
56 NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, jsArgs->data(), nullptr, nullptr));
57
58 std::string value = GetString(env, jsArgs[1]);
59 if (value == ets_proxy::SharedReferenceStorage::PROXY_NAPI_WRAPPER) {
60 napi_value result;
61 napi_get_property(env, jsArgs->data()[0], jsArgs->data()[1], &result);
62 return result;
63 }
64
65 ScopedManagedCodeThread managedScope(coro);
66
67 ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
68 ASSERT(storage != nullptr);
69 ets_proxy::SharedReference *sharedRef = storage->GetReference(env, jsArgs->data()[0]);
70 ASSERT(sharedRef != nullptr);
71
72 auto *etsThis = sharedRef->GetEtsObject();
73 ASSERT(etsThis != nullptr);
74
75 Method *getMethod = PlatformTypes()->escompatRecordGetter->GetPandaMethod();
76
77 if (argc < 2U) {
78 INTEROP_LOG(ERROR) << "Invalid number of arguments for $_get";
79 return nullptr;
80 }
81
82 Span sp(&jsArgs[1], 1);
83 return CallETSInstance(coro, ctx, getMethod, sp, etsThis);
84 }
85
RecordSetHandler(napi_env env,napi_callback_info cbinfo)86 napi_value JSRefConvertRecord::RecordSetHandler(napi_env env, napi_callback_info cbinfo)
87 {
88 auto coro = EtsCoroutine::GetCurrent();
89 auto ctx = InteropCtx::Current(coro);
90 INTEROP_CODE_SCOPE_JS(coro);
91 ScopedManagedCodeThread managedScope(coro);
92
93 size_t argc;
94 NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, nullptr, nullptr, nullptr));
95 auto jsArgs = ctx->GetTempArgs<napi_value>(argc);
96 NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, jsArgs->data(), nullptr, nullptr));
97
98 ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
99 ASSERT(storage != nullptr);
100 ets_proxy::SharedReference *sharedRef = storage->GetReference(env, jsArgs->data()[0]);
101 ASSERT(sharedRef != nullptr);
102
103 auto *etsThis = sharedRef->GetEtsObject();
104 ASSERT(etsThis != nullptr);
105
106 Method *setMethod = PlatformTypes()->escompatRecordSetter->GetPandaMethod();
107
108 if (argc < 3U) {
109 INTEROP_LOG(ERROR) << "Invalid number of arguments for $_set";
110 return nullptr;
111 }
112
113 Span sp(&jsArgs[1], 2U);
114
115 CallETSInstance(coro, ctx, setMethod, sp, etsThis);
116
117 napi_value trueValue = GetBooleanValue(env, true);
118 return trueValue;
119 }
120
UnwrapImpl(InteropCtx * ctx,napi_value jsValue)121 EtsObject *JSRefConvertRecord::UnwrapImpl([[maybe_unused]] InteropCtx *ctx, [[maybe_unused]] napi_value jsValue)
122 {
123 napi_env env = ctx->GetJSEnv();
124 ets_proxy::SharedReference *sharedRef = ctx->GetSharedRefStorage()->GetReference(env, jsValue);
125
126 if (LIKELY(sharedRef != nullptr)) {
127 EtsObject *etsObject = sharedRef->GetEtsObject();
128 if (UNLIKELY(!PlatformTypes()->escompatRecord->IsAssignableFrom(etsObject->GetClass()))) {
129 InteropCtx::ThrowJSTypeError(env, std::string("Object is not compatible with escompat.Record"));
130 return nullptr;
131 }
132
133 return etsObject;
134 }
135
136 InteropCtx::ThrowJSTypeError(env, std::string("Object is not compatible with escompat.Record"));
137 return nullptr;
138 }
139
140 } // namespace ark::ets::interop::js