1 /**
2 * Copyright (c) 2021-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/ets_class_linker_extension.h"
17 #include "plugins/ets/runtime/interop_js/interop_context.h"
18 #include "plugins/ets/runtime/interop_js/interop_common.h"
19 #include "plugins/ets/runtime/interop_js/js_refconvert.h"
20 #include "plugins/ets/runtime/interop_js/js_convert.h"
21 #include "runtime/mem/local_object_handle.h"
22
23 #include "plugins/ets/runtime/interop_js/js_refconvert_array.h"
24
25 // NOLINTBEGIN(readability-identifier-naming)
26 // CC-OFF(G.FMT.07) project code style
27 napi_status __attribute__((weak)) napi_is_map(napi_env env, napi_value value, bool *result);
28 // CC-OFF(G.FMT.07) project code style
29 napi_status __attribute__((weak)) napi_is_set(napi_env env, napi_value value, bool *result);
30 // NOLINTEND(readability-identifier-naming)
31
32 namespace ark::ets::interop::js {
33
34 // JSRefConvert adapter for builtin reference types
35 template <typename Conv>
36 class JSRefConvertBuiltin : public JSRefConvert {
37 public:
JSRefConvertBuiltin()38 JSRefConvertBuiltin() : JSRefConvert(this) {}
39
WrapImpl(InteropCtx * ctx,EtsObject * obj)40 napi_value WrapImpl(InteropCtx *ctx, EtsObject *obj)
41 {
42 using ObjType = std::remove_pointer_t<typename Conv::cpptype>;
43 return Conv::Wrap(ctx->GetJSEnv(), FromEtsObject<ObjType>(obj));
44 }
45
UnwrapImpl(InteropCtx * ctx,napi_value jsValue)46 EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value jsValue)
47 {
48 auto res = Conv::Unwrap(ctx, ctx->GetJSEnv(), jsValue);
49 if (!res) {
50 return nullptr;
51 }
52 return AsEtsObject(res.value());
53 }
54 };
55
56 template <typename Conv>
RegisterBuiltinRefConvertor(JSRefConvertCache * cache,Class * klass)57 static inline void RegisterBuiltinRefConvertor(JSRefConvertCache *cache, Class *klass)
58 {
59 [[maybe_unused]] bool res = CheckClassInitialized<true>(klass);
60 ASSERT(res);
61 cache->Insert(klass, std::make_unique<JSRefConvertBuiltin<Conv>>());
62 }
63
RegisterEtsProxyForStdClass(InteropCtx * ctx,std::string_view descriptor,char const * jsBuiltinName=nullptr,const ets_proxy::EtsClassWrapper::OverloadsMap * overloads=nullptr)64 static ets_proxy::EtsClassWrapper *RegisterEtsProxyForStdClass(
65 InteropCtx *ctx, std::string_view descriptor, char const *jsBuiltinName = nullptr,
66 const ets_proxy::EtsClassWrapper::OverloadsMap *overloads = nullptr)
67 {
68 auto coro = EtsCoroutine::GetCurrent();
69 ASSERT(coro != nullptr);
70 PandaEtsVM *vm = coro->GetPandaVM();
71 EtsClassLinker *etsClassLinker = vm->GetClassLinker();
72 auto etsClass = etsClassLinker->GetClass(descriptor.data());
73 if (UNLIKELY(etsClass == nullptr)) {
74 ctx->Fatal(std::string("nonexisting class ") + descriptor.data());
75 }
76
77 // create ets_proxy bound to js builtin-constructor
78 ets_proxy::EtsClassWrappersCache *cache = ctx->GetEtsClassWrappersCache();
79 std::unique_ptr<ets_proxy::EtsClassWrapper> wrapper =
80 ets_proxy::EtsClassWrapper::Create(ctx, etsClass, jsBuiltinName, overloads);
81 if (UNLIKELY(wrapper == nullptr)) {
82 ctx->Fatal(std::string("ets_proxy creation failed for ") + etsClass->GetDescriptor());
83 }
84 return cache->Insert(etsClass, std::move(wrapper));
85 }
86
87 namespace {
88
89 namespace descriptors = panda_file_items::class_descriptors;
90
91 [[maybe_unused]] constexpr const ets_proxy::EtsClassWrapper::OverloadsMap *NO_OVERLOADS = nullptr;
92 constexpr const char *NO_MIRROR = nullptr;
93
94 class CompatConvertorsRegisterer {
95 private:
NotImplemented(char const * name)96 [[noreturn]] void NotImplemented(char const *name) __attribute__((noinline))
97 {
98 InteropCtx::Fatal(std::string("compat.") + name + " box is not implemented");
99 }
100
NotAssignable(char const * name)101 EtsObject *NotAssignable(char const *name) __attribute__((noinline))
102 {
103 JSConvertTypeCheckFailed(name);
104 return nullptr;
105 };
106
StdCtorRef(InteropCtx * ctxx,char const * name)107 napi_ref StdCtorRef(InteropCtx *ctxx, char const *name)
108 {
109 napi_env env = ctxx->GetJSEnv();
110 napi_value val;
111 NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), name, &val));
112 INTEROP_FATAL_IF(IsNull(env, val));
113 napi_ref ref;
114 NAPI_CHECK_FATAL(napi_create_reference(env, val, 1, &ref));
115 return ref;
116 }
117
CheckInstanceof(napi_env env,napi_value val,napi_ref ctorRef)118 bool CheckInstanceof(napi_env env, napi_value val, napi_ref ctorRef)
119 {
120 bool result;
121 NAPI_CHECK_FATAL(napi_instanceof(env, val, GetReferenceValue(env, ctorRef), &result));
122 return result;
123 }
124
125 template <typename ConvTag>
BuiltinConvert(InteropCtx * inCtx,napi_env env,napi_value jsValue)126 EtsObject *BuiltinConvert(InteropCtx *inCtx, napi_env env, napi_value jsValue)
127 {
128 auto res = ConvTag::UnwrapImpl(inCtx, env, jsValue);
129 if (UNLIKELY(!res.has_value())) {
130 return nullptr;
131 }
132 return AsEtsObject(res.value());
133 }
134
RegisterExceptions()135 void RegisterExceptions()
136 {
137 static const ets_proxy::EtsClassWrapper::OverloadsMap W_ERROR_OVERLOADS {
138 {utf::CStringAsMutf8("<ctor>"), std::make_pair("Lstd/core/String;Lescompat/ErrorOptions;:V", 2)}};
139 static const ets_proxy::EtsClassWrapper::OverloadsMap W_EXCEPTION_OVERLOADS {
140 {utf::CStringAsMutf8("<ctor>"), std::make_pair("Lstd/core/String;:V", 1)}};
141 wError_ = RegisterClass(descriptors::ERROR, "Error", &W_ERROR_OVERLOADS);
142 RegisterClass(descriptors::EXCEPTION, nullptr, &W_EXCEPTION_OVERLOADS);
143
144 static const std::array STD_EXCEPTIONS_LIST = {
145 // Errors
146 std::make_tuple("Lstd/core/AssertionError;", NO_MIRROR, &W_ERROR_OVERLOADS),
147 std::make_tuple("Lstd/core/DivideByZeroError;", NO_MIRROR, &W_ERROR_OVERLOADS),
148 std::make_tuple("Lstd/core/NullPointerError;", NO_MIRROR, &W_ERROR_OVERLOADS),
149 std::make_tuple("Lstd/core/UncaughtExceptionError;", NO_MIRROR, &W_ERROR_OVERLOADS),
150 std::make_tuple("Lstd/core/ArithmeticError;", NO_MIRROR, &W_ERROR_OVERLOADS),
151 std::make_tuple("Lstd/core/ClassCastError;", NO_MIRROR, &W_ERROR_OVERLOADS),
152 std::make_tuple("Lstd/core/IndexOutOfBoundsError;", NO_MIRROR, &W_ERROR_OVERLOADS),
153 std::make_tuple("Lstd/core/ArrayStoreError;", NO_MIRROR, &W_ERROR_OVERLOADS),
154 std::make_tuple("Lstd/core/NegativeArraySizeError;", NO_MIRROR, &W_ERROR_OVERLOADS),
155 std::make_tuple("Lstd/core/LinkerError;", NO_MIRROR, &W_ERROR_OVERLOADS),
156 std::make_tuple("Lstd/core/LinkerClassNotFoundError;", NO_MIRROR, &W_ERROR_OVERLOADS),
157 // Exceptions
158 std::make_tuple("Lstd/core/NoDataException;", NO_MIRROR, &W_EXCEPTION_OVERLOADS),
159 std::make_tuple("Lstd/core/ArgumentOutOfRangeException;", NO_MIRROR, &W_EXCEPTION_OVERLOADS),
160 std::make_tuple("Lstd/core/IllegalStateException;", NO_MIRROR, &W_EXCEPTION_OVERLOADS),
161 std::make_tuple("Lstd/core/UnsupportedOperationException;", NO_MIRROR, &W_EXCEPTION_OVERLOADS),
162 std::make_tuple("Lstd/core/IllegalMonitorStateError;", NO_MIRROR, &W_EXCEPTION_OVERLOADS),
163 std::make_tuple("Lstd/core/IllegalArgumentException;", NO_MIRROR, &W_EXCEPTION_OVERLOADS),
164 std::make_tuple("Lstd/core/RuntimeException;", NO_MIRROR, &W_EXCEPTION_OVERLOADS),
165 };
166 for (const auto &[descr, mirror, ovl] : STD_EXCEPTIONS_LIST) {
167 RegisterClass(descr, mirror, ovl);
168 }
169 }
170
RegisterClass(std::string_view descriptor,char const * jsBuiltinName=nullptr,const ets_proxy::EtsClassWrapper::OverloadsMap * overloads=nullptr)171 ets_proxy::EtsClassWrapper *RegisterClass(std::string_view descriptor, char const *jsBuiltinName = nullptr,
172 const ets_proxy::EtsClassWrapper::OverloadsMap *overloads = nullptr)
173 {
174 ets_proxy::EtsClassWrapper *wclass = RegisterEtsProxyForStdClass(ctx_, descriptor, jsBuiltinName, overloads);
175 auto env = ctx_->GetJSEnv();
176 auto name = wclass->GetEtsClass()->GetRuntimeClass()->GetName();
177 auto jsCtor = wclass->GetJsCtor(env);
178 NAPI_CHECK_FATAL(napi_set_named_property(env, jsGlobalEts_, name.c_str(), jsCtor));
179 if (jsBuiltinName != nullptr) {
180 NAPI_CHECK_FATAL(napi_set_named_property(env, jsGlobalEts_, jsBuiltinName, jsCtor));
181 }
182 return wclass;
183 }
184
RegisterClassWithLeafMatcher(std::string_view descriptor,char const * jsBuiltinName=nullptr,const ets_proxy::EtsClassWrapper::OverloadsMap * overloads=nullptr)185 ets_proxy::EtsClassWrapper *RegisterClassWithLeafMatcher(
186 std::string_view descriptor, char const *jsBuiltinName = nullptr,
187 const ets_proxy::EtsClassWrapper::OverloadsMap *overloads = nullptr)
188 {
189 ets_proxy::EtsClassWrapper *wclass = RegisterClass(descriptor, jsBuiltinName, overloads);
190 wclass->SetJSBuiltinMatcher(
191 [self = *this, wclass, descriptor](InteropCtx *ctxx, napi_value jsValue, bool verified = true) mutable {
192 (void)verified;
193 auto env = ctxx->GetJSEnv();
194 napi_value constructor;
195 bool match = false;
196 NAPI_CHECK_FATAL(napi_get_named_property(env, jsValue, "constructor", &constructor));
197 NAPI_CHECK_FATAL(napi_strict_equals(env, constructor, wclass->GetBuiltin(env), &match));
198 if (match) {
199 return wclass->CreateJSBuiltinProxy(ctxx, jsValue);
200 }
201 return self.NotAssignable(descriptor.data());
202 });
203 return wclass;
204 }
205
RegisterArray()206 void RegisterArray()
207 {
208 static const ets_proxy::EtsClassWrapper::OverloadsMap W_ARRAY_OVERLOADS = {
209 {utf::CStringAsMutf8("<ctor>"), std::make_pair(":V", 1)},
210 {utf::CStringAsMutf8("$_get"), std::make_pair("I:Lstd/core/Object;", 2)},
211 {utf::CStringAsMutf8("$_set"), std::make_pair("ILstd/core/Object;:V", 3)},
212 {utf::CStringAsMutf8("at"), std::make_pair("I:Lstd/core/Object;", 2)},
213 {utf::CStringAsMutf8("copyWithin"), std::make_pair("III:Lescompat/Array;", 4)},
214 {utf::CStringAsMutf8("fill"),
215 std::make_pair("Lstd/core/Object;Lstd/core/Double;Lstd/core/Double;:Lescompat/Array;", 4)},
216 {utf::CStringAsMutf8("indexOf"), std::make_pair("Lstd/core/Object;Lstd/core/Double;:D", 3)},
217 {utf::CStringAsMutf8("lastIndexOf"), std::make_pair("Lstd/core/Object;I:I", 3)},
218 {utf::CStringAsMutf8("slice"), std::make_pair("II:Lescompat/Array;", 3)},
219 {utf::CStringAsMutf8("splice"), std::make_pair("I:Lescompat/Array;", 2)},
220 {utf::CStringAsMutf8("splice"), std::make_pair("IILescompat/Array;:Lescompat/Array;", 3)},
221 {utf::CStringAsMutf8("toSpliced"), std::make_pair("II[Lstd/core/Object;:Lescompat/Array;", 3)},
222 {utf::CStringAsMutf8("with"), std::make_pair("DLstd/core/Object;:Lescompat/Array;", 3)},
223 };
224 wArray_ = RegisterClass(descriptors::ARRAY, "Array", &W_ARRAY_OVERLOADS);
225 NAPI_CHECK_FATAL(napi_object_seal(ctx_->GetJSEnv(), jsGlobalEts_));
226 }
227
RegisterError()228 void RegisterError()
229 {
230 wRangeError_ = RegisterClassWithLeafMatcher(descriptors::RANGE_ERROR, "RangeError");
231 wReferenceError_ = RegisterClassWithLeafMatcher(descriptors::REFERENCE_ERROR, "ReferenceError");
232 wSyntaxError_ = RegisterClassWithLeafMatcher(descriptors::SYNTAX_ERROR, "SyntaxError");
233 wURIError_ = RegisterClassWithLeafMatcher(descriptors::URI_ERROR, "URIError");
234 wTypeError_ = RegisterClassWithLeafMatcher(descriptors::TYPE_ERROR, "TypeError");
235 ASSERT(wRangeError_ != nullptr);
236 ASSERT(wReferenceError_ != nullptr);
237 ASSERT(wSyntaxError_ != nullptr);
238 ASSERT(wURIError_ != nullptr);
239 ASSERT(wTypeError_ != nullptr);
240
241 ctorRangeError_ = StdCtorRef(ctx_, "RangeError");
242 ctorReferenceError_ = StdCtorRef(ctx_, "ReferenceError");
243 ctorSyntaxError_ = StdCtorRef(ctx_, "SyntaxError");
244 ctorURIError_ = StdCtorRef(ctx_, "URIError");
245 ctorTypeError_ = StdCtorRef(ctx_, "TypeError");
246 ASSERT(ctorRangeError_ != nullptr);
247 ASSERT(ctorReferenceError_ != nullptr);
248 ASSERT(ctorSyntaxError_ != nullptr);
249 ASSERT(ctorURIError_ != nullptr);
250 ASSERT(ctorTypeError_ != nullptr);
251 }
252
RegisterMap()253 void RegisterMap()
254 {
255 static const ets_proxy::EtsClassWrapper::OverloadsMap W_MAP_OVERLOADS = {
256 {utf::CStringAsMutf8("<ctor>"), std::make_pair("Lstd/core/Object;:V", 2)}};
257 wMap_ = RegisterClassWithLeafMatcher(descriptors::MAP, "Map", &W_MAP_OVERLOADS);
258 }
259
RegisterSet()260 void RegisterSet()
261 {
262 static const ets_proxy::EtsClassWrapper::OverloadsMap W_SET_OVERLOADS = {
263 {utf::CStringAsMutf8("<ctor>"), std::make_pair("Lstd/core/Object;:V", 2)}};
264 wSet_ = RegisterClassWithLeafMatcher(descriptors::SET, "Set", &W_SET_OVERLOADS);
265 }
266
RegisterDate()267 void RegisterDate()
268 {
269 static const ets_proxy::EtsClassWrapper::OverloadsMap W_DATE_OVERLOADS {
270 {utf::CStringAsMutf8("setDate"), std::make_pair("D:D", 2)},
271 {utf::CStringAsMutf8("setUTCDate"), std::make_pair("D:D", 2)},
272 {utf::CStringAsMutf8("setMilliseconds"), std::make_pair("D:D", 2)},
273 {utf::CStringAsMutf8("setUTCMilliseconds"), std::make_pair("D:D", 2)},
274 {utf::CStringAsMutf8("setTime"), std::make_pair("J:V", 2)},
275 };
276 wDate_ = RegisterClassWithLeafMatcher(descriptors::DATE, "Date", &W_DATE_OVERLOADS);
277 }
278
MArray(InteropCtx * ctxx,napi_value jsValue,bool verified=true)279 EtsObject *MArray(InteropCtx *ctxx, napi_value jsValue, bool verified = true)
280 {
281 napi_env env = ctxx->GetJSEnv();
282 bool isInstanceof;
283 if (!verified) {
284 NAPI_CHECK_FATAL(napi_is_array(env, jsValue, &isInstanceof));
285 if (!isInstanceof) {
286 return NotAssignable("Array");
287 }
288 }
289 return wArray_->CreateJSBuiltinProxy(ctxx, jsValue);
290 }
291
MMap(InteropCtx * ctxx,napi_value jsValue,bool verified=true)292 EtsObject *MMap(InteropCtx *ctxx, napi_value jsValue, bool verified = true)
293 {
294 napi_env env = ctxx->GetJSEnv();
295 bool isInstanceof;
296 if (!verified) {
297 NAPI_CHECK_FATAL(napi_is_map(env, jsValue, &isInstanceof));
298 if (!isInstanceof) {
299 return NotAssignable("Map");
300 }
301 }
302 return wMap_->CreateJSBuiltinProxy(ctxx, jsValue);
303 }
304
MSet(InteropCtx * ctxx,napi_value jsValue,bool verified=true)305 EtsObject *MSet(InteropCtx *ctxx, napi_value jsValue, bool verified = true)
306 {
307 napi_env env = ctxx->GetJSEnv();
308 bool isInstanceof;
309 if (!verified) {
310 NAPI_CHECK_FATAL(napi_is_set(env, jsValue, &isInstanceof));
311 if (!isInstanceof) {
312 return NotAssignable("Set");
313 }
314 }
315 return wSet_->CreateJSBuiltinProxy(ctxx, jsValue);
316 }
317
MDate(InteropCtx * ctxx,napi_value jsValue,bool verified=true)318 EtsObject *MDate(InteropCtx *ctxx, napi_value jsValue, bool verified = true)
319 {
320 napi_env env = ctxx->GetJSEnv();
321 bool isInstanceof;
322 if (!verified) {
323 NAPI_CHECK_FATAL(napi_is_date(env, jsValue, &isInstanceof));
324 if (!isInstanceof) {
325 return NotAssignable("Date");
326 }
327 }
328 ASSERT(wDate_ != nullptr);
329 return wDate_->CreateJSBuiltinProxy(ctxx, jsValue);
330 }
331
RegisterObject()332 void RegisterObject()
333 {
334 wObject_ = RegisterClass(descriptors::OBJECT, "Object");
335 }
336
MError(InteropCtx * ctxx,napi_value jsValue,bool verified=true)337 EtsObject *MError(InteropCtx *ctxx, napi_value jsValue, bool verified = true)
338 {
339 napi_env env = ctxx->GetJSEnv();
340 bool isInstanceof;
341 if (!verified) {
342 NAPI_CHECK_FATAL(napi_is_error(env, jsValue, &isInstanceof));
343 if (!isInstanceof) {
344 return NotAssignable("Error");
345 }
346 }
347
348 if (CheckInstanceof(env, jsValue, ctorRangeError_)) {
349 return wRangeError_->CreateJSBuiltinProxy(ctxx, jsValue);
350 }
351 if (CheckInstanceof(env, jsValue, ctorReferenceError_)) {
352 return wReferenceError_->CreateJSBuiltinProxy(ctxx, jsValue);
353 }
354 if (CheckInstanceof(env, jsValue, ctorSyntaxError_)) {
355 return wSyntaxError_->CreateJSBuiltinProxy(ctxx, jsValue);
356 }
357 if (CheckInstanceof(env, jsValue, ctorURIError_)) {
358 return wURIError_->CreateJSBuiltinProxy(ctxx, jsValue);
359 }
360 if (CheckInstanceof(env, jsValue, ctorTypeError_)) {
361 return wTypeError_->CreateJSBuiltinProxy(ctxx, jsValue);
362 }
363
364 // NOTE(vpukhov): compat: remove when compat/Error is implemented
365 return BuiltinConvert<JSConvertESError>(ctxx, env, jsValue);
366 }
367
MObjectObject(InteropCtx * ctxx,napi_value jsValue)368 EtsObject *MObjectObject(InteropCtx *ctxx, napi_value jsValue)
369 {
370 napi_env env = ctxx->GetJSEnv();
371 bool isInstanceof;
372 NAPI_CHECK_FATAL(napi_is_array(env, jsValue, &isInstanceof));
373 if (isInstanceof) {
374 return MArray(ctxx, jsValue);
375 }
376 NAPI_CHECK_FATAL(napi_is_map(env, jsValue, &isInstanceof));
377 if (isInstanceof) {
378 return MMap(ctxx, jsValue);
379 }
380 NAPI_CHECK_FATAL(napi_is_set(env, jsValue, &isInstanceof));
381 if (isInstanceof) {
382 return MSet(ctxx, jsValue);
383 }
384 NAPI_CHECK_FATAL(napi_is_promise(env, jsValue, &isInstanceof));
385 if (isInstanceof) {
386 return BuiltinConvert<JSConvertPromise>(ctxx, env, jsValue);
387 }
388 NAPI_CHECK_FATAL(napi_is_error(env, jsValue, &isInstanceof));
389 if (isInstanceof) {
390 return MError(ctxx, jsValue);
391 }
392 NAPI_CHECK_FATAL(napi_is_date(env, jsValue, &isInstanceof));
393 if (isInstanceof) {
394 return MDate(ctxx, jsValue);
395 }
396 if (IsConstructor(env, jsValue, CONSTRUCTOR_NAME_NUMBER)) {
397 return BuiltinConvert<JSConvertStdlibDouble>(ctxx, env, jsValue);
398 }
399 if (IsConstructor(env, jsValue, CONSTRUCTOR_NAME_BOOLEAN)) {
400 return BuiltinConvert<JSConvertStdlibBoolean>(ctxx, env, jsValue);
401 }
402 if (IsConstructor(env, jsValue, CONSTRUCTOR_NAME_STRING)) {
403 return BuiltinConvert<JSConvertString>(ctxx, env, jsValue);
404 }
405 return BuiltinConvert<JSConvertJSValue>(ctxx, env, jsValue);
406 }
407
MObject(InteropCtx * ctxx,napi_value jsValue,bool verified=true)408 EtsObject *MObject(InteropCtx *ctxx, napi_value jsValue, bool verified = true)
409 {
410 napi_env env = ctxx->GetJSEnv();
411 (void)verified; // ignored for Object
412
413 napi_valuetype jsType = GetValueType(env, jsValue);
414 switch (jsType) {
415 case napi_boolean:
416 return BuiltinConvert<JSConvertStdlibBoolean>(ctxx, env, jsValue);
417 case napi_number:
418 return BuiltinConvert<JSConvertStdlibDouble>(ctxx, env, jsValue);
419 case napi_string:
420 return BuiltinConvert<JSConvertString>(ctxx, env, jsValue);
421 case napi_object:
422 return MObjectObject(ctx_, jsValue);
423 case napi_null:
424 return ctx_->GetNullValue();
425 case napi_symbol:
426 [[fallthrough]];
427 case napi_function:
428 [[fallthrough]];
429 case napi_external:
430 [[fallthrough]];
431 case napi_bigint:
432 return BuiltinConvert<JSConvertJSValue>(ctxx, env, jsValue);
433 default:
434 ASSERT(!IsNullOrUndefined(env, jsValue));
435 InteropCtx::Fatal("Bad jsType in Object value matcher");
436 };
437 }
438
439 public:
CompatConvertorsRegisterer(InteropCtx * ctx)440 explicit CompatConvertorsRegisterer(InteropCtx *ctx) : ctx_(ctx)
441 {
442 auto env = ctx_->GetJSEnv();
443 NAPI_CHECK_FATAL(napi_create_object(env, &jsGlobalEts_));
444 NAPI_CHECK_FATAL(napi_set_named_property(env, GetGlobal(env), "ets", jsGlobalEts_));
445 }
446
447 DEFAULT_MOVE_SEMANTIC(CompatConvertorsRegisterer);
448
449 DEFAULT_COPY_SEMANTIC(CompatConvertorsRegisterer);
450
451 ~CompatConvertorsRegisterer() = default;
452
Run()453 void Run()
454 {
455 RegisterObject();
456
457 RegisterExceptions();
458
459 RegisterDate();
460 // #IC4UO2
461 RegisterClassWithLeafMatcher(descriptors::MAPENTRY, nullptr);
462 RegisterClassWithLeafMatcher(descriptors::MAPITERATOR, nullptr);
463 RegisterClassWithLeafMatcher(descriptors::EMPTYMAPITERATOR, nullptr);
464
465 RegisterMap();
466 RegisterSet();
467
468 RegisterClassWithLeafMatcher(descriptors::ARRAY_ENTRIES_ITERATOR_T, nullptr);
469 RegisterClassWithLeafMatcher(descriptors::ITERATOR_RESULT, nullptr);
470 RegisterClassWithLeafMatcher(descriptors::ARRAY_KEYS_ITERATOR, nullptr);
471 RegisterClassWithLeafMatcher(descriptors::ARRAY_VALUES_ITERATOR_T, nullptr);
472
473 RegisterError();
474 RegisterArray();
475
476 ASSERT(wError_ != nullptr);
477 wError_->SetJSBuiltinMatcher(
478 [self = *this](InteropCtx *ctxx, napi_value jsValue, bool verified = true) mutable {
479 return self.MError(ctxx, jsValue, verified);
480 });
481
482 ASSERT(wObject_ != nullptr);
483 wObject_->SetJSBuiltinMatcher(
484 [self = *this](InteropCtx *ctxx, napi_value jsValue, bool verified = true) mutable {
485 return self.MObject(ctxx, jsValue, verified);
486 });
487
488 ASSERT(wArray_ != nullptr);
489 wArray_->SetJSBuiltinMatcher(
490 [self = *this](InteropCtx *ctxx, napi_value jsValue, bool verified = true) mutable {
491 return self.MArray(ctxx, jsValue, verified);
492 });
493 }
494
495 private:
496 InteropCtx *ctx_;
497 napi_value jsGlobalEts_ {};
498 ets_proxy::EtsClassWrapper *wError_ {};
499 ets_proxy::EtsClassWrapper *wObject_ {};
500 ets_proxy::EtsClassWrapper *wArray_ {};
501 ets_proxy::EtsClassWrapper *wDate_ {};
502 ets_proxy::EtsClassWrapper *wMap_ {};
503 ets_proxy::EtsClassWrapper *wSet_ {};
504 ets_proxy::EtsClassWrapper *wRangeError_ {};
505 ets_proxy::EtsClassWrapper *wReferenceError_ {};
506 ets_proxy::EtsClassWrapper *wSyntaxError_ {};
507 ets_proxy::EtsClassWrapper *wURIError_ {};
508 ets_proxy::EtsClassWrapper *wTypeError_ {};
509
510 napi_ref ctorTypeError_ {nullptr};
511 napi_ref ctorRangeError_ {nullptr};
512 napi_ref ctorReferenceError_ {nullptr};
513 napi_ref ctorSyntaxError_ = {nullptr};
514 napi_ref ctorURIError_ = {nullptr};
515 };
516
517 static_assert(std::is_trivially_copy_constructible_v<CompatConvertorsRegisterer>);
518 static_assert(std::is_trivially_copy_assignable_v<CompatConvertorsRegisterer>);
519 static_assert(std::is_trivially_move_constructible_v<CompatConvertorsRegisterer>);
520 static_assert(std::is_trivially_move_assignable_v<CompatConvertorsRegisterer>);
521 static_assert(std::is_trivially_destructible_v<CompatConvertorsRegisterer>);
522
523 } // namespace
524
RegisterCompatConvertors(InteropCtx * ctx)525 static void RegisterCompatConvertors(InteropCtx *ctx)
526 {
527 CompatConvertorsRegisterer(ctx).Run();
528 }
529
RegisterBuiltinJSRefConvertors(InteropCtx * ctx)530 void RegisterBuiltinJSRefConvertors(InteropCtx *ctx)
531 {
532 auto cache = ctx->GetRefConvertCache();
533 auto coro = EtsCoroutine::GetCurrent();
534 ASSERT(coro != nullptr);
535 PandaEtsVM *vm = coro->GetPandaVM();
536 EtsClassLinkerExtension *linkerExt = vm->GetClassLinker()->GetEtsClassLinkerExtension();
537 auto ptypes = PlatformTypes(coro);
538
539 RegisterBuiltinRefConvertor<JSConvertJSValue>(cache, ctx->GetJSValueClass());
540 RegisterBuiltinRefConvertor<JSConvertESError>(cache, ctx->GetESErrorClass());
541 RegisterBuiltinRefConvertor<JSConvertString>(cache, ctx->GetStringClass());
542 RegisterBuiltinRefConvertor<JSConvertBigInt>(cache, ctx->GetBigIntClass());
543 RegisterBuiltinRefConvertor<JSConvertPromise>(cache, ctx->GetPromiseClass());
544 RegisterBuiltinRefConvertor<JSConvertEtsNull>(cache, ctx->GetNullValueClass());
545
546 RegisterBuiltinRefConvertor<JSConvertStdlibBoolean>(cache, ptypes->coreBoolean->GetRuntimeClass());
547 RegisterBuiltinRefConvertor<JSConvertStdlibByte>(cache, ptypes->coreByte->GetRuntimeClass());
548 RegisterBuiltinRefConvertor<JSConvertStdlibChar>(cache, ptypes->coreChar->GetRuntimeClass());
549 RegisterBuiltinRefConvertor<JSConvertStdlibShort>(cache, ptypes->coreShort->GetRuntimeClass());
550 RegisterBuiltinRefConvertor<JSConvertStdlibInt>(cache, ptypes->coreInt->GetRuntimeClass());
551 RegisterBuiltinRefConvertor<JSConvertStdlibLong>(cache, ptypes->coreLong->GetRuntimeClass());
552 RegisterBuiltinRefConvertor<JSConvertStdlibFloat>(cache, ptypes->coreFloat->GetRuntimeClass());
553 RegisterBuiltinRefConvertor<JSConvertStdlibDouble>(cache, ptypes->coreDouble->GetRuntimeClass());
554
555 RegisterBuiltinArrayConvertor<ClassRoot::ARRAY_U1, JSConvertU1>(cache, linkerExt);
556 RegisterBuiltinArrayConvertor<ClassRoot::ARRAY_I32, JSConvertI32>(cache, linkerExt);
557 RegisterBuiltinArrayConvertor<ClassRoot::ARRAY_F32, JSConvertF32>(cache, linkerExt);
558 RegisterBuiltinArrayConvertor<ClassRoot::ARRAY_I64, JSConvertI64>(cache, linkerExt);
559 RegisterBuiltinArrayConvertor<ClassRoot::ARRAY_F64, JSConvertF64>(cache, linkerExt);
560 RegisterBuiltinArrayConvertor<ClassRoot::ARRAY_STRING, JSConvertString>(cache, linkerExt);
561 // NOTE(vpukhov): jsvalue[] specialization, currently uses JSRefConvertArrayRef
562
563 RegisterCompatConvertors(ctx);
564 }
565
566 } // namespace ark::ets::interop::js
567