1 /**
2 * Copyright (c) 2023-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 #ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTRINSICS_API_IMPL_H_
17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTRINSICS_API_IMPL_H_
18
19 #include "code_scopes.h"
20 #include "plugins/ets/runtime/interop_js/js_convert.h"
21 #include "plugins/ets/runtime/interop_js/interop_common.h"
22 #include "plugins/ets/runtime/interop_js/interop_context.h"
23 #include "types/ets_object.h"
24 #include "types/ets_typeapi_type.h"
25
26 namespace ark::ets::interop::js {
27
28 void JSRuntimeFinalizationRegistryCallback(EtsObject *cbarg);
29 JSValue *JSRuntimeNewJSValueDouble(double v);
30 JSValue *JSRuntimeNewJSValueBoolean(uint8_t v);
31 JSValue *JSRuntimeNewJSValueString(EtsString *v);
32 JSValue *JSRuntimeNewJSValueObject(EtsObject *v);
33 uint8_t JSRuntimeIsJSValue(EtsObject *v);
34 JSValue *JSRuntimeNewJSValueBigInt(EtsBigInt *v);
35 double JSRuntimeGetValueDouble(JSValue *etsJsValue);
36 uint8_t JSRuntimeGetValueBoolean(JSValue *etsJsValue);
37 EtsString *JSRuntimeGetValueString(JSValue *etsJsValue);
38 EtsObject *JSRuntimeGetValueObject(JSValue *etsJsValue, EtsClass *clsObj);
39 JSValue *JSRuntimeGetUndefined();
40 JSValue *JSRuntimeGetNull();
41 JSValue *JSRuntimeGetGlobal();
42 JSValue *JSRuntimeCreateObject();
43 JSValue *JSRuntimeCreateArray();
44 uint8_t JSRuntimeInstanceOf(JSValue *object, JSValue *ctor);
45 uint8_t JSRuntimeInstanceOfDynamic(JSValue *object, JSValue *ctor);
46 uint8_t JSRuntimeInstanceOfStatic(JSValue *etsJsValue, EtsClass *etsCls);
47 std::pair<std::string_view, std::string_view> ResolveModuleName(std::string_view module);
48 JSValue *JSRuntimeLoadModule(EtsString *module);
49 uint8_t JSRuntimeStrictEqual([[maybe_unused]] JSValue *lhs, [[maybe_unused]] JSValue *rhs);
50 uint8_t JSRuntimeHasProperty(JSValue *object, EtsString *name);
51 JSValue *JSRuntimeGetProperty(JSValue *object, JSValue *property);
52 uint8_t JSRuntimeHasPropertyJSValue(JSValue *object, JSValue *property);
53 uint8_t JSRuntimeHasElement(JSValue *object, int index);
54 uint8_t JSRuntimeHasOwnProperty(JSValue *object, EtsString *name);
55 uint8_t JSRuntimeHasOwnPropertyJSValue(JSValue *object, JSValue *property);
56 EtsString *JSRuntimeTypeOf(JSValue *object);
57 uint8_t JSRuntimeIsPromise(JSValue *object);
58 uint8_t JSRuntimeInstanceOfStaticType(JSValue *object, EtsTypeAPIType *paramType);
59 JSValue *JSRuntimeInvoke(JSValue *recv, JSValue *func, EtsArray *args);
60 JSValue *JSRuntimeInstantiate(JSValue *callable, EtsArray *args);
61 EtsString *JSValueToString(JSValue *object);
62 napi_value ToLocal(void *value);
63 void *CompilerGetJSNamedProperty(void *val, char *propStr);
64 void *CompilerGetJSProperty(void *val, void *prop);
65 void *CompilerGetJSElement(void *val, int32_t index);
66 void *CompilerJSCallCheck(void *fn);
67 void *CompilerJSNewInstance(void *fn, uint32_t argc, void *args);
68 void CreateLocalScope();
69 void CompilerDestroyLocalScope();
70 void *CompilerLoadJSConstantPool();
71 void CompilerInitJSCallClassForCtx(void *klassPtr);
72 void *CompilerConvertVoidToLocal();
73 void *CompilerConvertRefTypeToLocal(EtsObject *etsValue);
74 void PromiseInteropResolve(EtsObject *etsValue, EtsLong deferred);
75 void PromiseInteropReject(EtsObject *etsValue, EtsLong deferred);
76 EtsString *JSONStringify(JSValue *jsvalue);
77 EtsString *CompilerConvertLocalToString(void *value);
78 EtsObject *CompilerConvertLocalToRefType(void *klassPtr, void *value);
79 JSValue *JSRuntimeGetPropertyJSValueyByKey(JSValue *objectValue, JSValue *keyValue);
80 EtsEscompatArrayBuffer *TransferArrayBufferToStatic(ESValue *object);
81 EtsObject *TransferArrayBufferToDynamic(EtsEscompatArrayBuffer *staticArrayBuffer);
82 EtsObject *CreateDynamicTypedArray(EtsEscompatArrayBuffer *staticArrayBuffer, int32_t typedArrayType, double length,
83 double byteOffset);
84 EtsObject *CreateDynamicDataView(EtsEscompatArrayBuffer *staticArrayBuffer, double byteLength, double byteOffset);
85 void SetInteropRuntimeLinker(EtsRuntimeLinker *linker);
86
87 template <typename T>
JSValueNamedGetter(JSValue * etsJsValue,EtsString * etsPropName)88 typename T::cpptype JSValueNamedGetter(JSValue *etsJsValue, EtsString *etsPropName)
89 {
90 auto coro = EtsCoroutine::GetCurrent();
91 auto ctx = InteropCtx::Current(coro);
92 if (ctx == nullptr) {
93 ThrowNoInteropContextException();
94 return {};
95 }
96 INTEROP_CODE_SCOPE_ETS(coro);
97 auto env = ctx->GetJSEnv();
98 NapiScope jsHandleScope(env);
99
100 PandaString propName = etsPropName->GetMutf8();
101 auto res = JSValueGetByName<T>(ctx, etsJsValue, propName.c_str());
102 if (UNLIKELY(!res)) {
103 ctx->ForwardJSException(coro);
104 return {};
105 }
106 return res.value();
107 }
108
109 template <typename T>
JSValueNamedSetter(JSValue * etsJsValue,EtsString * etsPropName,typename T::cpptype etsPropVal)110 void JSValueNamedSetter(JSValue *etsJsValue, EtsString *etsPropName, typename T::cpptype etsPropVal)
111 {
112 auto coro = EtsCoroutine::GetCurrent();
113 auto ctx = InteropCtx::Current(coro);
114 if (ctx == nullptr) {
115 ThrowNoInteropContextException();
116 return;
117 }
118 INTEROP_CODE_SCOPE_ETS(coro);
119 auto env = ctx->GetJSEnv();
120 NapiScope jsHandleScope(env);
121
122 PandaString propName = etsPropName->GetMutf8();
123 JSValueSetByName<T>(ctx, etsJsValue, propName.c_str(), etsPropVal);
124 }
125
126 template <typename T>
JSValueIndexedGetter(JSValue * etsJsValue,int32_t index)127 typename T::cpptype JSValueIndexedGetter(JSValue *etsJsValue, int32_t index)
128 {
129 auto coro = EtsCoroutine::GetCurrent();
130 auto ctx = InteropCtx::Current(coro);
131 if (ctx == nullptr) {
132 ThrowNoInteropContextException();
133 return {};
134 }
135 INTEROP_CODE_SCOPE_ETS(coro);
136 auto env = ctx->GetJSEnv();
137 NapiScope jsHandleScope(env);
138
139 napi_value result;
140 napi_value jsVal = etsJsValue->GetNapiValue(env);
141 napi_status jsStatus;
142 {
143 ScopedNativeCodeThread nativeScope(coro);
144 jsStatus = napi_get_element(env, jsVal, index, &result);
145 }
146
147 if (jsStatus != napi_ok) {
148 ctx->ForwardJSException(coro);
149 return {};
150 }
151
152 auto res = T::UnwrapWithNullCheck(ctx, env, result);
153 if (!res) {
154 ctx->ForwardJSException(coro);
155 return {};
156 }
157
158 return res.value();
159 }
160
161 template <typename T>
JSValueIndexedSetter(JSValue * etsJsValue,int32_t index,typename T::cpptype value)162 void JSValueIndexedSetter(JSValue *etsJsValue, int32_t index, typename T::cpptype value)
163 {
164 auto coro = EtsCoroutine::GetCurrent();
165 auto ctx = InteropCtx::Current(coro);
166 INTEROP_CODE_SCOPE_ETS(coro);
167 auto env = ctx->GetJSEnv();
168 NapiScope jsHandleScope(env);
169 napi_status jsStatus;
170 {
171 ScopedNativeCodeThread nativeScope(coro);
172
173 jsStatus = napi_set_element(env, JSConvertJSValue::WrapWithNullCheck(env, etsJsValue), index,
174 T::WrapWithNullCheck(env, value));
175 }
176 if (jsStatus != napi_ok) {
177 ctx->ForwardJSException(coro);
178 }
179 }
180
181 template <typename T>
ConvertToLocal(typename T::cpptype etsValue)182 void *ConvertToLocal(typename T::cpptype etsValue)
183 {
184 auto coro = EtsCoroutine::GetCurrent();
185 auto ctx = InteropCtx::Current(coro);
186 if (ctx == nullptr) {
187 ThrowNoInteropContextException();
188 return nullptr;
189 }
190 INTEROP_CODE_SCOPE_ETS(coro);
191 napi_env env = ctx->GetJSEnv();
192 napi_value localJsValue = T::Wrap(env, etsValue);
193 if (UNLIKELY(localJsValue == nullptr)) {
194 ctx->ForwardJSException(coro);
195 return nullptr;
196 }
197 return localJsValue;
198 }
199
200 template <bool USE_RET>
CompilerJSCallFunction(void * obj,void * fn,uint32_t argc,void * args)201 std::conditional_t<USE_RET, void *, void> CompilerJSCallFunction(void *obj, void *fn, uint32_t argc, void *args)
202 {
203 auto jsThis = ToLocal(obj);
204 auto jsFn = ToLocal(fn);
205 auto jsArgs = reinterpret_cast<napi_value *>(args);
206 auto coro = EtsCoroutine::GetCurrent();
207
208 InteropCtx *ctx = nullptr;
209 if constexpr (USE_RET) {
210 ctx = InteropCtx::Current(coro);
211 if (ctx == nullptr) {
212 ThrowNoInteropContextException();
213 return nullptr;
214 }
215 } else {
216 ctx = InteropCtx::Current(coro);
217 if (ctx == nullptr) {
218 ThrowNoInteropContextException();
219 return;
220 }
221 }
222 ASSERT(ctx != nullptr);
223
224 INTEROP_CODE_SCOPE_ETS(coro);
225 napi_env env = ctx->GetJSEnv();
226
227 [[maybe_unused]] napi_value jsRet;
228 napi_status jsStatus;
229 {
230 ScopedNativeCodeThread nativeScope(coro);
231 if constexpr (USE_RET) {
232 jsStatus = napi_call_function(env, jsThis, jsFn, argc, jsArgs, &jsRet);
233 } else {
234 jsStatus = napi_call_function(env, jsThis, jsFn, argc, jsArgs, nullptr);
235 }
236 }
237
238 if (UNLIKELY(jsStatus != napi_ok)) {
239 INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
240 ctx->ForwardJSException(coro);
241 INTEROP_LOG(DEBUG) << "JSValueJSCall: exit with pending exception";
242 if constexpr (USE_RET) {
243 return nullptr;
244 }
245 }
246 if constexpr (USE_RET) {
247 return jsRet;
248 }
249 }
250
HandleExceptions(napi_env env,InteropCtx * ctx)251 inline void HandleExceptions(napi_env env, InteropCtx *ctx)
252 {
253 if (NapiIsExceptionPending(env)) {
254 ctx->ForwardJSException(EtsCoroutine::GetCurrent());
255 }
256 ASSERT(ctx->SanityETSExceptionPending());
257 }
258
259 template <typename CONVERTOR>
ConvertFromLocal(void * value)260 typename CONVERTOR::cpptype ConvertFromLocal(void *value)
261 {
262 auto coro = EtsCoroutine::GetCurrent();
263 auto ctx = InteropCtx::Current(coro);
264 if (ctx == nullptr) {
265 ThrowNoInteropContextException();
266 if constexpr (!std::is_pointer_v<typename CONVERTOR::cpptype>) {
267 return 0;
268 } else {
269 return nullptr;
270 }
271 }
272 INTEROP_CODE_SCOPE_ETS(coro);
273 napi_env env = ctx->GetJSEnv();
274 auto res = CONVERTOR::Unwrap(ctx, env, ToLocal(value));
275 if (UNLIKELY(!res.has_value())) {
276 HandleExceptions(env, ctx);
277 if constexpr (!std::is_pointer_v<typename CONVERTOR::cpptype>) {
278 return 0;
279 } else {
280 return nullptr;
281 }
282 }
283 return res.value();
284 }
285
286 template <>
287 // CC-OFFNXT(G.FUD.06) solid logic
288 inline JSValue *ConvertFromLocal<JSConvertJSValue>(void *value)
289 {
290 INTEROP_CODE_SCOPE_ETS(EtsCoroutine::GetCurrent());
291
292 auto ctx = InteropCtx::Current(EtsCoroutine::GetCurrent());
293 if (ctx == nullptr) {
294 ThrowNoInteropContextException();
295 return nullptr;
296 }
297 if (IsUndefined(ctx->GetJSEnv(), ToLocal(value))) {
298 return nullptr;
299 }
300 if (auto res = JSConvertJSValue::Unwrap(ctx, ctx->GetJSEnv(), ToLocal(value)); res.has_value()) {
301 return res.value();
302 }
303 HandleExceptions(ctx->GetJSEnv(), ctx);
304 return nullptr;
305 }
306
307 } // namespace ark::ets::interop::js
308
309 #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTRINSICS_API_IMPL_H_
310