• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h"
17 #include <js_native_api.h>
18 #include <js_native_api_types.h>
19 #include <iostream>
20 #include <ostream>
21 #include <vector>
22 
23 #include "include/mem/panda_containers.h"
24 #include "interop_js/interop_common.h"
25 #include "interop_js/js_proxy/js_proxy.h"
26 #include "interop_js/js_refconvert.h"
27 #include "plugins/ets/runtime/ets_handle.h"
28 #include "plugins/ets/runtime/ets_handle_scope.h"
29 #include "plugins/ets/runtime/interop_js/interop_context.h"
30 #include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h"
31 #include "plugins/ets/runtime/interop_js/call/call.h"
32 #include "plugins/ets/runtime/interop_js/code_scopes.h"
33 #include "runtime/mem/local_object_handle.h"
34 #include "plugins/ets/runtime/ets_platform_types.h"
35 
36 // NOLINTBEGIN(readability-identifier-naming, readability-redundant-declaration)
37 // CC-OFFNXT(G.FMT.10-CPP) project code style
38 napi_status __attribute__((weak)) napi_get_ets_implements(napi_env env, napi_value jsValue, napi_value *result);
39 // NOLINTEND(readability-identifier-naming, readability-redundant-declaration)
40 
41 namespace ark::ets::interop::js::ets_proxy {
42 
43 class JSRefConvertEtsProxy : public JSRefConvert {
44 public:
JSRefConvertEtsProxy(EtsClassWrapper * etsClassWrapper)45     explicit JSRefConvertEtsProxy(EtsClassWrapper *etsClassWrapper)
46         : JSRefConvert(this), etsClassWrapper_(etsClassWrapper)
47     {
48     }
49 
WrapImpl(InteropCtx * ctx,EtsObject * etsObject)50     napi_value WrapImpl(InteropCtx *ctx, EtsObject *etsObject)
51     {
52         return etsClassWrapper_->Wrap(ctx, etsObject);
53     }
UnwrapImpl(InteropCtx * ctx,napi_value jsValue)54     EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value jsValue)
55     {
56         return etsClassWrapper_->Unwrap(ctx, jsValue);
57     }
58 
59 private:
60     EtsClassWrapper *etsClassWrapper_ {};
61 };
62 
Wrap(InteropCtx * ctx,EtsObject * etsObject)63 napi_value EtsClassWrapper::Wrap(InteropCtx *ctx, EtsObject *etsObject)
64 {
65     CheckClassInitialized(etsClass_->GetRuntimeClass());
66 
67     napi_env env = ctx->GetJSEnv();
68 
69     ASSERT(etsObject != nullptr);
70     ASSERT(etsClass_ == etsObject->GetClass());
71 
72     SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
73     if (LIKELY(storage->HasReference(etsObject, env))) {
74         return storage->GetJsObject(etsObject, env);
75     }
76 
77     napi_value jsValue;
78     // etsObject will be wrapped in jsValue in responce to jsCtor call
79     auto *coro = EtsCoroutine::GetCurrent();
80     [[maybe_unused]] EtsHandleScope scope(coro);
81     EtsHandle<EtsObject> handle(coro, etsObject);
82     ctx->SetPendingNewInstance(handle);
83     {
84         ScopedNativeCodeThread nativeScope(coro);
85         NAPI_CHECK_FATAL(napi_new_instance(env, GetJsCtor(env), 0, nullptr, &jsValue));
86     }
87 
88     // NOTE(MockMockBlack, #IC59ZS): put proxy to SharedReferenceStorage more prettily
89     if (this->needProxy_) {
90         ASSERT(storage->HasReference(etsObject, env));
91         return storage->GetJsObject(etsObject, env);
92     }
93     return jsValue;
94 }
95 
96 // Use UnwrapEtsProxy if you expect exactly a EtsProxy
Unwrap(InteropCtx * ctx,napi_value jsValue)97 EtsObject *EtsClassWrapper::Unwrap(InteropCtx *ctx, napi_value jsValue)
98 {
99     CheckClassInitialized(etsClass_->GetRuntimeClass());
100 
101     napi_env env = ctx->GetJSEnv();
102 
103     ASSERT(!IsUndefined(env, jsValue));
104 
105     // Check if object has SharedReference
106     SharedReference *sharedRef = ctx->GetSharedRefStorage()->GetReference(env, jsValue);
107     if (LIKELY(sharedRef != nullptr)) {
108         EtsObject *etsObject = sharedRef->GetEtsObject();
109         if (UNLIKELY(!etsClass_->IsAssignableFrom(etsObject->GetClass()))) {
110             ThrowJSErrorNotAssignable(env, etsObject->GetClass(), etsClass_);
111             return nullptr;
112         }
113         return etsObject;
114     }
115 
116     // Check if object is subtype of js builtin class
117     if (LIKELY(HasBuiltin())) {
118         ASSERT(jsBuiltinMatcher_ != nullptr);
119         auto res = jsBuiltinMatcher_(ctx, jsValue, false);
120         ASSERT(res != nullptr || ctx->SanityJSExceptionPending() || ctx->SanityETSExceptionPending());
121         return res;
122     }
123 
124     InteropCtx::ThrowJSTypeError(env, std::string("Value is not assignable to ") + etsClass_->GetDescriptor());
125     return nullptr;
126 }
127 
128 // Special method to ensure unwrapped object is not a JSProxy
UnwrapEtsProxy(InteropCtx * ctx,napi_value jsValue)129 EtsObject *EtsClassWrapper::UnwrapEtsProxy(InteropCtx *ctx, napi_value jsValue)
130 {
131     CheckClassInitialized(etsClass_->GetRuntimeClass());
132 
133     napi_env env = ctx->GetJSEnv();
134 
135     ASSERT(!IsNullOrUndefined(env, jsValue));
136 
137     // Check if object has SharedReference
138     SharedReference *sharedRef = ctx->GetSharedRefStorage()->GetReference(env, jsValue);
139     if (LIKELY(sharedRef != nullptr)) {
140         EtsObject *etsObject = sharedRef->GetEtsObject();
141         if (UNLIKELY(!etsClass_->IsAssignableFrom(etsObject->GetClass()))) {
142             ThrowJSErrorNotAssignable(env, etsObject->GetClass(), etsClass_);
143             return nullptr;
144         }
145         if (UNLIKELY(!sharedRef->HasETSFlag())) {
146             InteropCtx::ThrowJSTypeError(env, std::string("JS object in context of EtsProxy of class ") +
147                                                   etsClass_->GetDescriptor());
148             return nullptr;
149         }
150         return etsObject;
151     }
152     return nullptr;
153 }
154 
ThrowJSErrorNotAssignable(napi_env env,EtsClass * fromKlass,EtsClass * toKlass)155 void EtsClassWrapper::ThrowJSErrorNotAssignable(napi_env env, EtsClass *fromKlass, EtsClass *toKlass)
156 {
157     const char *from = fromKlass->GetDescriptor();
158     const char *to = toKlass->GetDescriptor();
159     InteropCtx::ThrowJSTypeError(env, std::string(from) + " is not assignable to " + to);
160 }
161 
CreateJSBuiltinProxy(InteropCtx * ctx,napi_value jsValue)162 EtsObject *EtsClassWrapper::CreateJSBuiltinProxy(InteropCtx *ctx, napi_value jsValue)
163 {
164     ASSERT(jsproxyWrapper_ != nullptr);
165     auto *storage = ctx->GetSharedRefStorage();
166     ASSERT(storage->GetReference(ctx->GetJSEnv(), jsValue) == nullptr);
167 
168     EtsObject *etsObject = EtsObject::Create(jsproxyWrapper_->GetProxyClass());
169     if (UNLIKELY(etsObject == nullptr)) {
170         ctx->ForwardEtsException(EtsCoroutine::GetCurrent());
171         return nullptr;
172     }
173 
174     SharedReference *sharedRef = storage->CreateJSObjectRefwithWrap(ctx, etsObject, jsValue);
175     if (UNLIKELY(sharedRef == nullptr)) {
176         ASSERT(InteropCtx::SanityJSExceptionPending());
177         return nullptr;
178     }
179     return sharedRef->GetEtsObject();  // fetch again after gc
180 }
181 
182 /*static*/
CreateJSRefConvertEtsProxy(InteropCtx * ctx,Class * klass)183 std::unique_ptr<JSRefConvert> EtsClassWrapper::CreateJSRefConvertEtsProxy(InteropCtx *ctx, Class *klass)
184 {
185     ASSERT(!klass->IsInterface());
186     EtsClass *etsClass = EtsClass::FromRuntimeClass(klass);
187     EtsClassWrapper *wrapper = EtsClassWrapper::Get(ctx, etsClass);
188     if (UNLIKELY(wrapper == nullptr)) {
189         return nullptr;
190     }
191     ASSERT(wrapper->etsClass_ == etsClass);
192     return std::make_unique<JSRefConvertEtsProxy>(wrapper);
193 }
194 
195 class JSRefConvertJSProxy : public JSRefConvert {
196 public:
JSRefConvertJSProxy()197     explicit JSRefConvertJSProxy() : JSRefConvert(this) {}
198 
WrapImpl(InteropCtx * ctx,EtsObject * etsObject)199     napi_value WrapImpl(InteropCtx *ctx, EtsObject *etsObject)
200     {
201         SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
202         INTEROP_FATAL_IF(!storage->HasReference(etsObject, ctx->GetJSEnv()));
203         return storage->GetJsObject(etsObject, ctx->GetJSEnv());
204     }
205 
UnwrapImpl(InteropCtx * ctx,napi_value jsValue)206     EtsObject *UnwrapImpl(InteropCtx *ctx, [[maybe_unused]] napi_value jsValue)
207     {
208         ctx->Fatal("Unwrap called on JSProxy class");
209         return nullptr;
210     }
211 };
212 
213 /*static*/
CreateJSRefConvertJSProxy(InteropCtx * ctx,Class * klass)214 std::unique_ptr<JSRefConvert> EtsClassWrapper::CreateJSRefConvertJSProxy([[maybe_unused]] InteropCtx *ctx,
215                                                                          [[maybe_unused]] Class *klass)
216 {
217     ASSERT(js_proxy::JSProxy::IsProxyClass(klass));
218     return std::make_unique<JSRefConvertJSProxy>();
219 }
220 
221 class JSRefConvertInterface : public JSRefConvert {
222 public:
JSRefConvertInterface(Class * klass)223     explicit JSRefConvertInterface(Class *klass) : JSRefConvert(this), klass_(klass)
224     {
225         ASSERT(klass->IsInterface());
226     }
227 
WrapImpl(InteropCtx * ctx,EtsObject * etsObject)228     napi_value WrapImpl(InteropCtx *ctx, EtsObject *etsObject)
229     {
230         auto realConverter = JSRefConvertResolve(ctx, etsObject->GetClass()->GetRuntimeClass());
231         if (realConverter == nullptr) {
232             InteropFatal("Cannot get ref converter for object");
233         }
234         return realConverter->Wrap(ctx, etsObject);
235     }
236 
UnwrapImpl(InteropCtx * ctx,napi_value jsValue)237     EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value jsValue)
238     {
239         auto *coro = EtsCoroutine::GetCurrent();
240         napi_env env = ctx->GetJSEnv();
241         SharedReference *sharedRef = ctx->GetSharedRefStorage()->GetReference(env, jsValue);
242         if (LIKELY(sharedRef != nullptr)) {
243             EtsObject *etsObject = sharedRef->GetEtsObject();
244             return etsObject;
245         }
246         if (IsStdClass(klass_)) {
247             auto *objectConverter =
248                 ctx->GetEtsClassWrappersCache()->Lookup(EtsClass::FromRuntimeClass(ctx->GetObjectClass()));
249             auto *ret = objectConverter->Unwrap(ctx, jsValue);
250             ASSERT(ret != nullptr);
251             if (!ret->IsInstanceOf(EtsClass::FromRuntimeClass(klass_))) {
252                 ctx->ThrowJSTypeError(ctx->GetJSEnv(), "object of type " +
253                                                            ret->GetClass()->GetRuntimeClass()->GetName() +
254                                                            " is not assignable to " + klass_->GetName());
255                 return nullptr;
256             }
257             return ret;
258         }
259 
260         napi_value result;
261         NAPI_CHECK_FATAL(napi_get_ets_implements(env, jsValue, &result));
262         if (GetValueType(env, result) != napi_string) {
263             ctx->ThrowJSTypeError(ctx->GetJSEnv(), std::string("object is not a type of Interface: ") +
264                                                        utf::Mutf8AsCString(klass_->GetDescriptor()));
265             return nullptr;
266         }
267         auto interfaceName = GetString(env, result);
268         auto proxy = ctx->GetInterfaceProxyInstance(interfaceName);
269         if (proxy == nullptr) {
270             auto interfaces = GetInterfaceClass(ctx, interfaceName);
271             proxy = js_proxy::JSProxy::CreateInterfaceProxy(interfaces, interfaceName);
272             ctx->SetInterfaceProxyInstance(interfaceName, proxy);
273         }
274         LocalObjectHandle<EtsObject> etsObject(coro, EtsObject::Create(proxy->GetProxyClass()));
275         if (UNLIKELY(etsObject.GetPtr() == nullptr)) {
276             ctx->ThrowJSTypeError(ctx->GetJSEnv(),
277                                   "Interface Proxy EtsObject create failed, interfaceList: " + interfaceName);
278             return nullptr;
279         }
280         sharedRef = ctx->GetSharedRefStorage()->CreateHybridObjectRef(ctx, etsObject.GetPtr(), jsValue);
281         if (UNLIKELY(sharedRef == nullptr)) {
282             ASSERT(InteropCtx::SanityJSExceptionPending());
283             return nullptr;
284         }
285         return etsObject.GetPtr();
286     }
287 
288 protected:
289     template <typename D>
JSRefConvertInterface(Class * klass,D * instance)290     explicit JSRefConvertInterface(Class *klass, D *instance) : JSRefConvert(instance), klass_(klass)
291     {
292         ASSERT(klass->IsInterface());
293     }
294 
GetInterfaceClass(InteropCtx * ctx,std::string & interfaces)295     PandaSet<Class *> GetInterfaceClass(InteropCtx *ctx, std::string &interfaces)
296     {
297         PandaSet<Class *> interfaceList;
298         std::istringstream iss {interfaces};
299         std::string descriptor;
300         auto *coro = EtsCoroutine::GetCurrent();
301         ASSERT(coro != nullptr);
302         while (std::getline(iss, descriptor, ',')) {
303             auto interfaceCls =
304                 coro->GetPandaVM()->GetClassLinker()->GetClass(descriptor.data(), true, ctx->LinkerCtx());
305             ASSERT(interfaceCls != nullptr);
306             interfaceList.insert(interfaceCls->GetRuntimeClass());
307         }
308         return interfaceList;
309     }
310 
GetKlass()311     Class *GetKlass()
312     {
313         return klass_;
314     }
315 
316 private:
317     Class *klass_;
318 };
319 
320 class JSRefConvertInterfaceIterator : public JSRefConvertInterface {
321 public:
JSRefConvertInterfaceIterator(Class * klass)322     explicit JSRefConvertInterfaceIterator(Class *klass) : JSRefConvertInterface(klass, this)
323     {
324         ASSERT(klass->IsInterface());
325     }
326 
UnwrapImpl(InteropCtx * ctx,napi_value jsValue)327     EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value jsValue)
328     {
329         auto objectConverter =
330             ctx->GetEtsClassWrappersCache()->Lookup(EtsClass::FromRuntimeClass(ctx->GetObjectClass()));
331         ASSERT(objectConverter != nullptr);
332         auto ret = objectConverter->Unwrap(ctx, jsValue);
333 
334         std::array args = {Value {ret->GetCoreType()}};
335         auto *method = EtsClass::FromRuntimeClass(ctx->GetJSRuntimeClass())->GetStaticMethod("CreateIterator", nullptr);
336         auto *coro = EtsCoroutine::GetCurrent();
337         auto resObject = method->GetPandaMethod()->Invoke(coro, args.data());
338         ret = EtsObject::FromCoreType(resObject.GetAs<ObjectHeader *>());
339         if (!ret->IsInstanceOf(EtsClass::FromRuntimeClass(GetKlass()))) {
340             ctx->ThrowJSTypeError(ctx->GetJSEnv(), "object of type " + ret->GetClass()->GetRuntimeClass()->GetName() +
341                                                        " is not a assignable to " + GetKlass()->GetName());
342             return nullptr;
343         }
344         return ret;
345     }
346 };
347 
348 /*static*/
CreateJSRefConvertEtsInterface(InteropCtx * ctx,Class * klass)349 std::unique_ptr<JSRefConvert> EtsClassWrapper::CreateJSRefConvertEtsInterface([[maybe_unused]] InteropCtx *ctx,
350                                                                               Class *klass)
351 {
352     if (klass->GetName() == INTERFACE_ITERABLE_NAME) {
353         return std::make_unique<JSRefConvertInterfaceIterator>(klass);
354     }
355     return std::make_unique<JSRefConvertInterface>(klass);
356 }
357 
GetMethod(const std::string & name) const358 EtsMethodSet *EtsClassWrapper::GetMethod(const std::string &name) const
359 {
360     for (const auto &item : etsMethods_) {
361         if (name == item->GetName()) {
362             return item.get();
363         }
364     }
365     return nullptr;
366 }
367 
368 /*static*/
Get(InteropCtx * ctx,EtsClass * etsClass)369 EtsClassWrapper *EtsClassWrapper::Get(InteropCtx *ctx, EtsClass *etsClass)
370 {
371     EtsClassWrappersCache *cache = ctx->GetEtsClassWrappersCache();
372 
373     ASSERT(etsClass != nullptr);
374     EtsClassWrapper *etsClassWrapper = cache->Lookup(etsClass);
375     if (LIKELY(etsClassWrapper != nullptr)) {
376         return etsClassWrapper;
377     }
378 
379     ASSERT(!etsClass->IsPrimitive() && etsClass->GetComponentType() == nullptr);
380     ASSERT(ctx->GetRefConvertCache()->Lookup(etsClass->GetRuntimeClass()) == nullptr);
381 
382     if (IsStdClass(etsClass) && !etsClass->IsInterface() && !etsClass->IsEtsEnum() &&
383         !IsSubClassOfError(etsClass)) {  // NOTE(gogabr): temporary ugly workaround for Function... interfaces
384         return nullptr;
385     }
386     ASSERT(!js_proxy::JSProxy::IsProxyClass((etsClass->GetRuntimeClass())));
387 
388     std::unique_ptr<EtsClassWrapper> wrapper = EtsClassWrapper::Create(ctx, etsClass);
389     if (UNLIKELY(wrapper == nullptr)) {
390         return nullptr;
391     }
392     return cache->Insert(etsClass, std::move(wrapper));
393 }
394 
SetupHierarchy(InteropCtx * ctx,const char * jsBuiltinName)395 bool EtsClassWrapper::SetupHierarchy(InteropCtx *ctx, const char *jsBuiltinName)
396 {
397     ASSERT(etsClass_->GetBase() != etsClass_);
398     if (etsClass_->GetBase() != nullptr) {
399         baseWrapper_ = EtsClassWrapper::Get(ctx, etsClass_->GetBase());
400         if (baseWrapper_ == nullptr) {
401             return false;
402         }
403     }
404 
405     if (jsBuiltinName != nullptr && std::string(jsBuiltinName) != "Object") {
406         auto env = ctx->GetJSEnv();
407         napi_value jsBuiltinCtor;
408         NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), jsBuiltinName, &jsBuiltinCtor));
409         NAPI_CHECK_FATAL(napi_create_reference(env, jsBuiltinCtor, 1, &jsBuiltinCtorRef_));
410     }
411     return true;
412 }
413 
SetBaseWrapperMethods(napi_env env,const EtsClassWrapper::MethodsVec & methods)414 void EtsClassWrapper::SetBaseWrapperMethods(napi_env env, const EtsClassWrapper::MethodsVec &methods)
415 {
416     for (auto method : methods) {
417         const std::string methodName(method->GetName());
418         if (methodName == GET_INDEX_METHOD || methodName == SET_INDEX_METHOD) {
419             SetUpMimicHandler(env);
420         }
421         auto baseClassWrapper = baseWrapper_;
422         while (nullptr != baseClassWrapper) {
423             EtsMethodSet *baseMethodSet = baseClassWrapper->GetMethod(methodName);
424             if (nullptr != baseMethodSet) {
425                 method->SetBaseMethodSet(baseMethodSet);
426                 break;
427             }
428             baseClassWrapper = baseClassWrapper->baseWrapper_;
429         }
430     }
431 }
432 
CalculateProperties(const OverloadsMap * overloads)433 std::pair<EtsClassWrapper::FieldsVec, EtsClassWrapper::MethodsVec> EtsClassWrapper::CalculateProperties(
434     const OverloadsMap *overloads)
435 {
436     auto fatalNoMethod = [](Class *klass, const uint8_t *name, const char *signature) {
437         InteropCtx::Fatal(std::string("No method ") + utf::Mutf8AsCString(name) + " " + signature + " in " +
438                           klass->GetName());
439     };
440 
441     auto klass = etsClass_->GetRuntimeClass();
442     PropsMap props;
443 
444     // Collect fields
445     for (auto &f : klass->GetFields()) {
446         if (f.IsPublic()) {
447             props.insert({f.GetName().data, &f});
448         }
449     }
450     // Select preferred overloads
451     if (overloads != nullptr) {
452         for (auto &[name, signaturePair] : *overloads) {
453             Method *method = etsClass_->GetDirectMethod(name, signaturePair.first)->GetPandaMethod();
454             if (UNLIKELY(method == nullptr)) {
455                 fatalNoMethod(klass, name, signaturePair.first);
456             }
457             auto etsMethodSet = std::make_unique<EtsMethodSet>(EtsMethodSet::Create(method));
458             auto it = props.insert({method->GetName().data, etsMethodSet.get()});
459             if (!it.second && std::holds_alternative<EtsMethodSet *>(it.first->second)) {
460                 // Possible correct method overloading: merge to existing entry
461                 auto addedMethods = std::get<EtsMethodSet *>(it.first->second);
462                 addedMethods->MergeWith(*etsMethodSet.get());
463             } else {
464                 etsMethods_.push_back(std::move(etsMethodSet));
465             }
466         }
467     }
468 
469     CollectClassMethods(&props, overloads);
470     if (etsClass_ != PlatformTypes()->coreObject) {
471         UpdatePropsWithBaseClasses(&props);
472     }
473 
474     return CalculateFieldsAndMethods(props);
475 }
476 
CollectConstructors(EtsClassWrapper::PropsMap * props)477 void EtsClassWrapper::CollectConstructors(EtsClassWrapper::PropsMap *props)
478 {
479     auto objCtors = etsClass_->GetConstructors();
480     // Assuming that ETS StdLib guarantee that Object has the only one ctor
481     ASSERT(objCtors.size() == 1);
482     auto ctor = objCtors[0]->GetPandaMethod();
483     auto etsMethodSet = std::make_unique<EtsMethodSet>(EtsMethodSet::Create(ctor));
484     props->insert({ctor->GetName().data, etsMethodSet.get()});
485     etsMethods_.push_back(std::move(etsMethodSet));
486 }
487 
CollectClassMethods(EtsClassWrapper::PropsMap * props,const OverloadsMap * overloads)488 void EtsClassWrapper::CollectClassMethods(EtsClassWrapper::PropsMap *props, const OverloadsMap *overloads)
489 {
490     auto fatalMethodOverloaded = [](Method *method) {
491         for (auto &m : method->GetClass()->GetMethods()) {
492             if (utf::IsEqual(m.GetName().data, method->GetName().data)) {
493                 INTEROP_LOG(ERROR) << "overload: " << EtsMethod::FromRuntimeMethod(&m)->GetMethodSignature(true);
494             }
495         }
496         InteropCtx::Fatal(std::string("Method ") + utf::Mutf8AsCString(method->GetName().data) + " of class " +
497                           utf::Mutf8AsCString(method->GetClass()->GetDescriptor()) + " is overloaded");
498     };
499 
500     auto klass = etsClass_->GetRuntimeClass();
501     for (auto &m : klass->GetMethods()) {
502         if (m.IsPrivate()) {
503             continue;
504         }
505         if (overloads != nullptr) {
506             if (HasOverloadsMethod(overloads, &m)) {
507                 continue;
508             }
509         }
510         auto methodSet = std::make_unique<EtsMethodSet>(EtsMethodSet::Create(&m));
511         auto it = props->insert({m.GetName().data, methodSet.get()});
512         if (!it.second && !std::holds_alternative<EtsMethodSet *>(it.first->second)) {
513             // Method overloads non-method field
514             fatalMethodOverloaded(&m);
515         }
516         if (!it.second && std::holds_alternative<EtsMethodSet *>(it.first->second)) {
517             // Possible correct method overloading: merge to existing entry
518             auto addedMethods = std::get<EtsMethodSet *>(it.first->second);
519             addedMethods->MergeWith(*methodSet.get());
520         } else {
521             etsMethods_.push_back(std::move(methodSet));
522         }
523     }
524 }
525 
HasOverloadsMethod(const OverloadsMap * overloads,Method * m)526 bool EtsClassWrapper::HasOverloadsMethod(const OverloadsMap *overloads, Method *m)
527 {
528     auto name = m->GetName().data;
529     auto range = overloads->equal_range(name);
530     EtsMethod *etsMethod = EtsMethod::FromRuntimeMethod(m);
531     for (auto iter = range.first; iter != range.second; ++iter) {
532         if (iter->second.second ==
533             etsMethod->GetNumArgs() - static_cast<unsigned int>(etsMethod->GetPandaMethod()->HasVarArgs())) {
534             return true;
535         }
536     }
537     return false;
538 }
539 
UpdatePropsWithBaseClasses(EtsClassWrapper::PropsMap * props)540 void EtsClassWrapper::UpdatePropsWithBaseClasses(EtsClassWrapper::PropsMap *props)
541 {
542     auto hasSquashedProtoOhosImpl = [](EtsClassWrapper *wclass) {
543         ASSERT(wclass->HasBuiltin() || wclass->baseWrapper_ != nullptr);
544         return wclass->HasBuiltin() || wclass->baseWrapper_->HasBuiltin();
545     };
546 
547     auto hasSquashedProtoOtherImpl = [](EtsClassWrapper *wclass) {
548         // NOTE(vpukhov): some napi implementations add explicit receiver checks in call handler,
549         //                thus method inheritance via prototype chain wont work
550         (void)wclass;
551         return true;
552     };
553 
554 #if defined(PANDA_TARGET_OHOS) || defined(PANDA_JS_ETS_HYBRID_MODE)
555     auto hasSquashedProto = hasSquashedProtoOhosImpl;
556     (void)hasSquashedProtoOtherImpl;
557 #else
558     auto hasSquashedProto = hasSquashedProtoOtherImpl;
559     (void)hasSquashedProtoOhosImpl;
560 #endif
561 
562     if (hasSquashedProto(this)) {
563         // Copy properties of base classes if we have to split prototype chain
564         for (auto wclass = baseWrapper_; wclass != nullptr && (wclass->etsClass_ != PlatformTypes()->coreObject);
565              wclass = wclass->baseWrapper_) {
566             for (auto &wfield : wclass->GetFields()) {
567                 Field *field = wfield.GetField();
568                 props->insert({field->GetName().data, field});
569             }
570             for (auto &link : wclass->GetMethods()) {
571                 EtsMethodSet *methodSet = link.IsResolved() ? link.GetResolved()->GetMethodSet() : link.GetUnresolved();
572                 props->insert({utf::CStringAsMutf8(methodSet->GetName()), methodSet});
573             }
574 
575             if (hasSquashedProto(wclass)) {
576                 break;
577             }
578         }
579     }
580 }
581 
CalculateFieldsAndMethods(const PropsMap & props)582 std::pair<EtsClassWrapper::FieldsVec, EtsClassWrapper::MethodsVec> EtsClassWrapper::CalculateFieldsAndMethods(
583     const PropsMap &props)
584 {
585     std::vector<EtsMethodSet *> methods;
586     std::vector<Field *> fields;
587 
588     for (auto &[n, p] : props) {
589         if (std::holds_alternative<EtsMethodSet *>(p)) {
590             auto method = std::get<EtsMethodSet *>(p);
591             if (method->IsConstructor() && !method->IsStatic()) {
592                 etsCtorLink_ = LazyEtsMethodWrapperLink(method);
593             }
594             if (!method->IsConstructor()) {
595                 methods.push_back(method);
596             }
597         } else if (std::holds_alternative<Field *>(p)) {
598             fields.push_back(std::get<Field *>(p));
599         } else {
600             UNREACHABLE();
601         }
602     }
603 
604     return {fields, methods};
605 }
606 
BuildJSProperties(napi_env & env,Span<Field * > fields,Span<EtsMethodSet * > methods)607 std::vector<napi_property_descriptor> EtsClassWrapper::BuildJSProperties(napi_env &env, Span<Field *> fields,
608                                                                          Span<EtsMethodSet *> methods)
609 {
610     std::vector<napi_property_descriptor> jsProps;
611     jsProps.reserve(fields.size() + methods.size() + 1);
612 
613     // Process fields
614     numFields_ = fields.size();
615     // NOLINTNEXTLINE(modernize-avoid-c-arrays)
616     etsFieldWrappers_ = std::make_unique<EtsFieldWrapper[]>(numFields_);
617     Span<EtsFieldWrapper> etsFieldWrappers(etsFieldWrappers_.get(), numFields_);
618     size_t fieldIdx = 0;
619 
620     for (Field *field : fields) {
621         auto wfield = &etsFieldWrappers[fieldIdx++];
622         if (field->IsStatic()) {
623             EtsClassWrapper *fieldWclass = LookupBaseWrapper(EtsClass::FromRuntimeClass(field->GetClass()));
624             ASSERT(fieldWclass != nullptr);
625             jsProps.emplace_back(wfield->MakeStaticProperty(fieldWclass, field));
626         } else {
627             jsProps.emplace_back(wfield->MakeInstanceProperty(this, field));
628         }
629     }
630 
631     // Process methods
632     numMethods_ = methods.size();
633     // NOLINTNEXTLINE(modernize-avoid-c-arrays)
634     etsMethodWrappers_ = std::make_unique<LazyEtsMethodWrapperLink[]>(numMethods_);
635     Span<LazyEtsMethodWrapperLink> etsMethodWrappers(etsMethodWrappers_.get(), numMethods_);
636     size_t methodIdx = 0;
637     GetterSetterPropsMap propMap;
638     for (auto &method : methods) {
639         ASSERT(!method->IsConstructor());
640         auto lazyLink = &etsMethodWrappers[methodIdx++];
641         lazyLink->Set(method);
642         jsProps.emplace_back(EtsMethodWrapper::MakeNapiProperty(method, lazyLink));
643         if (strcmp(method->GetName(), ITERATOR_METHOD) == 0) {
644             auto iterator = EtsClassWrapper::GetGlobalSymbolIterator(env);
645             jsProps.emplace_back(EtsMethodWrapper::MakeNapiIteratorProperty(iterator, method, lazyLink));
646         }
647         if (method->IsGetter() || method->IsSetter()) {
648             BuildGetterSetterFieldProperties(propMap, method);
649         }
650     }
651     jsProps.reserve(jsProps.size() + propMap.size());
652     for (auto &item : propMap) {
653         jsProps.emplace_back(item.second);
654     }
655 
656     return jsProps;
657 }
658 
BuildGetterSetterFieldProperties(GetterSetterPropsMap & propMap,EtsMethodSet * method)659 void EtsClassWrapper::BuildGetterSetterFieldProperties(GetterSetterPropsMap &propMap, EtsMethodSet *method)
660 {
661     auto ptr = reinterpret_cast<uintptr_t>(method->GetName());
662     const char *fieldName = reinterpret_cast<char *>(ptr + strlen(SETTER_BEGIN));
663     const std::string key(fieldName);
664     auto result = propMap.find(key);
665     if (result != propMap.end()) {
666         EtsMethodWrapper::AttachGetterSetterToProperty(method, result->second);
667     } else {
668         napi_property_descriptor prop {};
669         auto fieldWrapper = std::make_unique<EtsFieldWrapper>(this);
670         prop.utf8name = fieldName;
671         prop.attributes = method->IsStatic() ? EtsClassWrapper::STATIC_FIELD_ATTR : EtsClassWrapper::FIELD_ATTR;
672         prop.data = fieldWrapper.get();
673         EtsMethodWrapper::AttachGetterSetterToProperty(method, prop);
674         propMap.insert({key, prop});
675         getterSetterFieldWrappers_.push_back(std::move(fieldWrapper));
676     }
677 }
678 
679 /* static */
GetGlobalSymbolIterator(napi_env & env)680 napi_value EtsClassWrapper::GetGlobalSymbolIterator(napi_env &env)
681 {
682     napi_value global;
683     napi_value symbol;
684     napi_value iterator;
685     NAPI_CHECK_FATAL(napi_get_global(env, &global));
686     NAPI_CHECK_FATAL(napi_get_named_property(env, global, "Symbol", &symbol));
687     NAPI_CHECK_FATAL(napi_get_named_property(env, symbol, "iterator", &iterator));
688 
689     return iterator;
690 }
691 
LookupBaseWrapper(EtsClass * klass)692 EtsClassWrapper *EtsClassWrapper::LookupBaseWrapper(EtsClass *klass)
693 {
694     for (auto wclass = this; wclass != nullptr; wclass = wclass->baseWrapper_) {
695         if (wclass->etsClass_ == klass) {
696             return wclass;
697         }
698     }
699     return nullptr;
700 }
701 
DoSetPrototype(napi_env env,napi_value obj,napi_value proto)702 static void DoSetPrototype(napi_env env, napi_value obj, napi_value proto)
703 {
704     napi_value builtinObject;
705     napi_value setprotoFn;
706     NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), "Object", &builtinObject));
707     NAPI_CHECK_FATAL(napi_get_named_property(env, builtinObject, "setPrototypeOf", &setprotoFn));
708 
709     std::array args = {obj, proto};
710     NAPI_CHECK_FATAL(NapiCallFunction(env, builtinObject, setprotoFn, args.size(), args.data(), nullptr));
711 }
712 
SetNullPrototype(napi_env env,napi_value jsCtor)713 static void SetNullPrototype(napi_env env, napi_value jsCtor)
714 {
715     napi_value prot;
716     NAPI_CHECK_FATAL(napi_get_named_property(env, jsCtor, "prototype", &prot));
717 
718     auto nullProto = GetNull(env);
719     DoSetPrototype(env, jsCtor, nullProto);
720     DoSetPrototype(env, prot, nullProto);
721 }
722 
SimulateJSInheritance(napi_env env,napi_value jsCtor,napi_value jsBaseCtor)723 static void SimulateJSInheritance(napi_env env, napi_value jsCtor, napi_value jsBaseCtor)
724 {
725     napi_value cprototype;
726     napi_value baseCprototype;
727     NAPI_CHECK_FATAL(napi_get_named_property(env, jsCtor, "prototype", &cprototype));
728     NAPI_CHECK_FATAL(napi_get_named_property(env, jsBaseCtor, "prototype", &baseCprototype));
729 
730     DoSetPrototype(env, jsCtor, jsBaseCtor);
731     DoSetPrototype(env, cprototype, baseCprototype);
732 }
733 
734 /*static*/
Create(InteropCtx * ctx,EtsClass * etsClass,const char * jsBuiltinName,const OverloadsMap * overloads)735 std::unique_ptr<EtsClassWrapper> EtsClassWrapper::Create(InteropCtx *ctx, EtsClass *etsClass, const char *jsBuiltinName,
736                                                          const OverloadsMap *overloads)
737 {
738     auto env = ctx->GetJSEnv();
739 
740     // CC-OFFNXT(G.RES.09) private constructor
741     // NOLINTNEXTLINE(readability-identifier-naming)
742     auto _this = std::unique_ptr<EtsClassWrapper>(new EtsClassWrapper(etsClass));
743     if (!_this->SetupHierarchy(ctx, jsBuiltinName)) {
744         return nullptr;
745     }
746 
747     auto [fields, methods] = _this->CalculateProperties(overloads);
748     _this->SetBaseWrapperMethods(env, methods);
749 
750     auto jsProps = _this->BuildJSProperties(env, {fields.data(), fields.size()}, {methods.data(), methods.size()});
751 
752     // NOTE(vpukhov): fatal no-public-fields check when escompat adopt accessors
753     if (_this->HasBuiltin() && !fields.empty()) {
754         INTEROP_LOG(ERROR) << "built-in class " << etsClass->GetDescriptor() << " has field properties";
755     }
756     if (_this->HasBuiltin() && etsClass->IsFinal()) {
757         INTEROP_LOG(FATAL) << "built-in class " << etsClass->GetDescriptor() << " is final";
758     }
759     // NOTE(vpukhov): forbid "true" ets-field overriding in js-derived class, as it cannot be proxied back
760     if (!etsClass->IsFinal()) {
761         auto ungroupedMethods = CollectAllPandaMethods(methods.begin(), methods.end());
762         _this->jsproxyWrapper_ = ctx->GetJsProxyInstance(etsClass);
763         if (_this->jsproxyWrapper_ == nullptr) {
764             // NOTE(konstanting): we assume that the method list stays the same for every proxied class
765             _this->jsproxyWrapper_ =
766                 js_proxy::JSProxy::CreateBuiltinProxy(etsClass, {ungroupedMethods.data(), ungroupedMethods.size()});
767             ctx->SetJsProxyInstance(etsClass, _this->jsproxyWrapper_);
768         }
769     }
770     napi_value jsCtor {};
771     NAPI_CHECK_FATAL(napi_define_class(env, etsClass->GetDescriptor(), NAPI_AUTO_LENGTH,
772                                        EtsClassWrapper::JSCtorCallback, _this.get(), jsProps.size(), jsProps.data(),
773                                        &jsCtor));
774 
775     if (etsClass == PlatformTypes()->coreObject) {
776         SetNullPrototype(env, jsCtor);
777 
778         NAPI_CHECK_FATAL(napi_create_reference(env, jsCtor, 1, &_this->jsCtorRef_));
779         NAPI_CHECK_FATAL(napi_create_reference(env, jsCtor, 1, &_this->jsBuiltinCtorRef_));
780         NAPI_CHECK_FATAL(napi_object_seal(env, jsCtor));
781 
782         return _this;
783     }
784 
785     auto base = _this->baseWrapper_;
786     napi_value fakeSuper = _this->HasBuiltin() ? _this->GetBuiltin(env)
787                                                : (base->HasBuiltin() ? base->GetBuiltin(env) : base->GetJsCtor(env));
788 
789     SimulateJSInheritance(env, jsCtor, fakeSuper);
790     NAPI_CHECK_FATAL(napi_object_seal(env, jsCtor));
791     NAPI_CHECK_FATAL(napi_create_reference(env, jsCtor, 1, &_this->jsCtorRef_));
792 
793     return _this;
794 }
795 
SetUpMimicHandler(napi_env env)796 void EtsClassWrapper::SetUpMimicHandler(napi_env env)
797 {
798     this->needProxy_ = true;
799 
800     napi_value global;
801     NAPI_CHECK_FATAL(napi_get_global(env, &global));
802 
803     napi_value jsProxyCtorRef;
804     NAPI_CHECK_FATAL(napi_get_named_property(env, global, "Proxy", &jsProxyCtorRef));
805     NAPI_CHECK_FATAL(napi_create_reference(env, jsProxyCtorRef, 1, &this->jsProxyCtorRef_));
806 
807     napi_value jsProxyHandlerRef;
808     NAPI_CHECK_FATAL(napi_create_object(env, &jsProxyHandlerRef));
809     NAPI_CHECK_FATAL(napi_create_reference(env, jsProxyHandlerRef, 1, &this->jsProxyHandlerRef_));
810 
811     napi_value getHandlerFunc;
812     NAPI_CHECK_FATAL(napi_create_function(env, nullptr, NAPI_AUTO_LENGTH, EtsClassWrapper::MimicGetHandler, nullptr,
813                                           &getHandlerFunc));
814     napi_value setHandlerFunc;
815     NAPI_CHECK_FATAL(napi_create_function(env, nullptr, NAPI_AUTO_LENGTH, EtsClassWrapper::MimicSetHandler, nullptr,
816                                           &setHandlerFunc));
817 
818     NAPI_CHECK_FATAL(napi_set_named_property(env, jsProxyHandlerRef, "get", getHandlerFunc));
819     NAPI_CHECK_FATAL(napi_set_named_property(env, jsProxyHandlerRef, "set", setHandlerFunc));
820 }
821 
822 /*static*/
CreateProxy(napi_env env,napi_value jsCtor,EtsClassWrapper * thisWrapper)823 napi_value EtsClassWrapper::CreateProxy(napi_env env, napi_value jsCtor, EtsClassWrapper *thisWrapper)
824 {
825     napi_value jsProxyHandlerRef;
826     NAPI_CHECK_FATAL(napi_get_reference_value(env, thisWrapper->jsProxyHandlerRef_, &jsProxyHandlerRef));
827     napi_value jsProxyCtorRef;
828     NAPI_CHECK_FATAL(napi_get_reference_value(env, thisWrapper->jsProxyCtorRef_, &jsProxyCtorRef));
829 
830     napi_value proxyObj;
831     std::vector<napi_value> args = {jsCtor, jsProxyHandlerRef};
832 
833     NAPI_CHECK_FATAL(napi_new_instance(env, jsProxyCtorRef, args.size(), args.data(), &proxyObj));
834     return proxyObj;
835 }
836 
837 /*static*/
MimicGetHandler(napi_env env,napi_callback_info info)838 napi_value EtsClassWrapper::MimicGetHandler(napi_env env, napi_callback_info info)
839 {
840     ASSERT_SCOPED_NATIVE_CODE();
841     auto coro = EtsCoroutine::GetCurrent();
842     auto ctx = InteropCtx::Current(coro);
843     INTEROP_CODE_SCOPE_JS(coro);
844 
845     size_t argc;
846     NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr));
847     auto jsArgs = ctx->GetTempArgs<napi_value>(argc);
848     NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &argc, jsArgs->data(), nullptr, nullptr));
849 
850     napi_value target = jsArgs[0];
851     napi_value property = jsArgs[1];
852 
853     if (GetValueType(env, property) == napi_number) {
854         ScopedManagedCodeThread managedCode(coro);
855         ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
856         ASSERT(storage != nullptr);
857         ets_proxy::SharedReference *sharedRef = storage->GetReference(env, jsArgs[0]);
858         if (sharedRef == nullptr) {
859             InteropFatal("MimicGetHandler sharedRef is empty");
860         }
861 
862         auto *etsThis = sharedRef->GetEtsObject();
863         ASSERT(etsThis != nullptr);
864         EtsMethod *method = etsThis->GetClass()->GetInstanceMethod(GET_INDEX_METHOD, nullptr);
865         ASSERT(method != nullptr);
866 
867         Span sp(jsArgs->begin(), jsArgs->end());
868         const size_t startIndex = 1;
869         const size_t argSize = 1;
870         return CallETSInstance(coro, ctx, method->GetPandaMethod(), sp.SubSpan(startIndex, argSize), etsThis);
871     }
872 
873     napi_value result;
874     NAPI_CHECK_FATAL(napi_get_property(env, target, property, &result));
875     return result;
876 }
877 
878 /*static*/
MimicSetHandler(napi_env env,napi_callback_info info)879 napi_value EtsClassWrapper::MimicSetHandler(napi_env env, napi_callback_info info)
880 {
881     ASSERT_SCOPED_NATIVE_CODE();
882     auto coro = EtsCoroutine::GetCurrent();
883     auto ctx = InteropCtx::Current(coro);
884     INTEROP_CODE_SCOPE_JS(coro);
885 
886     size_t argc;
887     NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr));
888     auto jsArgs = ctx->GetTempArgs<napi_value>(argc);
889     NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &argc, jsArgs->data(), nullptr, nullptr));
890 
891     ScopedManagedCodeThread managedCode(coro);
892     ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
893     ASSERT(storage != nullptr);
894     ets_proxy::SharedReference *sharedRef = storage->GetReference(env, jsArgs[0]);
895     if (sharedRef == nullptr) {
896         InteropFatal("MimicSetHandler sharedRef is empty");
897     }
898 
899     auto *etsThis = sharedRef->GetEtsObject();
900     ASSERT(etsThis != nullptr);
901     EtsMethod *method = etsThis->GetClass()->GetInstanceMethod(SET_INDEX_METHOD, nullptr);
902     ASSERT(method != nullptr);
903 
904     Span sp(jsArgs->begin(), jsArgs->end());
905 
906     const size_t startIndex = 1;
907     const size_t argSize = 2;
908     CallETSInstance(coro, ctx, method->GetPandaMethod(), sp.SubSpan(startIndex, argSize), etsThis);
909 
910     napi_value trueValue;
911     NAPI_CHECK_FATAL(napi_get_boolean(env, true, &trueValue));
912     return trueValue;
913 }
914 
915 /*static*/
JSCtorCallback(napi_env env,napi_callback_info cinfo)916 napi_value EtsClassWrapper::JSCtorCallback(napi_env env, napi_callback_info cinfo)
917 {
918     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
919     InteropCtx *ctx = InteropCtx::Current(coro);
920     INTEROP_CODE_SCOPE_JS(coro);
921 
922     napi_value jsThis;
923     size_t argc;
924     void *data;
925     NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, &jsThis, &data));
926     auto etsClassWrapper = reinterpret_cast<EtsClassWrapper *>(data);
927 
928     ScopedManagedCodeThread managedScope(coro);
929     EtsObject *etsObject = ctx->AcquirePendingNewInstance();
930 
931     if (LIKELY(etsObject != nullptr)) {
932         // proxy $get and $set
933         napi_value jsObject = nullptr;
934         if (etsClassWrapper->needProxy_) {
935             jsObject = CreateProxy(env, jsThis, etsClassWrapper);
936         } else {
937             jsObject = jsThis;
938         }
939 
940         // Create shared reference for existing ets object
941         SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
942         if (UNLIKELY(!storage->CreateETSObjectRef(ctx, etsObject, jsObject))) {
943             ASSERT(InteropCtx::SanityJSExceptionPending());
944             return nullptr;
945         }
946         NAPI_CHECK_FATAL(napi_object_seal(env, jsThis));
947         return nullptr;
948     }
949 
950     if (!etsClassWrapper->etsCtorLink_.IsResolved() && etsClassWrapper->etsCtorLink_.GetUnresolved() == nullptr) {
951         InteropCtx::ThrowJSError(env,
952                                  etsClassWrapper->GetEtsClass()->GetRuntimeClass()->GetName() + " has no constructor");
953         return nullptr;
954     }
955 
956     auto jsArgs = ctx->GetTempArgs<napi_value>(argc);
957     NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, jsArgs->data(), nullptr, nullptr));
958 
959     napi_value jsNewtarget;
960     NAPI_CHECK_FATAL(napi_get_new_target(env, cinfo, &jsNewtarget));
961 
962     // create new object and wrap it
963     if (UNLIKELY(!etsClassWrapper->CreateAndWrap(env, jsNewtarget, jsThis, *jsArgs))) {
964         ASSERT(InteropCtx::SanityJSExceptionPending());
965         return nullptr;
966     }
967 
968     // NOTE(ivagin): JS constructor is not required to return 'this', but ArkUI NAPI requires it
969     return jsThis;
970 }
971 
CreateAndWrap(napi_env env,napi_value jsNewtarget,napi_value jsThis,Span<napi_value> jsArgs)972 bool EtsClassWrapper::CreateAndWrap(napi_env env, napi_value jsNewtarget, napi_value jsThis, Span<napi_value> jsArgs)
973 {
974     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
975     InteropCtx *ctx = InteropCtx::Current(coro);
976 
977     if (UNLIKELY(!CheckClassInitialized<true>(etsClass_->GetRuntimeClass()))) {
978         ctx->ForwardEtsException(coro);
979         return false;
980     }
981 
982     bool notExtensible;
983     NAPI_CHECK_FATAL(napi_strict_equals(env, jsNewtarget, GetJsCtor(env), &notExtensible));
984 
985     EtsClass *instanceClass {};
986 
987     if (LIKELY(notExtensible)) {
988 #if !defined(PANDA_TARGET_OHOS) && !defined(PANDA_JS_ETS_HYBRID_MODE)
989         // In case of OHOS sealed object can't be wrapped, therefore seal it after wrapping
990         NAPI_CHECK_FATAL(napi_object_seal(env, jsThis));
991 #endif  // PANDA_TARGET_OHOS
992         instanceClass = etsClass_;
993     } else {
994         if (UNLIKELY(jsproxyWrapper_ == nullptr)) {
995             ctx->ThrowJSTypeError(env, std::string("Proxy for ") + etsClass_->GetDescriptor() + " is not extensible");
996             return false;
997         }
998         instanceClass = jsproxyWrapper_->GetProxyClass();
999     }
1000 
1001     LocalObjectHandle<EtsObject> etsObject(coro, EtsObject::Create(instanceClass));
1002     if (UNLIKELY(etsObject.GetPtr() == nullptr)) {
1003         return false;
1004     }
1005 
1006     // NOTE(MockMockBlack, #IC59ZS): put proxy to SharedReferenceStorage more prettily
1007     SharedReference *sharedRef;
1008     if (LIKELY(notExtensible)) {
1009         sharedRef = ctx->GetSharedRefStorage()->CreateETSObjectRef(ctx, etsObject.GetPtr(), jsThis);
1010 #if defined(PANDA_TARGET_OHOS) || defined(PANDA_JS_ETS_HYBRID_MODE)
1011         // In case of OHOS sealed object can't be wrapped, therefore seal it after wrapping
1012         NAPI_CHECK_FATAL(napi_object_seal(env, jsThis));
1013 #endif  // PANDA_TARGET_OHOS
1014     } else {
1015         sharedRef = ctx->GetSharedRefStorage()->CreateHybridObjectRef(ctx, etsObject.GetPtr(), jsThis);
1016     }
1017     if (UNLIKELY(sharedRef == nullptr)) {
1018         ASSERT(InteropCtx::SanityJSExceptionPending());
1019         return false;
1020     }
1021 
1022     EtsMethodWrapper *ctorWrapper = EtsMethodWrapper::ResolveLazyLink(ctx, etsCtorLink_);
1023     ASSERT(ctorWrapper != nullptr);
1024     EtsMethod *ctorMethod = ctorWrapper->GetEtsMethod(jsArgs.Size());
1025     ASSERT(ctorMethod->IsInstanceConstructor());
1026 
1027     napi_value callRes = CallETSInstance(coro, ctx, ctorMethod->GetPandaMethod(), jsArgs, etsObject.GetPtr());
1028     return callRes != nullptr;
1029 }
1030 
1031 }  // namespace ark::ets::interop::js::ets_proxy
1032