From 1b7dc5835feaac9dc393f794505ad53840dc40cc Mon Sep 17 00:00:00 2001 From: Vsevolod Pukhov Date: Wed, 5 Jul 2023 17:31:39 +0300 Subject: [PATCH] interop_js: Finish basic implementation of ets_proxy * Use ets_proxy classes as reference convertors and vice versa * Implement abstract REFERENCE property accessors * Typechecks, null/initialize safety, proper exception forwarding * Refactor ets_proxy::SharedReference, hide actual implementation * Add rest numeric primitive types convertors * Tests: exceptions, type-checking, number precision, arrays Signed-off-by: Vsevolod Pukhov --- plugins/ets/runtime/ets_handle.h | 6 + plugins/ets/runtime/interop_js/BUILD.gn | 6 +- plugins/ets/runtime/interop_js/CMakeLists.txt | 6 +- .../ets_proxy/ets_class_wrapper.cpp | 268 ++++++++++-------- .../interop_js/ets_proxy/ets_class_wrapper.h | 44 +-- .../ets_proxy/ets_field_wrapper.cpp | 238 +++++++++------- .../interop_js/ets_proxy/ets_field_wrapper.h | 27 +- .../ets_proxy/ets_method_wrapper.cpp | 96 +++++-- .../interop_js/ets_proxy/ets_method_wrapper.h | 32 ++- .../ets_proxy/ets_object_reference.h | 2 +- .../interop_js/ets_proxy/ets_proxy.cpp | 12 +- .../runtime/interop_js/ets_proxy/ets_proxy.h | 1 - .../interop_js/ets_proxy/js_convert_object.h | 112 -------- .../ets_proxy/lazy_ets_class_wrapper_link.h | 31 -- ...bject_wrapper.cpp => shared_reference.cpp} | 47 +-- ...ts_object_wrapper.h => shared_reference.h} | 32 +-- ...orage.cpp => shared_reference_storage.cpp} | 71 +++-- ...s_storage.h => shared_reference_storage.h} | 48 ++-- .../interop_js/ets_proxy/typed_pointer.h | 16 +- .../interop_js/ets_proxy/wrappers_cache.h | 6 +- .../runtime/interop_js/ets_type_visitor-inl.h | 3 + .../ets/runtime/interop_js/ets_vm_plugin.cpp | 14 +- .../{ts2ets_common.cpp => interop_common.cpp} | 12 +- .../ets/runtime/interop_js/interop_common.h | 182 ++++++++++++ .../runtime/interop_js/interop_context.cpp | 35 ++- .../ets/runtime/interop_js/interop_context.h | 88 ++++-- .../interop_js/intrinsics_api_impl.cpp | 4 +- plugins/ets/runtime/interop_js/js_convert.h | 164 +++++------ .../ets/runtime/interop_js/js_job_queue.cpp | 6 +- .../ets/runtime/interop_js/js_refconvert.cpp | 20 +- .../ets/runtime/interop_js/js_refconvert.h | 10 +- .../runtime/interop_js/js_refconvert_array.h | 6 +- plugins/ets/runtime/interop_js/js_value.h | 2 +- .../ets/runtime/interop_js/js_value_call.cpp | 130 ++++----- plugins/ets/runtime/interop_js/logger.h | 75 +++++ .../ets/runtime/interop_js/napi_env_scope.h | 2 +- .../ets/runtime/interop_js/ts2ets_common.h | 238 +--------------- .../ets/runtime/interop_js/ts2ets_copy.cpp | 14 +- .../ets/runtime/interop_js/ts2ets_tstype.cpp | 64 ++--- .../interop_js/gtest_plugin/gtest_launcher.js | 27 +- .../interop_js/tests/ets_proxy/CMakeLists.txt | 4 +- .../call_ets_function.test.js | 28 -- .../CMakeLists.txt | 6 +- .../class_proxy/check_access_primitives.js | 74 +++++ .../class_proxy/check_access_references.js | 74 +++++ .../class_proxy/check_proxy_objects.js | 77 +++++ .../class_proxy.test.js} | 8 +- .../test_class_proxy.cpp} | 36 ++- .../class_proxy/test_class_proxy.ets | 158 +++++++++++ .../CMakeLists.txt | 6 +- .../function_proxy/check_function_proxy.js | 42 +++ .../function_proxy.test.js} | 16 +- .../test_function_proxy.cpp} | 12 +- .../test_function_proxy.ets} | 2 +- .../tests/ets_proxy/mem/CMakeLists.txt | 4 +- .../CMakeLists.txt | 6 +- .../test_proxy_reference_storage_1.cpp} | 146 +++++----- .../test_proxy_reference_storage_1.ets} | 0 .../CMakeLists.txt | 6 +- .../test_proxy_reference_storage_2.ets} | 2 +- .../test_proxy_reference_storage_2.js} | 6 +- .../check_access_to_builtin_fields.js | 28 -- .../check_access_to_primitive_fields.js | 59 ---- .../check_access_to_reference_fields.js | 30 -- ...check_access_to_static_primitive_fields.js | 56 ---- ...check_access_to_static_reference_fields.js | 67 ----- .../check_call_with_primitives.js | 28 -- .../check_call_with_references.js | 24 -- .../use_ets_class/check_null_object.js | 33 --- .../check_the_same_ets_object.js | 42 --- .../use_ets_class/test_use_ets_class.cpp | 74 ----- .../use_ets_class/test_use_ets_class.ets | 189 ------------ .../use_ets_class/use_ets_class.test.js | 32 --- .../tests/proxy_types/proxy_types_test.js | 24 +- runtime/mem/vm_handle.h | 5 + 75 files changed, 1699 insertions(+), 1902 deletions(-) delete mode 100644 plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h delete mode 100644 plugins/ets/runtime/interop_js/ets_proxy/lazy_ets_class_wrapper_link.h rename plugins/ets/runtime/interop_js/ets_proxy/{ets_object_wrapper.cpp => shared_reference.cpp} (42%) rename plugins/ets/runtime/interop_js/ets_proxy/{ets_object_wrapper.h => shared_reference.h} (62%) rename plugins/ets/runtime/interop_js/ets_proxy/{ets_object_wrappers_storage.cpp => shared_reference_storage.cpp} (42%) rename plugins/ets/runtime/interop_js/ets_proxy/{ets_object_wrappers_storage.h => shared_reference_storage.h} (40%) rename plugins/ets/runtime/interop_js/{ts2ets_common.cpp => interop_common.cpp} (84%) create mode 100644 plugins/ets/runtime/interop_js/interop_common.h create mode 100644 plugins/ets/runtime/interop_js/logger.h delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/call_ets_function.test.js rename plugins/ets/tests/interop_js/tests/ets_proxy/{use_ets_class => class_proxy}/CMakeLists.txt (75%) create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_access_primitives.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_access_references.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_proxy_objects.js rename plugins/ets/tests/interop_js/tests/ets_proxy/{call_ets_function/check_primitives.js => class_proxy/class_proxy.test.js} (75%) rename plugins/ets/tests/interop_js/tests/ets_proxy/{use_ets_class/check_inheritance.js => class_proxy/test_class_proxy.cpp} (56%) create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/test_class_proxy.ets rename plugins/ets/tests/interop_js/tests/ets_proxy/{call_ets_function => function_proxy}/CMakeLists.txt (73%) create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/check_function_proxy.js rename plugins/ets/tests/interop_js/tests/ets_proxy/{call_ets_function/check_objects.js => function_proxy/function_proxy.test.js} (74%) rename plugins/ets/tests/interop_js/tests/ets_proxy/{call_ets_function/test_call_ets_function.cpp => function_proxy/test_function_proxy.cpp} (72%) rename plugins/ets/tests/interop_js/tests/ets_proxy/{call_ets_function/test_call_ets_function.ets => function_proxy/test_function_proxy.ets} (97%) rename plugins/ets/tests/interop_js/tests/ets_proxy/mem/{ets_object_wrappers_storage_1 => proxy_reference_storage_1}/CMakeLists.txt (71%) rename plugins/ets/tests/interop_js/tests/ets_proxy/mem/{ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.cpp => proxy_reference_storage_1/test_proxy_reference_storage_1.cpp} (31%) rename plugins/ets/tests/interop_js/tests/ets_proxy/mem/{ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.ets => proxy_reference_storage_1/test_proxy_reference_storage_1.ets} (100%) rename plugins/ets/tests/interop_js/tests/ets_proxy/mem/{ets_object_wrappers_storage_2 => proxy_reference_storage_2}/CMakeLists.txt (71%) rename plugins/ets/tests/interop_js/tests/ets_proxy/mem/{ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.ets => proxy_reference_storage_2/test_proxy_reference_storage_2.ets} (94%) rename plugins/ets/tests/interop_js/tests/ets_proxy/mem/{ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.js => proxy_reference_storage_2/test_proxy_reference_storage_2.js} (82%) delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_builtin_fields.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_primitive_fields.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_reference_fields.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_primitive_fields.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_reference_fields.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_primitives.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_references.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_null_object.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_the_same_ets_object.js delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.cpp delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.ets delete mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/use_ets_class.test.js diff --git a/plugins/ets/runtime/ets_handle.h b/plugins/ets/runtime/ets_handle.h index 8c84b5527..28d3751e6 100644 --- a/plugins/ets/runtime/ets_handle.h +++ b/plugins/ets/runtime/ets_handle.h @@ -31,6 +31,12 @@ public: : VMHandle(ManagedThread::CastFromThread(coroutine), GetObjectHeader(ets_obj)) { } + + template + inline explicit EtsHandle(const VMHandle

&other) : VMHandle(other) + { + } + ~EtsHandle() = default; DEFAULT_NOEXCEPT_MOVE_SEMANTIC(EtsHandle); NO_COPY_SEMANTIC(EtsHandle); diff --git a/plugins/ets/runtime/interop_js/BUILD.gn b/plugins/ets/runtime/interop_js/BUILD.gn index 0162528d8..fa3d87c55 100644 --- a/plugins/ets/runtime/interop_js/BUILD.gn +++ b/plugins/ets/runtime/interop_js/BUILD.gn @@ -34,13 +34,13 @@ ohos_shared_library("ets_interop_js_napi") { "ets_proxy/ets_class_wrapper.cpp", "ets_proxy/ets_field_wrapper.cpp", "ets_proxy/ets_method_wrapper.cpp", - "ets_proxy/ets_object_wrapper.cpp", - "ets_proxy/ets_object_wrappers_storage.cpp", + "ets_proxy/shared_reference.cpp", + "ets_proxy/shared_reference_storage.cpp", "ets_proxy/ets_proxy.cpp", "interop_context.cpp", "intrinsics_api_impl.cpp", "js_value_call.cpp", - "ts2ets_common.cpp", + "interop_common.cpp", "ts2ets_copy.cpp", "ts2ets_tstype.cpp", "js_job_queue.cpp", diff --git a/plugins/ets/runtime/interop_js/CMakeLists.txt b/plugins/ets/runtime/interop_js/CMakeLists.txt index f928446ed..74da917cc 100644 --- a/plugins/ets/runtime/interop_js/CMakeLists.txt +++ b/plugins/ets/runtime/interop_js/CMakeLists.txt @@ -33,13 +33,13 @@ panda_ets_interop_js_plugin(ets_interop_js_napi ets_proxy/ets_class_wrapper.cpp ets_proxy/ets_field_wrapper.cpp ets_proxy/ets_method_wrapper.cpp - ets_proxy/ets_object_wrapper.cpp - ets_proxy/ets_object_wrappers_storage.cpp + ets_proxy/shared_reference.cpp + ets_proxy/shared_reference_storage.cpp ets_proxy/ets_proxy.cpp js_value.cpp js_refconvert.cpp js_value_call.cpp - ts2ets_common.cpp + interop_common.cpp ts2ets_copy.cpp ts2ets_tstype.cpp js_job_queue.cpp diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp index 782ff40e9..17fcfce73 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp @@ -18,10 +18,10 @@ #include "plugins/ets/runtime/ets_handle.h" #include "plugins/ets/runtime/ets_handle_scope.h" #include "plugins/ets/runtime/interop_js/interop_context.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h" #include "plugins/ets/runtime/interop_js/js_value_call.h" #include "plugins/ets/runtime/interop_js/napi_env_scope.h" +#include "runtime/mem/local_object_handle.h" namespace panda::ets::interop::js::ets_proxy { @@ -34,38 +34,86 @@ public: napi_value WrapImpl(InteropCtx *ctx, EtsObject *ets_object) { - return JSConvertOBJECT::Wrap(ctx, ctx->GetJSEnv(), ets_class_wrapper_, ets_object); + return ets_class_wrapper_->WrapImpl(ctx, ets_object); } - EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value js_value) { - napi_env env = ctx->GetJSEnv(); - std::optional ets_object = JSConvertOBJECT::UnWrap(ctx, env, ets_class_wrapper_, js_value); - if (UNLIKELY(!ets_object.has_value())) { - return nullptr; - } - return ets_object.value(); + return ets_class_wrapper_->UnwrapImpl(ctx, js_value); } private: EtsClassWrapper *ets_class_wrapper_ {}; }; +napi_value EtsClassWrapper::WrapImpl(InteropCtx *ctx, EtsObject *ets_object) +{ + CheckClassInitialized(ets_class_->GetRuntimeClass()); + + napi_env env = ctx->GetJSEnv(); + + ASSERT(ets_object != nullptr); + ASSERT(ets_class_ == ets_object->GetClass()); // TODO(vpukhov): inheritance + + SharedReferenceStorage *storage = ctx->GetProxyRefStorage(); + if (LIKELY(storage->HasReference(ets_object))) { + // The SharedReference already exists for the EtsObject, + // so we have to use it to get the associated napi_value. + SharedReference *shared_ref = storage->GetReference(ets_object); + ASSERT(shared_ref != nullptr); + return shared_ref->GetJsObject(env); + } + + napi_value js_value; + + // ets_object will be wrapped in js_value in responce to js_ctor call + ctx->SetPendingNewInstance(ets_object); + NAPI_CHECK_FATAL(napi_new_instance(env, GetJsCtor(env), 0, nullptr, &js_value)); + return js_value; +} + +EtsObject *EtsClassWrapper::UnwrapImpl(InteropCtx *ctx, napi_value js_value) +{ + CheckClassInitialized(ets_class_->GetRuntimeClass()); + + napi_env env = ctx->GetJSEnv(); + + ASSERT(!IsNullOrUndefined(env, js_value)); + + SharedReferenceStorage *storage = ctx->GetProxyRefStorage(); + SharedReference *shared_ref = storage->GetReference(env, js_value); + if (UNLIKELY(shared_ref == nullptr)) { + InteropCtx::ThrowJSTypeError(env, std::string("Value is not assignable to ") + ets_class_->GetDescriptor()); + return nullptr; + } + + EtsObject *ets_object = shared_ref->GetEtsObject(ctx); + + if (UNLIKELY(!ets_class_->IsAssignableFrom(ets_object->GetClass()))) { + const char *object_descriptor = ets_object->GetClass()->GetDescriptor(); + const char *expected_descriptor = ets_class_->GetDescriptor(); + InteropCtx::ThrowJSTypeError(env, + std::string(object_descriptor) + " is not assignable to " + expected_descriptor); + return nullptr; + } + return ets_object; +} + /*static*/ -std::unique_ptr EtsClassWrapper::CreateRefConv(napi_env env, Class *klass) +std::unique_ptr EtsClassWrapper::CreateJSRefConvert(InteropCtx *ctx, Class *klass) { - EtsClassWrapper *proxy_wrapper = EtsClassWrapper::Get(env, EtsClass::FromRuntimeClass(klass)); - if (UNLIKELY(proxy_wrapper == nullptr)) { + EtsClass *ets_class = EtsClass::FromRuntimeClass(klass); + EtsClassWrapper *wrapper = EtsClassWrapper::Get(ctx, ets_class); + if (UNLIKELY(wrapper == nullptr)) { return nullptr; } - return std::make_unique(proxy_wrapper); + ASSERT(wrapper->ets_class_ == ets_class); + return std::make_unique(wrapper); } /*static*/ -EtsClassWrapper *EtsClassWrapper::Get(napi_env env, EtsClass *ets_class) +EtsClassWrapper *EtsClassWrapper::Get(InteropCtx *ctx, EtsClass *ets_class) { - // TODO(vpukhov): Use JSRefConvertCache instead of EtsClassWrappersCache, #12573 - EtsClassWrappersCache *cache = InteropCtx::Current()->GetEtsClassWrappersCache(); + EtsClassWrappersCache *cache = ctx->GetEtsClassWrappersCache(); ASSERT(ets_class != nullptr); EtsClassWrapper *ets_class_wrapper = cache->Lookup(ets_class); @@ -73,12 +121,14 @@ EtsClassWrapper *EtsClassWrapper::Get(napi_env env, EtsClass *ets_class) return ets_class_wrapper; } - // TODO(vpukhov): trigger Create exactly when we need it! - std::unique_ptr ets_class_wrapper_u = EtsClassWrapper::Create(env, ets_class); - if (UNLIKELY(ets_class_wrapper_u.get() == nullptr)) { + ASSERT(std::string(ets_class->GetDescriptor()).rfind("Lstd/", 0) == std::string::npos); + ASSERT(ctx->GetRefConvertCache()->Lookup(ets_class->GetRuntimeClass()) == nullptr); + + std::unique_ptr wrapper = EtsClassWrapper::Create(ctx->GetJSEnv(), ets_class); + if (UNLIKELY(wrapper == nullptr)) { return nullptr; } - return cache->Insert(ets_class, std::move(ets_class_wrapper_u)); + return cache->Insert(ets_class, std::move(wrapper)); } /*static*/ @@ -89,10 +139,6 @@ std::unique_ptr EtsClassWrapper::Create(napi_env env, EtsClass Class *rt_class = ets_class->GetRuntimeClass(); ASSERT(!ets_class->IsPrimitive()); - if (ets_class->IsStringClass() || ets_class->IsArrayClass()) { - INTEROP_LOG(FATAL) << "String and Array are not supporeted"; - return nullptr; - } const size_t n_fields = rt_class->GetFields().size(); const size_t n_methods = rt_class->GetMethods().size() - 1; // Skip '' @@ -101,139 +147,133 @@ std::unique_ptr EtsClassWrapper::Create(napi_env env, EtsClass js_props.reserve(n_fields + n_methods); // Process fields - std::vector> ets_field_wrappers; - ets_field_wrappers.reserve(n_fields); - Span instance_fields = rt_class->GetInstanceFields(); - for (const Field &field : instance_fields) { - std::unique_ptr ets_field_wrapper = EtsFieldWrapper::Create(_this.get(), &field); - js_props.emplace_back(ets_field_wrapper->MakeNapiProperty(field)); - ets_field_wrappers.push_back(std::move(ets_field_wrapper)); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + _this->ets_field_wrappers_ = std::make_unique(n_fields); + Span ets_field_wrappers(_this->ets_field_wrappers_.get(), n_fields); + size_t field_idx = 0; + + for (const Field &field : rt_class->GetInstanceFields()) { + auto wfield = new (&ets_field_wrappers[field_idx++]) EtsFieldWrapper(_this.get(), &field); + js_props.emplace_back(wfield->MakeNapiProperty(field)); } - Span static_fields = rt_class->GetStaticFields(); - for (const Field &field : static_fields) { - std::unique_ptr ets_field_wrapper = EtsFieldWrapper::Create(_this.get(), &field); - js_props.emplace_back(ets_field_wrapper->MakeNapiStaticProperty(field)); - ets_field_wrappers.push_back(std::move(ets_field_wrapper)); + for (const Field &field : rt_class->GetStaticFields()) { + auto wfield = new (&ets_field_wrappers[field_idx++]) EtsFieldWrapper(_this.get(), &field); + js_props.emplace_back(wfield->MakeNapiStaticProperty(field)); } // Process methods - Span methods = rt_class->GetMethods(); - std::unordered_set names; - std::vector ets_method_wrappers; - ets_method_wrappers.reserve(n_methods); - for (auto &method : methods) { + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + _this->ets_method_wrappers_ = std::make_unique(n_methods); + Span ets_method_wrappers(_this->ets_method_wrappers_.get(), n_methods); + size_t method_idx = 0; + + std::unordered_set method_names; + + for (auto &method : rt_class->GetMethods()) { auto name = method.GetName().data; - auto it = names.insert(name); + auto it = method_names.insert(name); INTEROP_FATAL_IF(!it.second); // no overloading in subset if (UNLIKELY(method.IsInstanceConstructor())) { - _this->ets_ctor_wrapper_ = LazyEtsMethodWrapperLink(&method); + _this->ets_ctor_link_ = LazyEtsMethodWrapperLink(&method); continue; } - if (UNLIKELY(method.IsConstructor())) { + if (UNLIKELY(method.IsStaticConstructor())) { // Skip '' continue; } - ets_method_wrappers.emplace_back(LazyEtsMethodWrapperLink(&method)); - LazyEtsMethodWrapperLink *lazy_link_space = &ets_method_wrappers.back(); - napi_property_descriptor prop = EtsMethodWrapper::MakeNapiProperty(&method, lazy_link_space); + auto lazy_link = new (&ets_method_wrappers[method_idx++]) LazyEtsMethodWrapperLink(&method); + napi_property_descriptor prop = EtsMethodWrapper::MakeNapiProperty(&method, lazy_link); js_props.emplace_back(prop); } - ASSERT(_this->ets_ctor_wrapper_.GetUnresolved() != nullptr); - - _this->ets_field_wrappers_ = std::move(ets_field_wrappers); - _this->ets_method_wrappers_ = std::move(ets_method_wrappers); + ASSERT(_this->ets_ctor_link_.GetUnresolved() != nullptr); napi_value js_ctor {}; - NAPI_CHECK_FATAL(napi_define_class(env, ets_class->GetDescriptor(), NAPI_AUTO_LENGTH, EtsClassWrapper::CtorCallback, - _this.get(), js_props.size(), js_props.data(), &js_ctor)); + NAPI_CHECK_FATAL(napi_define_class(env, ets_class->GetDescriptor(), NAPI_AUTO_LENGTH, + EtsClassWrapper::JSCtorCallback, _this.get(), js_props.size(), js_props.data(), + &js_ctor)); NAPI_CHECK_FATAL(napi_create_reference(env, js_ctor, 1, &_this->js_ctor_ref_)); + NAPI_CHECK_FATAL(napi_object_seal(env, js_ctor)); return _this; } /*static*/ -napi_value EtsClassWrapper::CtorCallback(napi_env env, napi_callback_info cinfo) +napi_value EtsClassWrapper::JSCtorCallback(napi_env env, napi_callback_info cinfo) { - NapiScope scope(env); + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + InteropCtx *ctx = InteropCtx::Current(coro); + [[maybe_unused]] EtsJSNapiEnvScope envscope(ctx, env); - size_t argc; napi_value js_this; + size_t argc; void *data; NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, &js_this, &data)); - - EtsCoroutine *coro = EtsCoroutine::GetCurrent(); - InteropCtx *ctx = InteropCtx::Current(coro); - UniqueEtsObjectReference ets_ref = ctx->GetNewInstance(); auto ets_class_wrapper = reinterpret_cast(data); - if (ets_ref == nullptr) { - ets_class_wrapper->CreateEtsObject(env, cinfo, argc, js_this); - } else { - // Reset NewInstance - ctx->SetNewInstance(nullptr); - - // Create ObjectEtsWrapper if needed - EtsObjectWrappersStorage *storage = ctx->GetEtsObjectWrappersStorage(); - ScopedManagedCodeThreadSwitch managed_scope(coro); - EtsObject *ets_object = EtsObject::FromCoreType(ctx->Refstor()->Get(ets_ref.get())); - if (!storage->HasEtsObjectWrapper(ets_object)) { - EtsObjectWrapper *mem = storage->CreateObjectEtsWrapper(ets_object); - if (UNLIKELY(mem == nullptr)) { - InteropCtx::ThrowJSError(env, "Cannot create ObjectEtsWrapper"); - return nullptr; - } - EtsObjectWrapper::CreateInplace(mem, env, js_this, std::move(ets_ref), ets_class_wrapper); + + EtsObject *ets_object = ctx->AcquirePendingNewInstance(); + // wrap existing object + if (LIKELY(ets_object != nullptr)) { + SharedReferenceStorage *storage = ctx->GetProxyRefStorage(); + if (UNLIKELY(!storage->CreateReference(ctx, ets_object, js_this))) { + ASSERT(InteropCtx::SanityJSExceptionPending()); + return nullptr; } + NAPI_CHECK_FATAL(napi_object_seal(env, js_this)); // TODO(vpukhov): broken in ace napi + return nullptr; } - return js_this; -} -UniqueEtsObjectReference EtsClassWrapper::CreateEtsObject(napi_env env, napi_callback_info cinfo, size_t argc, - napi_value js_this) -{ - EtsCoroutine *coro = EtsCoroutine::GetCurrent(); - InteropCtx *ctx = InteropCtx::Current(coro); - [[maybe_unused]] EtsJSNapiEnvScope envscope(ctx, env); + auto js_args = ctx->GetTempArgs(argc); + NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, js_args->data(), nullptr, nullptr)); + + napi_value js_newtarget; + NAPI_CHECK_FATAL(napi_get_new_target(env, cinfo, &js_newtarget)); - EtsMethodWrapper *ets_ctor_wrapper = EtsMethodWrapper::ResolveLazyLink(env, ets_ctor_wrapper_); - if (UNLIKELY(ets_ctor_wrapper == nullptr)) { + // create new object and wrap it + if (UNLIKELY(!ets_class_wrapper->CreateAndWrap(env, js_newtarget, js_this, *js_args))) { + ASSERT(InteropCtx::SanityJSExceptionPending()); return nullptr; } + return nullptr; +} - std::vector &js_args = ctx->GetTempArgs(argc); - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, js_args.data(), nullptr, nullptr)); +bool EtsClassWrapper::CreateAndWrap(napi_env env, napi_value js_newtarget, napi_value js_this, Span js_args) +{ + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + InteropCtx *ctx = InteropCtx::Current(coro); ScopedManagedCodeThread managed_scope(coro); - if (!coro->GetPandaVM()->GetClassLinker()->InitializeClass(coro, ets_class_)) { + + if (UNLIKELY(!CheckClassInitialized(ets_class_->GetRuntimeClass()))) { ctx->ForwardEtsException(coro); - return nullptr; + return false; + } + + LocalObjectHandle ets_object(coro, EtsObject::Create(ets_class_)); + if (UNLIKELY(ets_object.GetPtr() == nullptr)) { + return false; } - EtsHandleScope scope(coro); - EtsHandle ets_object(coro, EtsObject::Create(ets_class_)); + SharedReferenceStorage *storage = ctx->GetProxyRefStorage(); + if (UNLIKELY(!storage->CreateReference(ctx, ets_object.GetPtr(), js_this))) { + ASSERT(InteropCtx::SanityJSExceptionPending()); + return false; + } - EtsObjectWrappersStorage *storage = ctx->GetEtsObjectWrappersStorage(); - if (!storage->HasEtsObjectWrapper(ets_object.GetPtr())) { - // Create New EtsObjectWrapper - mem::Reference *ref = ctx->Refstor()->Add(ets_object->GetCoreType(), mem::Reference::ObjectType::GLOBAL); - if (UNLIKELY(ref == nullptr)) { - InteropCtx::ThrowJSError(env, "Cannot create EtsObjectWrapper, ref=nullptr"); - return nullptr; - } - UniqueEtsObjectReference ets_ref(ref); - EtsObjectWrapper *mem = storage->CreateObjectEtsWrapper(ets_object.GetPtr()); - if (UNLIKELY(mem == nullptr)) { - InteropCtx::ThrowJSError(env, "Cannot create EtsObjectWrapper, mem=nullptr"); - return nullptr; - } - EtsObjectWrapper::CreateInplace(mem, env, js_this, std::move(ets_ref), this); + bool new_self; + NAPI_CHECK_FATAL(napi_strict_equals(env, js_newtarget, GetJsCtor(env), &new_self)); + if (LIKELY(new_self)) { + NAPI_CHECK_FATAL(napi_object_seal(env, js_this)); // TODO(vpukhov): broken in ace napi + } else { + // TODO(vpukhov): overriding via static-wrapper class } - Method *method = ets_ctor_wrapper->GetEtsMethod()->GetPandaMethod(); - ASSERT(method->IsStatic() == false); - ASSERT(method->IsInstanceConstructor() == true); - EtsCallImplInstance(coro, ctx, method, {js_args.data(), js_args.size()}, ets_object.GetPtr()); + EtsMethodWrapper *ctor_wrapper = EtsMethodWrapper::ResolveLazyLink(ctx, ets_ctor_link_); + ASSERT(ctor_wrapper != nullptr); + EtsMethod *ctor_method = ctor_wrapper->GetEtsMethod(); + ASSERT(ctor_method->IsInstanceConstructor()); - return nullptr; + napi_value call_res = EtsCallImplInstance(coro, ctx, ctor_method->GetPandaMethod(), js_args, ets_object.GetPtr()); + return call_res != nullptr; } } // namespace panda::ets::interop::js::ets_proxy diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h index 27e7f51c8..4c044430b 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h @@ -20,9 +20,8 @@ #include "plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h" #include "plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h" #include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/lazy_ets_class_wrapper_link.h" #include "plugins/ets/runtime/interop_js/js_refconvert.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include #include @@ -35,7 +34,6 @@ namespace panda::ets::interop::js::ets_proxy { using EtsClassWrappersCache = WrappersCache; -// TODO(vpukhov): Split this class into JSRefConvert-derived and GetEtsClass-related parts class EtsClassWrapper { public: // clang-format off @@ -46,26 +44,10 @@ public: // clang-format on static std::unique_ptr Create(napi_env env, EtsClass *ets_class); - ~EtsClassWrapper() = default; - - static EtsClassWrapper *Get(napi_env env, EtsClass *ets_class); - static std::unique_ptr CreateRefConv(napi_env env, Class *klass); + static EtsClassWrapper *Get(InteropCtx *ctx, EtsClass *ets_class); - static inline EtsClassWrapper *ResolveLazyLink(napi_env env, /*in/out*/ LazyEtsClassWrapperLink &lazy_link) - { - if (LIKELY(lazy_link.IsResolved())) { - return lazy_link.GetResolved(); - } - EtsClass *ets_class = lazy_link.GetUnresolved(); - EtsClassWrapper *ets_class_wrapper = EtsClassWrapper::Get(env, ets_class); - if (UNLIKELY(ets_class_wrapper == nullptr)) { - return nullptr; - } - // Update lazy_link - lazy_link = LazyEtsClassWrapperLink(ets_class_wrapper); - return ets_class_wrapper; - } + static std::unique_ptr CreateJSRefConvert(InteropCtx *ctx, Class *klass); EtsClass *GetEtsClass() { @@ -79,24 +61,26 @@ public: return ctor; } + napi_value WrapImpl(InteropCtx *ctx, EtsObject *ets_object); + EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value js_value); + + ~EtsClassWrapper() = default; + private: explicit EtsClassWrapper(EtsClass *ets_cls) : ets_class_(ets_cls) {} NO_COPY_SEMANTIC(EtsClassWrapper); NO_MOVE_SEMANTIC(EtsClassWrapper); - UniqueEtsObjectReference CreateEtsObject(napi_env env, napi_callback_info cinfo, size_t argc, napi_value js_this); - static napi_value CtorCallback(napi_env env, napi_callback_info cinfo); + bool CreateAndWrap(napi_env env, napi_value js_newtarget, napi_value js_this, Span js_args); + static napi_value JSCtorCallback(napi_env env, napi_callback_info cinfo); EtsClass *ets_class_ {}; - LazyEtsMethodWrapperLink ets_ctor_wrapper_ {}; + LazyEtsMethodWrapperLink ets_ctor_link_ {}; napi_ref js_ctor_ref_ {}; - // TODO(v.cherkashin): Reduce memory consumption, #12573 - // The following fields are only needed to store objects, - // and they are not used in this class, so we can move them to another space, - // thereby reducing the size of this object - std::vector ets_method_wrappers_; - std::vector> ets_field_wrappers_; + // TODO(vpukhov): allocate inplace to reduce memory consumption + std::unique_ptr ets_method_wrappers_; // NOLINT(modernize-avoid-c-arrays) + std::unique_ptr ets_field_wrappers_; // NOLINT(modernize-avoid-c-arrays) }; } // namespace panda::ets::interop::js::ets_proxy diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.cpp index 1d1899e97..e41770bf1 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.cpp @@ -20,12 +20,43 @@ #include "plugins/ets/runtime/ets_handle.h" #include "plugins/ets/runtime/ets_handle_scope.h" #include "plugins/ets/runtime/interop_js/js_convert.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h" +#include "plugins/ets/runtime/interop_js/napi_env_scope.h" #include "plugins/ets/runtime/types/ets_object.h" +#include "runtime/mem/local_object_handle.h" + +#include "runtime/mem/vm_handle-inl.h" namespace panda::ets::interop::js::ets_proxy { +template +static EtsObject *EtsAccessorsHandleThis(EtsFieldWrapper *field_wrapper, EtsCoroutine *coro, InteropCtx *ctx, + napi_env env, napi_value js_this) +{ + if constexpr (IS_STATIC) { + EtsClass *ets_class = field_wrapper->GetOwner()->GetEtsClass(); + if (UNLIKELY(!coro->GetPandaVM()->GetClassLinker()->InitializeClass(coro, ets_class))) { + ctx->ForwardEtsException(coro); + return nullptr; + } + return ets_class->AsObject(); + } + + if (UNLIKELY(IsNullOrUndefined(env, js_this))) { + ctx->ThrowJSTypeError(env, "ets this in set accessor cannot be null or undefined"); + return nullptr; + } + + EtsObject *ets_this = field_wrapper->GetOwner()->UnwrapImpl(ctx, js_this); + if (UNLIKELY(ets_this == nullptr)) { + if (coro->HasPendingException()) { + ctx->ForwardEtsException(coro); + } + return nullptr; + } + return ets_this; +} + template static napi_value EtsFieldGetter(napi_env env, napi_callback_info cinfo) { @@ -41,24 +72,18 @@ static napi_value EtsFieldGetter(napi_env env, napi_callback_info cinfo) auto ets_field_wrapper = reinterpret_cast(data); EtsCoroutine *coro = EtsCoroutine::GetCurrent(); InteropCtx *ctx = InteropCtx::Current(coro); + [[maybe_unused]] EtsJSNapiEnvScope scope(ctx, env); ScopedManagedCodeThread managed_scope(coro); - EtsHandleScope handle_scope(coro); - EtsHandle ets_object(coro, nullptr); - if constexpr (IS_STATIC) { - EtsClass *ets_class = ets_field_wrapper->GetOwner()->GetEtsClass(); - if (UNLIKELY(!coro->GetPandaVM()->GetClassLinker()->InitializeClass(coro, ets_class))) { - ctx->ForwardEtsException(coro); - return nullptr; - } - ets_object = EtsHandle(coro, ets_class->AsObject()); - } else { - std::optional opt = JSConvertOBJECT::UnWrap(ctx, env, ets_field_wrapper->GetOwner(), js_this); - if (UNLIKELY(!opt.has_value())) { - return napi_value {}; - } - ets_object = EtsHandle(coro, opt.value()); + + EtsObject *ets_this = EtsAccessorsHandleThis(ets_field_wrapper, coro, ctx, env, js_this); + if (UNLIKELY(ets_this == nullptr)) { + ASSERT(ctx->SanityJSExceptionPending()); + return nullptr; } - return FieldAccessor::Getter(ctx, env, std::move(ets_object), ets_field_wrapper); + + napi_value res = FieldAccessor::Getter(ctx, env, ets_this, ets_field_wrapper); + ASSERT(res != nullptr || ctx->SanityJSExceptionPending()); + return res; } template @@ -77,44 +102,57 @@ static napi_value EtsFieldSetter(napi_env env, napi_callback_info cinfo) auto ets_field_wrapper = reinterpret_cast(data); EtsCoroutine *coro = EtsCoroutine::GetCurrent(); InteropCtx *ctx = InteropCtx::Current(coro); + [[maybe_unused]] EtsJSNapiEnvScope scope(ctx, env); ScopedManagedCodeThread managed_scope(coro); - EtsHandleScope handle_scope(coro); - EtsHandle ets_object(coro, nullptr); - if constexpr (IS_STATIC) { - EtsClass *ets_class = ets_field_wrapper->GetOwner()->GetEtsClass(); - if (UNLIKELY(!coro->GetPandaVM()->GetClassLinker()->InitializeClass(coro, ets_class))) { + + EtsObject *ets_this = EtsAccessorsHandleThis(ets_field_wrapper, coro, ctx, env, js_this); + if (UNLIKELY(ets_this == nullptr)) { + ASSERT(ctx->SanityJSExceptionPending()); + return nullptr; + } + + LocalObjectHandle ets_this_handle(coro, ets_this); + auto res = FieldAccessor::Setter(ctx, env, EtsHandle(VMHandle(ets_this_handle)), + ets_field_wrapper, js_value); + if (UNLIKELY(!res)) { + if (coro->HasPendingException()) { ctx->ForwardEtsException(coro); - return nullptr; - } - ets_object = EtsHandle(coro, ets_class->AsObject()); - } else { - std::optional opt = JSConvertOBJECT::UnWrap(ctx, env, ets_field_wrapper->GetOwner(), js_this); - if (UNLIKELY(!opt.has_value())) { - return napi_value {}; } - ets_object = EtsHandle(coro, opt.value()); + ASSERT(ctx->SanityJSExceptionPending()); } - FieldAccessor::Setter(ctx, env, std::move(ets_object), ets_field_wrapper, js_value); - return napi_value {}; + return nullptr; } -struct EtsFieldAccessorOBJECT { - static napi_value Getter(InteropCtx *ctx, napi_env env, EtsHandle ets_object, - EtsFieldWrapper *ets_field_wrapper) +struct EtsFieldAccessorREFERENCE { + static napi_value Getter(InteropCtx *ctx, napi_env env, EtsObject *ets_object, EtsFieldWrapper *ets_field_wrapper) { - EtsClassWrapper *ets_class_wrapper = ets_field_wrapper->GetEtsClassWrapper(env); EtsObject *ets_value = ets_object->GetFieldObject(ets_field_wrapper->GetOffset()); - return JSConvertOBJECT::Wrap(ctx, env, ets_class_wrapper, ets_value); + if (ets_value == nullptr) { + return GetNull(env); + } + auto refconv = JSRefConvertResolve(ctx, ets_value->GetClass()->GetRuntimeClass()); + ASSERT(refconv != nullptr); + return refconv->Wrap(ctx, ets_value); } - static void Setter(InteropCtx *ctx, napi_env env, EtsHandle ets_object, + static bool Setter(InteropCtx *ctx, napi_env env, EtsHandle ets_object, EtsFieldWrapper *ets_field_wrapper, napi_value js_value) { - EtsClassWrapper *ets_class_wrapper = ets_field_wrapper->GetEtsClassWrapper(env); - std::optional ets_value = JSConvertOBJECT::UnWrap(ctx, env, ets_class_wrapper, js_value); - if (LIKELY(ets_value.has_value())) { - ets_object->SetFieldObject(ets_field_wrapper->GetOffset(), ets_value.value()); + EtsObject *ets_value; + if (IsNullOrUndefined(env, js_value)) { + ets_value = nullptr; + } else { + JSRefConvert *refconv = ets_field_wrapper->GetRefConvert(ctx); + if (UNLIKELY(refconv == nullptr)) { + return false; + } + ets_value = refconv->Unwrap(ctx, js_value); + if (UNLIKELY(ets_value == nullptr)) { + return false; + } } + ets_object->SetFieldObject(ets_field_wrapper->GetOffset(), ets_value); + return true; } }; @@ -122,91 +160,99 @@ template struct EtsFieldAccessorPRIMITIVE { using PrimitiveType = typename Convertor::cpptype; - static napi_value Getter(InteropCtx * /*ctx*/, napi_env env, EtsHandle ets_object, + static napi_value Getter(InteropCtx * /*ctx*/, napi_env env, EtsObject *ets_object, EtsFieldWrapper *ets_field_wrapper) { auto ets_value = ets_object->GetFieldPrimitive(ets_field_wrapper->GetOffset()); return Convertor::Wrap(env, ets_value); } - static void Setter(InteropCtx *ctx, napi_env env, EtsHandle ets_object, + // TODO(vpukhov): elide ets_object handle + static bool Setter(InteropCtx *ctx, napi_env env, EtsHandle ets_object, EtsFieldWrapper *ets_field_wrapper, napi_value js_value) { std::optional ets_value = Convertor::Unwrap(ctx, env, js_value); if (LIKELY(ets_value.has_value())) { ets_object->SetFieldPrimitive(ets_field_wrapper->GetOffset(), ets_value.value()); } + return ets_value.has_value(); } }; -EtsClassWrapper *EtsFieldWrapper::GetEtsClassWrapper(napi_env env) +template +JSRefConvert *EtsFieldWrapper::GetRefConvert(InteropCtx *ctx) { - if (LIKELY(lazy_ets_class_wrapper_link_.IsResolved())) { - return lazy_ets_class_wrapper_link_.GetResolved(); + if (LIKELY(lazy_refconvert_link_.IsResolved())) { + return lazy_refconvert_link_.GetResolved(); } - const Field *field = lazy_ets_class_wrapper_link_.GetUnresolved(); - if (UNLIKELY(field->GetTypeId() != panda_file::Type::TypeId::REFERENCE)) { - return nullptr; - } + const Field *field = lazy_refconvert_link_.GetUnresolved(); + ASSERT(field->GetTypeId() == panda_file::Type::TypeId::REFERENCE); - EtsClass *ets_class = EtsClass::FromRuntimeClass(field->ResolveTypeClass()); - EtsClassWrapper *ets_class_wrapper = EtsClassWrapper::Get(env, ets_class); - if (UNLIKELY(ets_class_wrapper == nullptr)) { + const auto *panda_file = field->GetPandaFile(); + auto *class_linker = Runtime::GetCurrent()->GetClassLinker(); + Class *field_class = + class_linker->GetClass(*panda_file, panda_file::FieldDataAccessor::GetTypeId(*panda_file, field->GetFileId()), + ctx->LinkerCtx(), nullptr); + + JSRefConvert *refconv = JSRefConvertResolve(ctx, field_class); + if (UNLIKELY(refconv == nullptr)) { return nullptr; } - - // Update lazy_link - lazy_ets_class_wrapper_link_ = FieldLazyEtsClassWrapperLink(ets_class_wrapper); - return ets_class_wrapper; + lazy_refconvert_link_.Set(refconv); // Update link + return refconv; } +// Explicit instantiation +template JSRefConvert *EtsFieldWrapper::GetRefConvert(InteropCtx *ctx); +template JSRefConvert *EtsFieldWrapper::GetRefConvert(InteropCtx *ctx); + template -static napi_property_descriptor DoMakeNapiProperty(const Field &field, void *prop_data) +static napi_property_descriptor DoMakeNapiProperty(const Field &field, EtsFieldWrapper *wrapper) { napi_property_descriptor prop {}; prop.utf8name = utf::Mutf8AsCString(field.GetName().data); prop.attributes = IS_STATIC ? EtsClassWrapper::STATIC_FIELD_ATTR : EtsClassWrapper::FIELD_ATTR; - prop.data = prop_data; + prop.data = wrapper; + + auto setup_accessors = [&](auto accessor_tag) { + using Accessor = typename decltype(accessor_tag)::type; + prop.getter = EtsFieldGetter; + prop.setter = EtsFieldSetter; + return prop; + }; panda_file::Type type = field.GetType(); switch (type.GetId()) { - case panda_file::Type::TypeId::U1: { - prop.getter = EtsFieldGetter, IS_STATIC>; - prop.setter = EtsFieldSetter, IS_STATIC>; - break; - } - case panda_file::Type::TypeId::I32: { - prop.getter = EtsFieldGetter, IS_STATIC>; - prop.setter = EtsFieldSetter, IS_STATIC>; - break; - } - case panda_file::Type::TypeId::I64: { - prop.getter = EtsFieldGetter, IS_STATIC>; - prop.setter = EtsFieldSetter, IS_STATIC>; - break; - } - case panda_file::Type::TypeId::F32: { - prop.getter = EtsFieldGetter, IS_STATIC>; - prop.setter = EtsFieldSetter, IS_STATIC>; - break; - } - case panda_file::Type::TypeId::F64: { - prop.getter = EtsFieldGetter, IS_STATIC>; - prop.setter = EtsFieldSetter, IS_STATIC>; - break; - } - case panda_file::Type::TypeId::REFERENCE: { - prop.getter = EtsFieldGetter; - prop.setter = EtsFieldSetter; - break; - } - default: { - INTEROP_LOG(FATAL) << "UNIMPLEMENTED, type=" << type; - break; - } + case panda_file::Type::TypeId::U1: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::I8: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::U8: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::I16: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::U16: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::I32: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::U32: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::I64: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::U64: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::F32: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::F64: + return setup_accessors(helpers::TypeIdentity>()); + case panda_file::Type::TypeId::REFERENCE: + return setup_accessors(helpers::TypeIdentity()); + default: + InteropCtx::Fatal(std::string("ConvertEtsVal: unsupported typeid ") + + panda_file::Type::GetSignatureByTypeId(type)); } - return prop; + UNREACHABLE(); } napi_property_descriptor EtsFieldWrapper::MakeNapiProperty(const Field &field) diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h b/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h index 6650af4b8..82ef5bea6 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h @@ -23,6 +23,12 @@ #include "runtime/include/field.h" namespace panda { + +namespace ets::interop::js { +class InteropCtx; +class JSRefConvert; +} // namespace ets::interop::js + class Field; } // namespace panda @@ -32,9 +38,9 @@ class EtsClassWrapper; class EtsFieldWrapper { public: - static std::unique_ptr Create(EtsClassWrapper *owner, const Field *field) + EtsFieldWrapper(EtsClassWrapper *owner, const Field *field) + : owner_(owner), lazy_refconvert_link_(field), offset_(field->GetOffset()) { - return std::unique_ptr(new EtsFieldWrapper(owner, field)); } EtsClassWrapper *GetOwner() @@ -47,21 +53,20 @@ public: return offset_; } - EtsClassWrapper *GetEtsClassWrapper(napi_env env); + template + JSRefConvert *GetRefConvert(InteropCtx *ctx); napi_property_descriptor MakeNapiProperty(const Field &field); napi_property_descriptor MakeNapiStaticProperty(const Field &field); -private: - using FieldLazyEtsClassWrapperLink = TypedPointer; - - EtsFieldWrapper(EtsClassWrapper *owner, const Field *field) - : owner_(owner), lazy_ets_class_wrapper_link_(field), offset_(field->GetOffset()) - { - } + EtsFieldWrapper() = default; + ~EtsFieldWrapper() = default; + NO_COPY_SEMANTIC(EtsFieldWrapper); + NO_MOVE_SEMANTIC(EtsFieldWrapper); +private: EtsClassWrapper *owner_ {}; - FieldLazyEtsClassWrapperLink lazy_ets_class_wrapper_link_ {}; + TypedPointer lazy_refconvert_link_ {}; uint32_t offset_ {}; }; diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.cpp index 8d664fc72..20f514def 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.cpp @@ -17,8 +17,7 @@ #include "plugins/ets/runtime/interop_js/interop_context.h" #include "plugins/ets/runtime/interop_js/js_refconvert.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h" #include "plugins/ets/runtime/interop_js/js_value_call.h" #include "plugins/ets/runtime/interop_js/napi_env_scope.h" @@ -27,35 +26,57 @@ namespace panda::ets::interop::js::ets_proxy { /*static*/ -std::unique_ptr EtsMethodWrapper::Create(napi_env env, EtsMethod *ets_method) +std::unique_ptr EtsMethodWrapper::CreateMethod(EtsMethod *ets_method) { - auto *_this = new EtsMethodWrapper(); // NOLINT(readability-identifier-naming) - _this->ets_method_ = ets_method; - - NAPI_CHECK_FATAL(napi_create_function(env, _this->ets_method_->GetName(), NAPI_AUTO_LENGTH, - &EtsMethodCallHandler, _this, - &_this->js_value_)); - NAPI_CHECK_FATAL(napi_create_reference(env, _this->js_value_, 1, &_this->js_ref_)); + auto wrapper = std::make_unique(); + wrapper->ets_method_ = ets_method; + return wrapper; +} - return std::unique_ptr(_this); +/*static*/ +std::unique_ptr EtsMethodWrapper::CreateFunction(InteropCtx *ctx, EtsMethod *ets_method) +{ + ASSERT(ets_method->IsStatic()); + auto env = ctx->GetJSEnv(); + auto wrapper = CreateMethod(ets_method); + + napi_value js_value; + NAPI_CHECK_FATAL(napi_create_function(env, wrapper->ets_method_->GetName(), NAPI_AUTO_LENGTH, + &EtsMethodCallHandler, wrapper.get(), + &js_value)); + NAPI_CHECK_FATAL(napi_create_reference(env, js_value, 1, &wrapper->js_ref_)); + NAPI_CHECK_FATAL(napi_object_seal(env, js_value)); + + return wrapper; } /*static*/ -EtsMethodWrapper *EtsMethodWrapper::Get(napi_env env, EtsMethod *ets_method) +EtsMethodWrapper *EtsMethodWrapper::GetMethod(InteropCtx *ctx, EtsMethod *ets_method) +{ + EtsMethodWrappersCache *cache = ctx->GetEtsMethodWrappersCache(); + EtsMethodWrapper *wrapper = cache->Lookup(ets_method); + if (LIKELY(wrapper != nullptr)) { + return wrapper; + } + + std::unique_ptr ets_method_wrapper = EtsMethodWrapper::CreateMethod(ets_method); + return cache->Insert(ets_method, std::move(ets_method_wrapper)); +} + +EtsMethodWrapper *EtsMethodWrapper::GetFunction(InteropCtx *ctx, EtsMethod *ets_method) { - EtsMethodWrappersCache *cache = InteropCtx::Current()->GetEtsMethodWrappersCache(); - EtsMethodWrapper *raw_ets_method_wrapper = cache->Lookup(ets_method); - if (LIKELY(raw_ets_method_wrapper != nullptr)) { - return raw_ets_method_wrapper; + EtsMethodWrappersCache *cache = ctx->GetEtsMethodWrappersCache(); + EtsMethodWrapper *wrapper = cache->Lookup(ets_method); + if (LIKELY(wrapper != nullptr)) { + return wrapper; } - std::unique_ptr ets_method_wrapper = EtsMethodWrapper::Create(env, ets_method); - raw_ets_method_wrapper = cache->Insert(ets_method, std::move(ets_method_wrapper)); - return raw_ets_method_wrapper; + std::unique_ptr ets_func_wrapper = EtsMethodWrapper::CreateFunction(ctx, ets_method); + return cache->Insert(ets_method, std::move(ets_func_wrapper)); } /* static */ -napi_property_descriptor EtsMethodWrapper::MakeNapiProperty(Method *method, LazyEtsMethodWrapperLink *lazy_link_space) +napi_property_descriptor EtsMethodWrapper::MakeNapiProperty(Method *method, LazyEtsMethodWrapperLink *lazy_link) { napi_callback callback {}; if (method->IsStatic()) { @@ -68,7 +89,7 @@ napi_property_descriptor EtsMethodWrapper::MakeNapiProperty(Method *method, Lazy prop.utf8name = utf::Mutf8AsCString(method->GetName().data); prop.method = callback; prop.attributes = method->IsStatic() ? EtsClassWrapper::STATIC_METHOD_ATTR : EtsClassWrapper::METHOD_ATTR; - prop.data = lazy_link_space; + prop.data = lazy_link; return prop; } @@ -81,12 +102,12 @@ napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env env, napi_callback_in InteropCtx *ctx = InteropCtx::Current(coro); [[maybe_unused]] EtsJSNapiEnvScope envscope(ctx, env); - size_t constexpr ARGS_OPT_SZ = 16U; // Preallocated size - size_t argc = ARGS_OPT_SZ; - auto &js_args = ctx->GetTempArgs(argc); + size_t argc; napi_value js_this; void *data; - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, js_args.data(), &js_this, &data)); + NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, nullptr, nullptr)); + auto js_args = ctx->GetTempArgs(argc); + NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, js_args->data(), &js_this, &data)); EtsMethodWrapper *_this; // NOLINT(readability-identifier-naming) @@ -94,7 +115,7 @@ napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env env, napi_callback_in _this = reinterpret_cast(data); } else { auto lazy_link = reinterpret_cast(data); - _this = EtsMethodWrapper::ResolveLazyLink(env, *lazy_link); + _this = EtsMethodWrapper::ResolveLazyLink(ctx, *lazy_link); if (UNLIKELY(_this == nullptr)) { return nullptr; } @@ -109,13 +130,26 @@ napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env env, napi_callback_in ctx->ForwardEtsException(coro); return nullptr; } - return EtsCallImplStatic(coro, ctx, method, {js_args.data(), js_args.size()}); + return EtsCallImplStatic(coro, ctx, method, *js_args); + } + + if (UNLIKELY(IsNullOrUndefined(env, js_this))) { + ctx->ThrowJSTypeError(env, "ets this in instance method cannot be null or undefined"); + return nullptr; + } + + JSRefConvert *refconv = JSRefConvertResolve(ctx, ets_class->GetRuntimeClass()); + + EtsObject *ets_this = refconv->Unwrap(ctx, js_this); + if (UNLIKELY(ets_this == nullptr)) { + if (coro->HasPendingException()) { + ctx->ForwardEtsException(coro); + } + ASSERT(ctx->SanityJSExceptionPending()); + return nullptr; } - EtsClassWrapper *ets_class_wrapper = EtsClassWrapper::Get(env, ets_class); - std::optional this_ets_object = JSConvertOBJECT::UnWrap(ctx, env, ets_class_wrapper, js_this); - ASSERT(this_ets_object.has_value()); - return EtsCallImplInstance(coro, ctx, method, {js_args.data(), js_args.size()}, this_ets_object.value()); + return EtsCallImplInstance(coro, ctx, method, *js_args, ets_this); } // Explicit instantiation diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h b/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h index d8b36df0c..3981f799e 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h @@ -18,10 +18,15 @@ #include "plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h" #include "plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/types/ets_method.h" #include +namespace panda::ets::interop::js { +class InteropCtx; +} // namespace panda::ets::interop::js + namespace panda::ets::interop::js::ets_proxy { class EtsMethodWrapper; @@ -31,11 +36,15 @@ using EtsMethodWrappersCache = WrappersCache; class EtsMethodWrapper { public: - static EtsMethodWrapper *Get(napi_env env, EtsMethod *ets_method); + static EtsMethodWrapper *GetMethod(InteropCtx *ctx, EtsMethod *ets_method); + static EtsMethodWrapper *GetFunction(InteropCtx *ctx, EtsMethod *ets_method); - napi_value GetJsValue() const + napi_value GetJsValue(napi_env env) const { - return js_value_; + ASSERT(js_ref_); + napi_value js_value; + NAPI_CHECK_FATAL(napi_get_reference_value(env, js_ref_, &js_value)); + return js_value; } EtsMethod *GetEtsMethod() const @@ -43,19 +52,20 @@ public: return ets_method_; } - static inline EtsMethodWrapper *ResolveLazyLink(napi_env env, /* in/out */ LazyEtsMethodWrapperLink &lazy_link) + static inline EtsMethodWrapper *ResolveLazyLink(InteropCtx *ctx, /* in/out */ LazyEtsMethodWrapperLink &lazy_link) { if (LIKELY(lazy_link.IsResolved())) { return lazy_link.GetResolved(); } - EtsMethod *ets_mathod = EtsMethod::FromRuntimeMethod(lazy_link.GetUnresolved()); - EtsMethodWrapper *ets_method_wrapper = EtsMethodWrapper::Get(env, ets_mathod); - if (UNLIKELY(ets_method_wrapper == nullptr)) { + EtsMethod *ets_method = EtsMethod::FromRuntimeMethod(lazy_link.GetUnresolved()); + EtsMethodWrapper *wrapper = EtsMethodWrapper::GetMethod(ctx, ets_method); + if (UNLIKELY(wrapper == nullptr)) { return nullptr; } + ASSERT(wrapper->js_ref_ == nullptr); // Update lazy_link - lazy_link = LazyEtsMethodWrapperLink(ets_method_wrapper); - return ets_method_wrapper; + lazy_link = LazyEtsMethodWrapperLink(wrapper); + return wrapper; } static napi_property_descriptor MakeNapiProperty(Method *method, LazyEtsMethodWrapperLink *lazy_link_space); @@ -64,10 +74,10 @@ public: static napi_value EtsMethodCallHandler(napi_env env, napi_callback_info cinfo); private: - static std::unique_ptr Create(napi_env env, EtsMethod *method); + static std::unique_ptr CreateMethod(EtsMethod *method); + static std::unique_ptr CreateFunction(InteropCtx *ctx, EtsMethod *method); mutable EtsMethod *ets_method_ {}; - napi_value js_value_ {}; napi_ref js_ref_ {}; // only for functions (ETSGLOBAL::) }; diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h index 6fad0f6b3..570decdcf 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h @@ -18,7 +18,7 @@ #include "libpandabase/macros.h" #include "libpandabase/utils/logger.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include namespace panda::mem { diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp index f0d1fc015..0c3d4ffcd 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp @@ -18,12 +18,15 @@ #include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h" #include "plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h" #include "plugins/ets/runtime/interop_js/interop_context.h" +#include "plugins/ets/runtime/interop_js/napi_env_scope.h" namespace panda::ets::interop::js::ets_proxy { napi_value GetETSFunction(napi_env env, std::string_view class_descriptor, std::string_view method_name) { EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + InteropCtx *ctx = InteropCtx::Current(coro); + [[maybe_unused]] EtsJSNapiEnvScope envscope(ctx, env); ScopedManagedCodeThread managed_scope(coro); EtsClass *ets_class = coro->GetPandaVM()->GetClassLinker()->GetClass(class_descriptor.data()); @@ -39,26 +42,29 @@ napi_value GetETSFunction(napi_env env, std::string_view class_descriptor, std:: return nullptr; } - EtsMethodWrapper *ets_method_wrapper = EtsMethodWrapper::Get(env, ets_method); + EtsMethodWrapper *ets_method_wrapper = EtsMethodWrapper::GetFunction(ctx, ets_method); if (UNLIKELY(ets_method_wrapper == nullptr)) { InteropCtx::ThrowJSError(env, "GetETSFunction: cannot get EtsMethodWrapper, class_descriptor=" + std::string(class_descriptor) + " method_name=" + std::string(method_name)); return nullptr; } - return ets_method_wrapper->GetJsValue(); + return ets_method_wrapper->GetJsValue(env); } napi_value GetETSClass(napi_env env, std::string_view class_descriptor) { EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + InteropCtx *ctx = InteropCtx::Current(coro); + [[maybe_unused]] EtsJSNapiEnvScope envscope(ctx, env); ScopedManagedCodeThread managed_scope(coro); + EtsClass *ets_klass = coro->GetPandaVM()->GetClassLinker()->GetClass(class_descriptor.data()); if (UNLIKELY(ets_klass == nullptr)) { InteropCtx::ThrowJSError(env, "GetETSClass: unresolved klass " + std::string(class_descriptor)); return nullptr; } - EtsClassWrapper *ets_class_wrapper = EtsClassWrapper::Get(env, ets_klass); + EtsClassWrapper *ets_class_wrapper = EtsClassWrapper::Get(ctx, ets_klass); if (UNLIKELY(ets_class_wrapper == nullptr)) { return nullptr; } diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h b/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h index 7bc58f81a..e119b8b1c 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h @@ -21,7 +21,6 @@ namespace panda::ets::interop::js::ets_proxy { -// TODO(vpukhov): Make these functions a general absraction for JSRefConvert napi_value GetETSFunction(napi_env env, std::string_view class_descriptor, std::string_view method_name); napi_value GetETSClass(napi_env env, std::string_view class_descriptor); diff --git a/plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h b/plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h deleted file mode 100644 index aaa076d43..000000000 --- a/plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_JS_CONVERT_OBJECT_H_ -#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_JS_CONVERT_OBJECT_H_ - -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" // TODO(v.cherkashin): Move NAPI_CHECK_FATAL() to separated filem, #12573 -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" -#include "plugins/ets/runtime/interop_js/interop_context.h" - -#include - -namespace panda::ets { -class EtsObject; -} // namespace panda::ets - -namespace panda::ets::interop::js::ets_proxy { - -struct JSConvertOBJECT { - static inline napi_value Wrap(InteropCtx *ctx, napi_env env, EtsClassWrapper *ets_class_wrapper, - EtsObject *ets_value) - { - ASSERT(ets_class_wrapper != nullptr); - if (UNLIKELY(ets_value == nullptr)) { - return GetNull(env); - } - ASSERT(ets_class_wrapper == EtsClassWrapper::Get(env, ets_value->GetClass())); - - EtsObjectWrappersStorage *storage = ctx->GetEtsObjectWrappersStorage(); - if (storage->HasEtsObjectWrapper(ets_value)) { - // The EtsObjectWrapper already exists for the EtsObject, - // so we have to use it to get the associated napi_value. - EtsObjectWrapper *ets_object_wrapper = storage->GetEtsObjectWrapper(ets_value); - ASSERT(ets_object_wrapper != nullptr); - return ets_object_wrapper->GetJsValue(env); - } - - // NOTE: - // We use napi_new_instance() to create the EtsObjectWrapper for the EtsObject - // and associated it with new napi_value - ObjectHeader *obj = ets_value->GetCoreType(); - mem::Reference *ref = ctx->Refstor()->Add(obj, mem::Reference::ObjectType::GLOBAL); - if (UNLIKELY(ref == nullptr)) { - InteropCtx::ThrowJSError(env, "Cannot create ets reference"); - return nullptr; - } - UniqueEtsObjectReference ets_ref(ref); - ctx->SetNewInstance(std::move(ets_ref)); - napi_value js_ctor = ets_class_wrapper->GetJsCtor(env); - - napi_value js_value; - NAPI_CHECK_FATAL(napi_new_instance(env, js_ctor, 0, nullptr, &js_value)); - ASSERT(ctx->GetNewInstance() == nullptr); - return js_value; - } - - static std::optional UnWrap(InteropCtx *ctx, napi_env env, EtsClassWrapper *ets_class_wrapper, - napi_value js_value) - { - ASSERT(ets_class_wrapper != nullptr); - - napi_valuetype js_type = GetValueType(env, js_value); - if (UNLIKELY(js_type == napi_null)) { - return nullptr; - } - - if (UNLIKELY(js_type != napi_object)) { - InteropCtx::ThrowJSError(env, "js value is not object"); - return {}; - } - - void *data; - if (UNLIKELY(napi_unwrap(env, js_value, &data) != napi_ok)) { - InteropCtx::ThrowJSError(env, "Cannot get native data from js value"); - return {}; - } - - EtsObjectWrappersStorage *storage = ctx->GetEtsObjectWrappersStorage(); - EtsObjectWrapper *ets_object_wrapper = storage->GetEtsObjectWrapperOrNull(data); - if (UNLIKELY(ets_object_wrapper == nullptr)) { - InteropCtx::ThrowJSError(env, "Incorrect native data"); - return {}; - } - - EtsObject *ets_object = ets_object_wrapper->GetEtsObject(ctx); - if (UNLIKELY(ets_object->GetClass() != ets_class_wrapper->GetEtsClass())) { - const char *object_descriptor = ets_object->GetClass()->GetDescriptor(); - const char *expected_descriptor = ets_class_wrapper->GetEtsClass()->GetDescriptor(); - InteropCtx::ThrowJSError(env, "Incorrect object type. object_type: '" + std::string(object_descriptor) + - "', expected object_type: '" + expected_descriptor + "'"); - return {}; - } - return ets_object; - } -}; - -} // namespace panda::ets::interop::js::ets_proxy - -#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_JS_CONVERT_OBJECT_H_ diff --git a/plugins/ets/runtime/interop_js/ets_proxy/lazy_ets_class_wrapper_link.h b/plugins/ets/runtime/interop_js/ets_proxy/lazy_ets_class_wrapper_link.h deleted file mode 100644 index 7f161b38c..000000000 --- a/plugins/ets/runtime/interop_js/ets_proxy/lazy_ets_class_wrapper_link.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_LAZY_ETS_CLASS_WRAPPER_LINK_H_ -#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_LAZY_ETS_CLASS_WRAPPER_LINK_H_ - -#include "plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h" - -namespace panda::ets { -class EtsClass; -} // namespace panda::ets - -namespace panda::ets::interop::js::ets_proxy { -class EtsClassWrapper; - -using LazyEtsClassWrapperLink = TypedPointer; -} // namespace panda::ets::interop::js::ets_proxy - -#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_LAZY_ETS_CLASS_WRAPPER_LINK_H_ diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp b/plugins/ets/runtime/interop_js/ets_proxy/shared_reference.cpp similarity index 42% rename from plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp rename to plugins/ets/runtime/interop_js/ets_proxy/shared_reference.cpp index 5c5403e8b..286ad6384 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/shared_reference.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h" #include "plugins/ets/runtime/ets_coroutine.h" #include "plugins/ets/runtime/ets_handle.h" @@ -27,50 +27,29 @@ namespace panda::ets::interop::js::ets_proxy { -/*static*/ -void EtsObjectWrapper::CreateInplace(EtsObjectWrapper *mem_this, napi_env env, napi_value js_object, - UniqueEtsObjectReference ets_ref, EtsClassWrapper *ets_class_wrapper) +void SharedReference::Initialize(InteropCtx *ctx, napi_value js_object, EtsObject *ets_object) { - mem_this->ets_ref_ = std::move(ets_ref); - - // can't create weakref with napi_wrap in ace napi - NAPI_CHECK_FATAL(napi_wrap(env, js_object, mem_this, FinalizeCB, nullptr, nullptr)); - NAPI_CHECK_FATAL(napi_create_reference(env, js_object, 0, &mem_this->js_ref_)); - - if (ets_class_wrapper != nullptr) { - napi_value js_ctor = ets_class_wrapper->GetJsCtor(env); - napi_value proto; - NAPI_CHECK_FATAL(napi_get_prototype(env, js_object, &proto)); + auto env = ctx->GetJSEnv(); - napi_value proto_ctor; - NAPI_CHECK_FATAL(napi_get_named_property(env, proto, "constructor", &proto_ctor)); + // TODO(vpukhov): can't create weakref with napi_wrap in ace napi + NAPI_CHECK_FATAL(napi_wrap(env, js_object, this, FinalizeCB, nullptr, nullptr)); + NAPI_CHECK_FATAL(napi_create_reference(env, js_object, 0, &js_ref_)); - bool eq = false; - NAPI_CHECK_FATAL(napi_strict_equals(env, proto_ctor, js_ctor, &eq)); - if (eq) { - NAPI_CHECK_FATAL(napi_object_seal(env, js_object)); // TODO(vpukhov): broken in ace napi - } - } + ets_ref_ = ctx->Refstor()->Add(ets_object->GetCoreType(), mem::Reference::ObjectType::GLOBAL); + ASSERT(ets_ref_ != nullptr); } /*static*/ -void EtsObjectWrapper::FinalizeCB([[maybe_unused]] napi_env env, void *data, [[maybe_unused]] void *hint) +void SharedReference::FinalizeCB([[maybe_unused]] napi_env env, void *data, [[maybe_unused]] void *hint) { - auto *mem_this = reinterpret_cast(data); EtsCoroutine *coro = EtsCoroutine::GetCurrent(); InteropCtx *ctx = InteropCtx::Current(coro); - { - ScopedManagedCodeThread managed_scope(coro); - ctx->Refstor()->Remove(mem_this->ets_ref_.release()); - } - ctx->GetEtsObjectWrappersStorage()->RemoveEtsObjectWrapper(mem_this); -} + auto ref = reinterpret_cast(data); + ASSERT(ref->ets_ref_ != nullptr); -EtsObject *EtsObjectWrapper::GetEtsObject(InteropCtx *ctx) const -{ - ASSERT_MANAGED_CODE(); - return EtsObject::FromCoreType(ctx->Refstor()->Get(ets_ref_.get())); + ctx->Refstor()->Remove(ref->ets_ref_); + ctx->GetProxyRefStorage()->RemoveReference(ref); } } // namespace panda::ets::interop::js::ets_proxy diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h b/plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h similarity index 62% rename from plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h rename to plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h index a4e25807b..f33664abf 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_OBJECT_WRAPPER_H_ -#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_OBJECT_WRAPPER_H_ +#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_H_ #include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h" #include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h" @@ -27,25 +27,23 @@ class Reference; namespace panda::ets::interop::js { class InteropCtx; +// Forward declarations to avoid cyclic deps. +inline mem::GlobalObjectStorage *RefstorFromInteropCtx(InteropCtx *ctx); } // namespace panda::ets::interop::js namespace panda::ets::interop::js::ets_proxy { -class EtsClassWrapper; - -class EtsObjectWrapper { +class SharedReference { public: - static void CreateInplace(EtsObjectWrapper *mem_this, napi_env env, napi_value js_object, - UniqueEtsObjectReference ets_ref, EtsClassWrapper *ets_class_wrapper); + void Initialize(InteropCtx *ctx, napi_value js_object, EtsObject *ets_object); - mem::Reference *GetEtsRef() + EtsObject *GetEtsObject(InteropCtx *ctx) const { - return ets_ref_.get(); + ASSERT_MANAGED_CODE(); + return EtsObject::FromCoreType(RefstorFromInteropCtx(ctx)->Get(ets_ref_)); } - EtsObject *GetEtsObject(InteropCtx *ctx) const; - - napi_value GetJsValue(napi_env env) const + napi_value GetJsObject(napi_env env) const { napi_value js_value; NAPI_CHECK_FATAL(napi_get_reference_value(env, js_ref_, &js_value)); @@ -55,12 +53,12 @@ public: private: static void FinalizeCB(napi_env env, void *data, void *hint); - UniqueEtsObjectReference ets_ref_ {}; - // NOTE: - // Don't move js_ref_ field, it are using in Validator::IsZeroedObject() - napi_ref js_ref_ {}; + mem::Reference *ets_ref_; + napi_ref js_ref_; }; +static_assert(std::is_trivial_v); + } // namespace panda::ets::interop::js::ets_proxy -#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_OBJECT_WRAPPER_H_ +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_H_ diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp b/plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.cpp similarity index 42% rename from plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp rename to plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.cpp index 5005c7b23..1c80f3fbc 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp +++ b/plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.cpp @@ -13,24 +13,24 @@ * limitations under the License. */ -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h" #include "plugins/ets/runtime/interop_js/interop_context.h" namespace panda::ets::interop::js::ets_proxy { -// The Validator class helps to check whether ets_object_wrapper is allocated +// The Validator class helps to check whether shared_ref is allocated class Validator { public: - static inline bool IsZeroedObject(EtsObjectWrapper *ets_object_wrapper) + static inline bool IsZeroedObject(SharedReference *shared_ref) { - auto *validator = reinterpret_cast(ets_object_wrapper); + auto *validator = reinterpret_cast(shared_ref); return validator->v0_ == 0; } - static inline void ZeroingObject(EtsObjectWrapper *ets_object_wrapper) + static inline void ZeroObject(SharedReference *shared_ref) { - auto *validator = reinterpret_cast(ets_object_wrapper); + auto *validator = reinterpret_cast(shared_ref); validator->v0_ = 0; } @@ -38,23 +38,23 @@ private: uintptr_t free_list_space_ FIELD_UNUSED; // Skip a memory space because it can store the link to next free item uintptr_t v0_; }; -static_assert(sizeof(Validator) == sizeof(EtsObjectWrapper)); -static_assert(alignof(Validator) == alignof(EtsObjectWrapper)); +static_assert(sizeof(Validator) == sizeof(SharedReference)); +static_assert(alignof(Validator) == alignof(SharedReference)); /*static*/ -std::unique_ptr EtsObjectWrappersStorage::Create() +std::unique_ptr SharedReferenceStorage::Create() { - size_t real_size = EtsObjectWrappersPool::MAX_POOL_SIZE; + size_t real_size = SharedReferencePool::MAX_POOL_SIZE; void *data = os::mem::MapRWAnonymousRaw(real_size); if (data == nullptr) { INTEROP_LOG(FATAL) << "Cannot allocate MemPool"; return nullptr; } - return std::unique_ptr(new EtsObjectWrappersStorage(data, real_size)); + return std::unique_ptr(new SharedReferenceStorage(data, real_size)); } -EtsObjectWrapper *EtsObjectWrappersStorage::GetEtsObjectWrapper(EtsObject *ets_object) +SharedReference *SharedReferenceStorage::GetReference(EtsObject *ets_object) { ASSERT(ets_object->IsHashed() == true); @@ -62,40 +62,53 @@ EtsObjectWrapper *EtsObjectWrappersStorage::GetEtsObjectWrapper(EtsObject *ets_o return GetItemByIndex(idx); } -EtsObjectWrapper *EtsObjectWrappersStorage::GetEtsObjectWrapperOrNull(void *data) +SharedReference *SharedReferenceStorage::GetReference(napi_env env, napi_value js_object) { - auto *ets_object_wrapper = reinterpret_cast(data); - if (UNLIKELY(!IsValidItem(ets_object_wrapper))) { - // Incorrect data pointer + void *data; + if (UNLIKELY(napi_unwrap(env, js_object, &data) != napi_ok)) { return nullptr; } - if (UNLIKELY(Validator::IsZeroedObject(ets_object_wrapper))) { - // ets_object_wrapper won't be allocated + return GetReference(data); +} + +SharedReference *SharedReferenceStorage::GetReference(void *data) +{ + auto *shared_ref = reinterpret_cast(data); + if (UNLIKELY(!IsValidItem(shared_ref))) { + // We don't own that object return nullptr; } - return ets_object_wrapper; + ASSERT(!Validator::IsZeroedObject(shared_ref)); + return shared_ref; } -EtsObjectWrapper *EtsObjectWrappersStorage::CreateObjectEtsWrapper(EtsObject *ets_object) +SharedReference *SharedReferenceStorage::CreateReference(InteropCtx *ctx, EtsObject *ets_object, napi_value js_object) { ASSERT(ets_object->IsHashed() == false); - EtsObjectWrapper *mem = AllocItem(); - if (UNLIKELY(mem == nullptr)) { + SharedReference *shared_ref = AllocItem(); + if (UNLIKELY(shared_ref == nullptr)) { + ctx->ThrowJSError(ctx->GetJSEnv(), "no free space for shared ref"); return nullptr; } - uint32_t new_idx = GetIndexByItem(mem); - ets_object->SetHash(new_idx); - return mem; + ets_object->SetHash(GetIndexByItem(shared_ref)); + shared_ref->Initialize(ctx, js_object, ets_object); + return shared_ref; } -void EtsObjectWrappersStorage::RemoveEtsObjectWrapper(EtsObjectWrapper *ets_object_wrapper) +void SharedReferenceStorage::RemoveReference(SharedReference *shared_ref) { - ASSERT(!Validator::IsZeroedObject(ets_object_wrapper)); + ASSERT(!Validator::IsZeroedObject(shared_ref)); // Zeroing memory before free to correctly works IsZeroedObject() - Validator::ZeroingObject(ets_object_wrapper); - FreeItem(ets_object_wrapper); + Validator::ZeroObject(shared_ref); + FreeItem(shared_ref); +} + +bool SharedReferenceStorage::CheckAlive(void *data) +{ + auto *shared_ref = reinterpret_cast(data); + return IsValidItem(shared_ref) && !Validator::IsZeroedObject(shared_ref); } } // namespace panda::ets::interop::js::ets_proxy diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h b/plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h similarity index 40% rename from plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h rename to plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h index c8aa2c301..1cc2e72a2 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h @@ -13,41 +13,55 @@ * limitations under the License. */ -#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_ETS_OBJECT_WRAPPERS_STORAGE_H_ -#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_ETS_OBJECT_WRAPPERS_STORAGE_H_ +#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_STORAGE_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_STORAGE_H_ #include "libpandabase/macros.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference.h" #include "plugins/ets/runtime/interop_js/ets_proxy/mem/items_pool.h" #include "plugins/ets/runtime/types/ets_object.h" #include "runtime/mark_word.h" +namespace panda::ets::interop::js { +class InteropCtx; +} // namespace panda::ets::interop::js + namespace panda::ets::interop::js::ets_proxy { -using EtsObjectWrappersPool = ItemsPool; +namespace testing { +class SharedReferenceStorage1GTest; +} // namespace testing -class EtsObjectWrappersStorage : private EtsObjectWrappersPool { -public: - static std::unique_ptr Create(); - ~EtsObjectWrappersStorage() = default; +using SharedReferencePool = ItemsPool; - EtsObjectWrapper *GetEtsObjectWrapper(EtsObject *ets_object); +class SharedReferenceStorage : private SharedReferencePool { +public: + static std::unique_ptr Create(); + ~SharedReferenceStorage() = default; - inline bool HasEtsObjectWrapper(EtsObject *ets_object) + static bool HasReference(EtsObject *ets_object) { return ets_object->IsHashed(); } - EtsObjectWrapper *GetEtsObjectWrapperOrNull(void *data); - EtsObjectWrapper *CreateObjectEtsWrapper(EtsObject *ets_object); - void RemoveEtsObjectWrapper(EtsObjectWrapper *ets_object_wrapper); + SharedReference *GetReference(napi_env env, napi_value js_object); + SharedReference *GetReference(EtsObject *ets_object); + + SharedReference *CreateReference(InteropCtx *ctx, EtsObject *ets_object, napi_value js_object); private: - EtsObjectWrappersStorage(void *data, size_t size) : EtsObjectWrappersPool(data, size) {} - NO_COPY_SEMANTIC(EtsObjectWrappersStorage); - NO_MOVE_SEMANTIC(EtsObjectWrappersStorage); + SharedReferenceStorage(void *data, size_t size) : SharedReferencePool(data, size) {} + NO_COPY_SEMANTIC(SharedReferenceStorage); + NO_MOVE_SEMANTIC(SharedReferenceStorage); + + SharedReference *GetReference(void *data); + void RemoveReference(SharedReference *shared_ref); + + bool CheckAlive(void *data); + friend class SharedReference; + friend class testing::SharedReferenceStorage1GTest; }; } // namespace panda::ets::interop::js::ets_proxy -#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_ETS_OBJECT_WRAPPERS_STORAGE_H_ +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_SHARED_REFERENCE_STORAGE_H_ diff --git a/plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h b/plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h index cc774b89a..30aed28b0 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h @@ -27,13 +27,13 @@ struct TypedPointer { explicit TypedPointer(U *ptr) { static_assert(alignof(U) >= (1U << 1U)); - Set(ptr, true); + SetTagged(ptr, true); ASSERT(!IsResolved()); } explicit TypedPointer(R *ptr) { static_assert(alignof(R) >= (1U << 1U)); - Set(ptr, false); + SetTagged(ptr, false); ASSERT(IsResolved()); } @@ -54,6 +54,16 @@ struct TypedPointer { return reinterpret_cast(ptr_); } + template + void Set(T *ptr) + { + *this = TypedPointer(ptr); + } + + DEFAULT_COPY_SEMANTIC(TypedPointer); + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(TypedPointer); + ~TypedPointer() = default; + private: uintptr_t static constexpr MASK = ~static_cast(1); @@ -68,7 +78,7 @@ private: } template - void Set(T *ptr, bool tag) + void SetTagged(T *ptr, bool tag) { ptr_ = reinterpret_cast(ptr); ASSERT(!GetTag()); diff --git a/plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h b/plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h index c98aeeeab..1d9b5f7e6 100644 --- a/plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h +++ b/plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h @@ -47,10 +47,10 @@ public: return it->second.get(); } - void Remove(Key key) + std::unique_ptr Steal(Key key) { - ASSERT(Lookup(key) != nullptr); - cache_.erase(key); + auto nh = cache_.extract(key); + return nh ? std::move(nh.mapped()) : nullptr; } private: diff --git a/plugins/ets/runtime/interop_js/ets_type_visitor-inl.h b/plugins/ets/runtime/interop_js/ets_type_visitor-inl.h index 1522614a2..53526be98 100644 --- a/plugins/ets/runtime/interop_js/ets_type_visitor-inl.h +++ b/plugins/ets/runtime/interop_js/ets_type_visitor-inl.h @@ -61,6 +61,9 @@ namespace panda::ets::interop::js { TYPEVIS_CHECK_ERROR(!_e, std::move(_e)); \ } while (0) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TYPEVIS_NAPI_CHECK(expr) TYPEVIS_CHECK_ERROR((expr) == napi_ok, #expr) + class EtsTypeVisitor { public: // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) diff --git a/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp b/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp index f2a6af1cd..d6e0f54df 100644 --- a/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp +++ b/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp @@ -19,7 +19,7 @@ #include "plugins/ets/runtime/interop_js/interop_context.h" #include "plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h" #include "plugins/ets/runtime/interop_js/js_value_call.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/ts2ets_copy.h" #include "generated/base_options.h" @@ -111,13 +111,13 @@ static napi_value Call(napi_env env, napi_callback_info info) assert(status == napi_ok); auto coro = EtsCoroutine::GetCurrent(); - auto &argv = InteropCtx::Current(coro)->GetTempArgs(argc); + auto argv = InteropCtx::Current(coro)->GetTempArgs(argc); napi_value this_arg {}; void *data = nullptr; - status = napi_get_cb_info(env, info, &argc, argv.data(), &this_arg, &data); + status = napi_get_cb_info(env, info, &argc, argv->data(), &this_arg, &data); assert(status == napi_ok); - return CallEtsFunctionImpl(env, {argv.data(), argc}); + return CallEtsFunctionImpl(env, *argv); } static napi_value CallWithCopy(napi_env env, napi_callback_info info) @@ -128,13 +128,13 @@ static napi_value CallWithCopy(napi_env env, napi_callback_info info) assert(status == napi_ok); auto coro = EtsCoroutine::GetCurrent(); - auto &argv = InteropCtx::Current(coro)->GetTempArgs(argc); + auto argv = InteropCtx::Current(coro)->GetTempArgs(argc); napi_value this_arg {}; void *data = nullptr; - status = napi_get_cb_info(env, info, &argc, argv.data(), &this_arg, &data); + status = napi_get_cb_info(env, info, &argc, argv->data(), &this_arg, &data); assert(status == napi_ok); - return InvokeEtsMethodImpl(env, argv.data(), argc, false); + return InvokeEtsMethodImpl(env, argv->data(), argc, false); } static napi_value CreateEtsRuntime(napi_env env, napi_callback_info info) diff --git a/plugins/ets/runtime/interop_js/ts2ets_common.cpp b/plugins/ets/runtime/interop_js/interop_common.cpp similarity index 84% rename from plugins/ets/runtime/interop_js/ts2ets_common.cpp rename to plugins/ets/runtime/interop_js/interop_common.cpp index db2068637..136c72ddf 100644 --- a/plugins/ets/runtime/interop_js/ts2ets_common.cpp +++ b/plugins/ets/runtime/interop_js/interop_common.cpp @@ -14,22 +14,28 @@ */ #include "plugins/ets/runtime/interop_js/interop_context.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" namespace panda::ets::interop::js { -[[noreturn]] void NapiFatal(const char *message) +[[noreturn]] void InteropFatal(const char *message) { InteropCtx::Fatal(message); UNREACHABLE(); } -[[noreturn]] void NapiFatal(const std::string &message) +[[noreturn]] void InteropFatal(const std::string &message) { InteropCtx::Fatal(message.c_str()); UNREACHABLE(); } +[[noreturn]] void InteropFatal(const char *message, napi_status status) +{ + InteropCtx::Fatal(std::string(message) + " status=" + std::to_string(status)); + UNREACHABLE(); +} + void InteropTrace(const char *func, const char *file, int line) { INTEROP_LOG(DEBUG) << "trace: " << func << ":" << file << ":" << line; diff --git a/plugins/ets/runtime/interop_js/interop_common.h b/plugins/ets/runtime/interop_js/interop_common.h new file mode 100644 index 000000000..80520317b --- /dev/null +++ b/plugins/ets/runtime/interop_js/interop_common.h @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_COMMON_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_COMMON_H_ + +#include "runtime/include/thread_scopes.h" +#include "runtime/mem/refstorage/global_object_storage.h" +#include "plugins/ets/runtime/interop_js/logger.h" + +#include + +namespace panda::ets::interop::js { + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +[[noreturn]] void InteropFatal(const char *message); +[[noreturn]] void InteropFatal(const std::string &message); +[[noreturn]] void InteropFatal(const char *message, napi_status status); + +// Alternative for ASSERT(!expr) with interop stacktraces, enabled in NDEBUG +#define INTEROP_FATAL_IF(expr) \ + do { \ + bool _expr = (expr); \ + if (UNLIKELY(_expr)) { \ + InteropFatal("INTEROP_FATAL: " #expr); \ + UNREACHABLE(); \ + } \ + } while (0) + +#ifndef NDEBUG +void InteropTrace(const char *func, const char *file, int line); +#define INTEROP_TRACE() \ + do { \ + InteropTrace(__func__, __FILE__, __LINE__); \ + } while (0) +#else +#define INTEROP_TRACE() +#endif + +#if !defined(NDEBUG) +#define NAPI_ASSERT_OK(expr) INTEROP_FATAL_IF(expr) +#else +#define NAPI_ASSERT_OK(expr) \ + do { \ + (expr); \ + } while (0) +#endif + +// Assertion for napi_* calls success, enabled in NDEBUG +#define NAPI_CHECK_FATAL(status) \ + do { \ + napi_status _status = (status); \ + if (UNLIKELY(_status != napi_ok)) { \ + InteropFatal("NAPI_CHECK_FATAL: " #status, _status); \ + UNREACHABLE(); \ + } \ + } while (0) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +class NapiScope { +public: + explicit NapiScope(napi_env env) : env_(env) + { + [[maybe_unused]] auto status = napi_open_handle_scope(env_, &scope_); + ASSERT(status == napi_ok); + } + + ~NapiScope() + { + [[maybe_unused]] auto status = napi_close_handle_scope(env_, scope_); + ASSERT(status == napi_ok); + } + + NO_COPY_SEMANTIC(NapiScope); + NO_MOVE_SEMANTIC(NapiScope); + +private: + napi_env env_ {}; + napi_handle_scope scope_ {}; +}; + +class NapiEscapableScope { +public: + explicit NapiEscapableScope(napi_env env) : env_(env) + { + [[maybe_unused]] auto status = napi_open_escapable_handle_scope(env_, &scope_); + ASSERT(status == napi_ok); + } + + void Escape(napi_value &val) + { + [[maybe_unused]] auto status = napi_escape_handle(env_, scope_, val, &val); + ASSERT(status == napi_ok); + } + + ~NapiEscapableScope() + { + [[maybe_unused]] auto status = napi_close_escapable_handle_scope(env_, scope_); + ASSERT(status == napi_ok); + } + + NO_COPY_SEMANTIC(NapiEscapableScope); + NO_MOVE_SEMANTIC(NapiEscapableScope); + +private: + napi_env env_ {}; + napi_escapable_handle_scope scope_ {}; +}; + +inline napi_valuetype GetValueType(napi_env env, napi_value val) +{ + napi_valuetype vtype; + NAPI_CHECK_FATAL(napi_typeof(env, val, &vtype)); + return vtype; +} + +inline napi_value GetUndefined(napi_env env) +{ + napi_value js_value_undefined {}; + NAPI_CHECK_FATAL(napi_get_undefined(env, &js_value_undefined)); + return js_value_undefined; +} + +inline napi_value GetNull(napi_env env) +{ + napi_value js_value_null {}; + NAPI_CHECK_FATAL(napi_get_null(env, &js_value_null)); + return js_value_null; +} + +inline bool IsUndefined(napi_env env, napi_value val) +{ + return GetValueType(env, val) == napi_undefined; +} + +inline bool IsNullOrUndefined(napi_env env, napi_value val) +{ + napi_valuetype vtype = GetValueType(env, val); + return vtype == napi_undefined || vtype == napi_null; +} + +inline std::string GetString(napi_env env, napi_value js_val) +{ + size_t length; + NAPI_CHECK_FATAL(napi_get_value_string_utf8(env, js_val, nullptr, 0, &length)); + std::string value; + value.resize(length); + // +1 for NULL terminated string!!! + NAPI_CHECK_FATAL(napi_get_value_string_utf8(env, js_val, value.data(), value.size() + 1, &length)); + return value; +} + +inline bool NapiIsExceptionPending(napi_env env) +{ + bool pending; + NAPI_CHECK_FATAL(napi_is_exception_pending(env, &pending)); + return pending; +} + +inline bool NapiThrownGeneric(napi_status rc) +{ + INTEROP_FATAL_IF(rc != napi_ok && rc != napi_generic_failure); + return rc == napi_generic_failure; +} + +} // namespace panda::ets::interop::js + +#endif // PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_COMMON_H_ diff --git a/plugins/ets/runtime/interop_js/interop_context.cpp b/plugins/ets/runtime/interop_js/interop_context.cpp index e61647ed4..cc0092731 100644 --- a/plugins/ets/runtime/interop_js/interop_context.cpp +++ b/plugins/ets/runtime/interop_js/interop_context.cpp @@ -19,7 +19,7 @@ #include "plugins/ets/runtime/ets_class_linker_extension.h" #include "plugins/ets/runtime/ets_vm.h" #include "plugins/ets/runtime/interop_js/js_convert.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/types/ets_method.h" #include "runtime/include/runtime.h" #include "runtime/mem/local_object_handle.h" @@ -186,17 +186,21 @@ void JSConvertTypeCheckFailed(const char *type_name) static std::optional NapiTryGetStack(napi_env env) { - napi_value js_err; - if (NapiIsExceptionPending(env)) { - napi_value pending; - if (napi_ok != napi_get_and_clear_last_exception(env, &pending)) { + bool is_pending; + if (napi_ok != napi_is_exception_pending(env, &is_pending)) { + return {}; + } + if (is_pending) { + napi_value value_pending; + if (napi_ok != napi_get_and_clear_last_exception(env, &value_pending)) { return {}; } } napi_value js_dummy_str; - if (napi_ok != napi_create_string_utf8(env, "probe-stacktrace", NAPI_AUTO_LENGTH, &js_dummy_str)) { + if (napi_ok != napi_create_string_utf8(env, "probe-stacktrace-not-actual-error", NAPI_AUTO_LENGTH, &js_dummy_str)) { return {}; } + napi_value js_err; auto rc = napi_create_error(env, nullptr, js_dummy_str, &js_err); if (napi_ok != rc) { return {}; @@ -239,16 +243,19 @@ static std::optional NapiTryGetStack(napi_env env) INTEROP_LOG(ERROR) << method->GetClass()->GetName() << "." << method->GetName().data << " at " << method->GetLineNumberAndSourceFile(stack.GetBytecodePc()); } + auto env = ctx->js_env_; + INTEROP_LOG(ERROR) << (env != nullptr ? "" : "current js_env is nullptr!"); INTEROP_LOG(ERROR) << "====================== ETS stack end =========================="; - auto env = ctx->GetJSEnv(); - std::optional js_stk = NapiTryGetStack(env); - if (js_stk.has_value()) { - INTEROP_LOG(ERROR) << "====================== JS stack begin ========================="; - INTEROP_LOG(ERROR) << js_stk.value(); - INTEROP_LOG(ERROR) << "====================== JS stack end ==========================="; - } else { - INTEROP_LOG(ERROR) << "JS stack print failed"; + if (env != nullptr) { + std::optional js_stk = NapiTryGetStack(env); + if (js_stk.has_value()) { + INTEROP_LOG(ERROR) << "====================== JS stack begin ========================="; + INTEROP_LOG(ERROR) << js_stk.value(); + INTEROP_LOG(ERROR) << "====================== JS stack end ==========================="; + } else { + INTEROP_LOG(ERROR) << "JS stack print failed"; + } } PrintStack(Logger::Message(Logger::Level::ERROR, Logger::Component::ETS_INTEROP_JS, false).GetStream()); diff --git a/plugins/ets/runtime/interop_js/interop_context.h b/plugins/ets/runtime/interop_js/interop_context.h index 4e436b498..3d3ff494b 100644 --- a/plugins/ets/runtime/interop_js/interop_context.h +++ b/plugins/ets/runtime/interop_js/interop_context.h @@ -22,7 +22,7 @@ #include "plugins/ets/runtime/interop_js/js_refconvert.h" #include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h" #include "plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h" #include "plugins/ets/runtime/interop_js/js_job_queue.h" #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h" #include "plugins/ets/runtime/interop_js/intrinsics/std_js_jsruntime.h" @@ -96,8 +96,8 @@ public: { // Initialize InteropCtx in VM ExternalData auto *ctx = new (InteropCtx::Current(coro)) InteropCtx(coro); - ctx->ets_object_wrappers_storage_ = ets_proxy::EtsObjectWrappersStorage::Create(); - ASSERT(ctx->ets_object_wrappers_storage_.get() != nullptr); + ctx->ets_proxy_ref_storage_ = ets_proxy::SharedReferenceStorage::Create(); + ASSERT(ctx->ets_proxy_ref_storage_.get() != nullptr); } static InteropCtx *Current(PandaEtsVM *ets_vm) @@ -124,6 +124,7 @@ public: napi_env GetJSEnv() const { + ASSERT(js_env_ != nullptr); return js_env_; } @@ -157,15 +158,49 @@ public: return jsvalue_fqueue_register_; } - template - std::vector &GetTempArgs(size_t sz) - { - auto &args = std::get>(tmp_args_); - if (UNLIKELY(args.capacity() < sz)) { - args.reserve(sz); - } - args.resize(sz); - return args; + template + ALWAYS_INLINE static auto GetTempArgs(size_t sz) + { + struct TempArgs { + public: + explicit TempArgs(size_t sz) + { + if (LIKELY(sz <= OPT_SZ)) { + sp_ = {new (inl_arr_.data()) T[sz], sz}; + } else { + sp_ = {new T[sz], sz}; + } + } + ~TempArgs() + { + if (UNLIKELY(static_cast(sp_.data()) != inl_arr_.data())) { + delete[] sp_.data(); + } else { + static_assert(std::is_trivially_destructible_v); + } + } + NO_COPY_SEMANTIC(TempArgs); + NO_MOVE_SEMANTIC(TempArgs); + + Span &operator*() + { + return sp_; + } + Span *operator->() + { + return &sp_; + } + T &operator[](size_t idx) + { + return sp_[idx]; + } + + private: + Span sp_; + alignas(T) std::array inl_arr_; + }; + + return TempArgs(sz); } std::vector &GetInteropFrames() @@ -229,14 +264,14 @@ public: void ForwardEtsException(EtsCoroutine *coro); void ForwardJSException(EtsCoroutine *coro); - static bool SanityETSExceptionPending() + [[nodiscard]] static bool SanityETSExceptionPending() { auto coro = EtsCoroutine::GetCurrent(); auto env = InteropCtx::Current(coro)->GetJSEnv(); return coro->HasPendingException() && !NapiIsExceptionPending(env); } - static bool SanityJSExceptionPending() + [[nodiscard]] static bool SanityJSExceptionPending() { auto coro = EtsCoroutine::GetCurrent(); auto env = InteropCtx::Current(coro)->GetJSEnv(); @@ -250,14 +285,16 @@ public: Fatal(msg.c_str()); } - void SetNewInstance(ets_proxy::UniqueEtsObjectReference new_instance) + void SetPendingNewInstance(EtsObject *new_instance) { - new_instance_ = std::move(new_instance); + pending_new_instance_ = new_instance; } - ets_proxy::UniqueEtsObjectReference GetNewInstance() + EtsObject *AcquirePendingNewInstance() { - return std::move(new_instance_); + auto res = pending_new_instance_; + pending_new_instance_ = nullptr; + return res; } ets_proxy::EtsMethodWrappersCache *GetEtsMethodWrappersCache() @@ -270,9 +307,9 @@ public: return &ets_class_wrappers_cache_; } - ets_proxy::EtsObjectWrappersStorage *GetEtsObjectWrappersStorage() + ets_proxy::SharedReferenceStorage *GetProxyRefStorage() { - return ets_object_wrappers_storage_.get(); + return ets_proxy_ref_storage_.get(); } private: @@ -285,9 +322,6 @@ private: JSValueStringStorage js_value_string_stor_ {}; mem::Reference *jsvalue_fqueue_ref_ {}; - // Temporary storages for arg convertors, std::tuple...> - std::tuple, std::vector, std::vector> tmp_args_; - std::vector interop_frames_ {}; JSRefConvertCache refconvert_cache_; @@ -304,10 +338,10 @@ private: Method *jsvalue_fqueue_register_ {}; // ets_proxy data - ets_proxy::UniqueEtsObjectReference new_instance_ {}; + EtsObject *pending_new_instance_ {}; ets_proxy::EtsMethodWrappersCache ets_method_wrappers_cache_ {}; ets_proxy::EtsClassWrappersCache ets_class_wrappers_cache_ {}; - std::unique_ptr ets_object_wrappers_storage_ {}; + std::unique_ptr ets_proxy_ref_storage_ {}; friend class EtsJSNapiEnvScope; }; @@ -320,6 +354,10 @@ inline napi_env JSEnvFromInteropCtx(InteropCtx *ctx) { return ctx->GetJSEnv(); } +inline mem::GlobalObjectStorage *RefstorFromInteropCtx(InteropCtx *ctx) +{ + return ctx->Refstor(); +} } // namespace panda::ets::interop::js diff --git a/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp b/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp index faabb6824..0930497e7 100644 --- a/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp +++ b/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp @@ -15,7 +15,7 @@ #include "plugins/ets/runtime/interop_js/js_value_call.h" #include "plugins/ets/runtime/interop_js/js_convert.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/intrinsics_api.h" #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h" #include "plugins/ets/runtime/interop_js/napi_env_scope.h" @@ -36,7 +36,7 @@ static JSValue *JSRuntimeNewJSValueString(EtsString *v) auto ctx = InteropCtx::Current(coro); std::string str; if (v->IsUtf16()) { - NapiFatal("not implemented"); + InteropCtx::Fatal("not implemented"); } else { str = std::string(utf::Mutf8AsCString(v->GetDataMUtf8())); } diff --git a/plugins/ets/runtime/interop_js/js_convert.h b/plugins/ets/runtime/interop_js/js_convert.h index b793440d7..5601a6634 100644 --- a/plugins/ets/runtime/interop_js/js_convert.h +++ b/plugins/ets/runtime/interop_js/js_convert.h @@ -17,7 +17,7 @@ #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_CONVERT_H_ #include "plugins/ets/runtime/ets_panda_file_items.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/js_value.h" #include "runtime/handle_scope-inl.h" #include "runtime/include/coretypes/class.h" @@ -60,7 +60,7 @@ struct JSConvertBase { static void TypeCheckFailed() { - JSConvertTypeCheckFailed(Impl::type_name); + JSConvertTypeCheckFailed(Impl::TYPE_NAME); } // Convert ets->js, returns nullptr if failed, throws JS exceptions @@ -113,7 +113,7 @@ struct JSConvertBase { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define JSCONVERT_DEFINE_TYPE(type, cpptype_) \ struct JSConvert##type : public JSConvertBase { \ - static constexpr const char *type_name = #type; \ + static constexpr const char *TYPE_NAME = #type; \ /* Must not fail */ \ [[maybe_unused]] static inline napi_value WrapImpl([[maybe_unused]] napi_env env, \ [[maybe_unused]] cpptype ets_val); \ @@ -132,93 +132,97 @@ struct JSConvertBase { inline std::optional JSConvert##type::UnwrapImpl( \ [[maybe_unused]] InteropCtx *ctx, [[maybe_unused]] napi_env env, [[maybe_unused]] napi_value js_val) -JSCONVERT_DEFINE_TYPE(U1, bool) -JSCONVERT_WRAP(U1) -{ - napi_value js_val; - NAPI_CHECK_FATAL(napi_get_boolean(env, static_cast(ets_val), &js_val)); - return js_val; -} -JSCONVERT_UNWRAP(U1) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_boolean)) { - TypeCheckFailed(); - return {}; +template +struct JSConvertNumeric : public JSConvertBase, Cpptype> { + static constexpr const char *TYPE_NAME = "number"; + + template + static std::enable_if_t, napi_value> WrapImpl(napi_env env, Cpptype ets_val) + { + napi_value js_val; + if constexpr (sizeof(Cpptype) >= sizeof(int32_t)) { + NAPI_CHECK_FATAL(napi_create_int64(env, ets_val, &js_val)); + } else if constexpr (std::is_signed_v) { + NAPI_CHECK_FATAL(napi_create_int32(env, ets_val, &js_val)); + } else { + NAPI_CHECK_FATAL(napi_create_uint32(env, ets_val, &js_val)); + } + return js_val; } - bool val; - NAPI_CHECK_FATAL(napi_get_value_bool(env, js_val, &val)); - return val; -} -JSCONVERT_DEFINE_TYPE(I32, int32_t) -JSCONVERT_WRAP(I32) -{ - napi_value js_val; - NAPI_CHECK_FATAL(napi_create_int32(env, ets_val, &js_val)); - return js_val; -} -JSCONVERT_UNWRAP(I32) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { - TypeCheckFailed(); - return {}; + template + static std::enable_if_t, std::optional> UnwrapImpl([[maybe_unused]] InteropCtx *ctx, + napi_env env, napi_value js_val) + { + if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { + JSConvertNumeric::TypeCheckFailed(); + return {}; + } + Cpptype ets_val; + if constexpr (sizeof(Cpptype) >= sizeof(int32_t)) { + int64_t val; + NAPI_CHECK_FATAL(napi_get_value_int64(env, js_val, &val)); + ets_val = val; + } else if constexpr (std::is_signed_v) { + int32_t val; + NAPI_CHECK_FATAL(napi_get_value_int32(env, js_val, &val)); + ets_val = val; + } else { + uint32_t val; + NAPI_CHECK_FATAL(napi_get_value_uint32(env, js_val, &val)); + ets_val = val; + } + return ets_val; } - int32_t val; - NAPI_CHECK_FATAL(napi_get_value_int32(env, js_val, &val)); - return val; -} -JSCONVERT_DEFINE_TYPE(F32, float) -JSCONVERT_WRAP(F32) -{ - napi_value js_val; - NAPI_CHECK_FATAL(napi_create_double(env, ets_val, &js_val)); - return js_val; -} -JSCONVERT_UNWRAP(F32) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { - TypeCheckFailed(); - return {}; + template + static std::enable_if_t, napi_value> WrapImpl(napi_env env, Cpptype ets_val) + { + napi_value js_val; + NAPI_CHECK_FATAL(napi_create_double(env, ets_val, &js_val)); + return js_val; } - double val; - NAPI_CHECK_FATAL(napi_get_value_double(env, js_val, &val)); - return static_cast(val); -} -JSCONVERT_DEFINE_TYPE(F64, double) -JSCONVERT_WRAP(F64) -{ - napi_value js_val; - NAPI_CHECK_FATAL(napi_create_double(env, ets_val, &js_val)); - return js_val; -} -JSCONVERT_UNWRAP(F64) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { - TypeCheckFailed(); - return {}; + template + static std::enable_if_t, std::optional> UnwrapImpl( + [[maybe_unused]] InteropCtx *ctx, napi_env env, napi_value js_val) + { + if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { + JSConvertNumeric::TypeCheckFailed(); + return {}; + } + double val; + NAPI_CHECK_FATAL(napi_get_value_double(env, js_val, &val)); + return val; } - double val; - NAPI_CHECK_FATAL(napi_get_value_double(env, js_val, &val)); - return val; -} +}; + +using JSConvertI8 = JSConvertNumeric; +using JSConvertU8 = JSConvertNumeric; +using JSConvertI16 = JSConvertNumeric; +using JSConvertU16 = JSConvertNumeric; +using JSConvertI32 = JSConvertNumeric; +using JSConvertU32 = JSConvertNumeric; +using JSConvertI64 = JSConvertNumeric; +using JSConvertU64 = JSConvertNumeric; +using JSConvertF32 = JSConvertNumeric; +using JSConvertF64 = JSConvertNumeric; -JSCONVERT_DEFINE_TYPE(I64, int64_t) -JSCONVERT_WRAP(I64) +JSCONVERT_DEFINE_TYPE(U1, bool) +JSCONVERT_WRAP(U1) { napi_value js_val; - NAPI_CHECK_FATAL(napi_create_int64(env, ets_val, &js_val)); + NAPI_CHECK_FATAL(napi_get_boolean(env, static_cast(ets_val), &js_val)); return js_val; } -JSCONVERT_UNWRAP(I64) +JSCONVERT_UNWRAP(U1) { - if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { + if (UNLIKELY(GetValueType(env, js_val) != napi_boolean)) { TypeCheckFailed(); return {}; } - int64_t val; - NAPI_CHECK_FATAL(napi_get_value_int64(env, js_val, &val)); + bool val; + NAPI_CHECK_FATAL(napi_get_value_bool(env, js_val, &val)); return val; } @@ -456,15 +460,11 @@ JSCONVERT_WRAP(Promise) if (ets_val->GetState() != EtsPromise::STATE_PENDING) { EtsObject *value = ets_val->GetValue(coro); napi_value completion_value; - auto ctx = InteropCtx::Current(); + auto ctx = InteropCtx::Current(coro); if (value == nullptr) { - napi_get_null(env, &completion_value); + completion_value = GetNull(env); } else { - EtsClass *value_class = value->GetClass(); - auto refconv = ctx->GetRefConvertCache()->Lookup(value_class->GetRuntimeClass()); - if (UNLIKELY(refconv == nullptr)) { - NapiFatal(std::string("ConvertNapiVal: unknown class: ") + value_class->GetDescriptor()); - } + auto refconv = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass()); completion_value = refconv->Wrap(ctx, value); } if (ets_val->IsResolved()) { @@ -480,7 +480,7 @@ JSCONVERT_WRAP(Promise) JSCONVERT_UNWRAP(Promise) { - UNREACHABLE(); + InteropCtx::Fatal("Promise unwrap not implemented"); } #undef JSCONVERT_DEFINE_TYPE diff --git a/plugins/ets/runtime/interop_js/js_job_queue.cpp b/plugins/ets/runtime/interop_js/js_job_queue.cpp index e2cb08fdd..8aa64b5f8 100644 --- a/plugins/ets/runtime/interop_js/js_job_queue.cpp +++ b/plugins/ets/runtime/interop_js/js_job_queue.cpp @@ -20,7 +20,7 @@ #include "plugins/ets/runtime/lambda_utils.h" #include "plugins/ets/runtime/types/ets_method.h" #include "plugins/ets/runtime/interop_js/js_job_queue.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/interop_context.h" namespace panda::ets::interop::js { @@ -60,7 +60,7 @@ void JsJobQueue::AddJob(EtsObject *callback) napi_get_undefined(env, &undefined); napi_status status = napi_create_promise(env, &deferred, &js_promise); if (status != napi_ok) { - NapiFatal("Cannot allocate a Promise instance"); + InteropCtx::Fatal("Cannot allocate a Promise instance"); } status = napi_get_named_property(env, js_promise, "then", &then_fn); ASSERT(status == napi_ok); @@ -73,7 +73,7 @@ void JsJobQueue::AddJob(EtsObject *callback) napi_value then_callback; status = napi_create_function(env, nullptr, 0, ThenCallback, callback_ref, &then_callback); if (status != napi_ok) { - NapiFatal("Cannot create a function"); + InteropCtx::Fatal("Cannot create a function"); } napi_value then_promise; diff --git a/plugins/ets/runtime/interop_js/js_refconvert.cpp b/plugins/ets/runtime/interop_js/js_refconvert.cpp index 30fdc2b1a..37e899aa1 100644 --- a/plugins/ets/runtime/interop_js/js_refconvert.cpp +++ b/plugins/ets/runtime/interop_js/js_refconvert.cpp @@ -15,7 +15,7 @@ #include "plugins/ets/runtime/ets_class_linker_extension.h" #include "plugins/ets/runtime/interop_js/interop_context.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/js_refconvert.h" #include "plugins/ets/runtime/interop_js/js_convert.h" #include "runtime/mem/local_object_handle.h" @@ -74,7 +74,7 @@ static std::unique_ptr JSRefConvertCreateImpl(InteropCtx *ctx, Cla return std::unique_ptr(new JSRefConvertReftypeArray(klass)); } - return ets_proxy::EtsClassWrapper::CreateRefConv(ctx->GetJSEnv(), klass); + return ets_proxy::EtsClassWrapper::CreateJSRefConvert(ctx, klass); } template @@ -106,14 +106,6 @@ void RegisterBuiltinJSRefConvertors(InteropCtx *ctx) RegisterBuiltinRefConvertor(cache, ctx->GetStringClass()); RegisterBuiltinRefConvertor(cache, ctx->GetPromiseClass()); - RegisterBuiltinArrayConvertor(cache, linker_ext); - RegisterBuiltinArrayConvertor(cache, linker_ext); - RegisterBuiltinArrayConvertor(cache, linker_ext); - RegisterBuiltinArrayConvertor(cache, linker_ext); - RegisterBuiltinArrayConvertor(cache, linker_ext); - RegisterBuiltinArrayConvertor(cache, linker_ext); - // TODO(vpukhov): jsvalue[] specialization, currently uses JSRefConvertArrayRef - RegisterBuiltinRefConvertor(cache, linker_ext->GetBoxBooleanClass()); RegisterBuiltinRefConvertor(cache, linker_ext->GetBoxByteClass()); RegisterBuiltinRefConvertor(cache, linker_ext->GetBoxCharClass()); @@ -122,6 +114,14 @@ void RegisterBuiltinJSRefConvertors(InteropCtx *ctx) RegisterBuiltinRefConvertor(cache, linker_ext->GetBoxLongClass()); RegisterBuiltinRefConvertor(cache, linker_ext->GetBoxFloatClass()); RegisterBuiltinRefConvertor(cache, linker_ext->GetBoxDoubleClass()); + + RegisterBuiltinArrayConvertor(cache, linker_ext); + RegisterBuiltinArrayConvertor(cache, linker_ext); + RegisterBuiltinArrayConvertor(cache, linker_ext); + RegisterBuiltinArrayConvertor(cache, linker_ext); + RegisterBuiltinArrayConvertor(cache, linker_ext); + RegisterBuiltinArrayConvertor(cache, linker_ext); + // TODO(vpukhov): jsvalue[] specialization, currently uses JSRefConvertArrayRef } } // namespace panda::ets::interop::js diff --git a/plugins/ets/runtime/interop_js/js_refconvert.h b/plugins/ets/runtime/interop_js/js_refconvert.h index 3ddf28bab..d18046204 100644 --- a/plugins/ets/runtime/interop_js/js_refconvert.h +++ b/plugins/ets/runtime/interop_js/js_refconvert.h @@ -18,7 +18,7 @@ #include "plugins/ets/runtime/ets_coroutine.h" #include "plugins/ets/runtime/ets_vm.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "libpandabase/macros.h" #include #include @@ -54,6 +54,13 @@ public: NO_MOVE_SEMANTIC(JSRefConvert); virtual ~JSRefConvert() = default; + template >> + static D *Cast(JSRefConvert *base) + { + ASSERT(base->wrap_ == &D::WrapImpl && base->unwrap_ == &D::UnwrapImpl); + return static_cast(base); + } + protected: template explicit JSRefConvert(D * /*unused*/) @@ -136,6 +143,7 @@ template extern JSRefConvert *JSRefConvertCreate(InteropCtx *ctx, Class *klass); // Find or create JSRefConvert for some Class +// TODO(vpukhov): should never throw? template inline JSRefConvert *JSRefConvertResolve(InteropCtx *ctx, Class *klass) { diff --git a/plugins/ets/runtime/interop_js/js_refconvert_array.h b/plugins/ets/runtime/interop_js/js_refconvert_array.h index a2329d9ad..bf69db219 100644 --- a/plugins/ets/runtime/interop_js/js_refconvert_array.h +++ b/plugins/ets/runtime/interop_js/js_refconvert_array.h @@ -18,7 +18,7 @@ #include "plugins/ets/runtime/ets_class_linker_extension.h" #include "plugins/ets/runtime/interop_js/interop_context.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/js_refconvert.h" #include "plugins/ets/runtime/interop_js/js_convert.h" #include "runtime/mem/local_object_handle.h" @@ -154,6 +154,8 @@ public: if (UNLIKELY(elem_conv_ == nullptr)) { elem_conv_ = JSRefConvertResolve(ctx, klass_->GetComponentType()); } + ASSERT(klass_->GetComponentType() == + ets_elem->GetClass()->GetRuntimeClass()); // TODO(vpukhov): inheritance js_elem = elem_conv_->Wrap(ctx, ets_elem); if (UNLIKELY(js_elem == nullptr)) { return nullptr; @@ -220,7 +222,7 @@ private: static constexpr auto ELEM_SIZE = ClassHelper::OBJECT_POINTER_SIZE; Class *klass_ {}; - JSRefConvert *elem_conv_ {}; // TODO(vpukhov): Add tagged link and overload JSRefConvertResolve + JSRefConvert *elem_conv_ {}; }; } // namespace panda::ets::interop::js diff --git a/plugins/ets/runtime/interop_js/js_value.h b/plugins/ets/runtime/interop_js/js_value.h index f3132ab9c..7a57d4acc 100644 --- a/plugins/ets/runtime/interop_js/js_value.h +++ b/plugins/ets/runtime/interop_js/js_value.h @@ -17,7 +17,7 @@ #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JSVALUE_H_ #include "plugins/ets/runtime/ets_coroutine.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/interop_context.h" #include "plugins/ets/runtime/types/ets_object.h" #include "runtime/include/coretypes/class.h" diff --git a/plugins/ets/runtime/interop_js/js_value_call.cpp b/plugins/ets/runtime/interop_js/js_value_call.cpp index 4283047dd..98fd0319e 100644 --- a/plugins/ets/runtime/interop_js/js_value_call.cpp +++ b/plugins/ets/runtime/interop_js/js_value_call.cpp @@ -15,11 +15,10 @@ #include "plugins/ets/runtime/types/ets_string.h" #include "plugins/ets/runtime/types/ets_method.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h" #include "plugins/ets/runtime/interop_js/js_value_call.h" #include "plugins/ets/runtime/interop_js/napi_env_scope.h" #include "plugins/ets/runtime/interop_js/js_convert.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "runtime/include/panda_vm.h" #include "runtime/include/class_linker-inl.h" #include "runtime/handle_scope-inl.h" @@ -56,30 +55,28 @@ template // do nothing return true; } - case panda_file::Type::TypeId::U1: { + case panda_file::Type::TypeId::U1: return unwrap_val(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::I8: { - return unwrap_val(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::U16: { - return unwrap_val(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::I16: { + case panda_file::Type::TypeId::I8: + return unwrap_val(helpers::TypeIdentity()); + case panda_file::Type::TypeId::U8: + return unwrap_val(helpers::TypeIdentity()); + case panda_file::Type::TypeId::I16: + return unwrap_val(helpers::TypeIdentity()); + case panda_file::Type::TypeId::U16: + return unwrap_val(helpers::TypeIdentity()); + case panda_file::Type::TypeId::I32: return unwrap_val(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::I32: { - return unwrap_val(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::I64: { + case panda_file::Type::TypeId::U32: + return unwrap_val(helpers::TypeIdentity()); + case panda_file::Type::TypeId::I64: return unwrap_val(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::F32: { + case panda_file::Type::TypeId::U64: + return unwrap_val(helpers::TypeIdentity()); + case panda_file::Type::TypeId::F32: return unwrap_val(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::F64: { + case panda_file::Type::TypeId::F64: return unwrap_val(helpers::TypeIdentity()); - } case panda_file::Type::TypeId::REFERENCE: { if (UNLIKELY(IsNullOrUndefined(env, js_val))) { store_ref(nullptr); @@ -137,33 +134,31 @@ template switch (type.GetId()) { case panda_file::Type::TypeId::VOID: { - store_res(nullptr); + store_res(GetUndefined(env)); return true; } - case panda_file::Type::TypeId::U1: { + case panda_file::Type::TypeId::U1: return wrap_prim(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::I8: { - return wrap_prim(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::U16: { - return wrap_prim(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::I16: { - return wrap_prim(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::I32: { + case panda_file::Type::TypeId::I8: + return wrap_prim(helpers::TypeIdentity()); + case panda_file::Type::TypeId::U8: + return wrap_prim(helpers::TypeIdentity()); + case panda_file::Type::TypeId::I16: + return wrap_prim(helpers::TypeIdentity()); + case panda_file::Type::TypeId::U16: + return wrap_prim(helpers::TypeIdentity()); + case panda_file::Type::TypeId::I32: return wrap_prim(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::I64: { + case panda_file::Type::TypeId::U32: + return wrap_prim(helpers::TypeIdentity()); + case panda_file::Type::TypeId::I64: return wrap_prim(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::F32: { + case panda_file::Type::TypeId::U64: + return wrap_prim(helpers::TypeIdentity()); + case panda_file::Type::TypeId::F32: return wrap_prim(helpers::TypeIdentity()); - } - case panda_file::Type::TypeId::F64: { + case panda_file::Type::TypeId::F64: return wrap_prim(helpers::TypeIdentity()); - } case panda_file::Type::TypeId::REFERENCE: { ObjectHeader *ref = read_val(helpers::TypeIdentity()); if (UNLIKELY(ref == nullptr)) { @@ -181,9 +176,6 @@ template } // start slowpath auto refconv = JSRefConvertResolve(ctx, klass); - if (UNLIKELY(refconv == nullptr)) { - return false; - } auto res = refconv->Wrap(ctx, EtsObject::FromCoreType(ref)); store_res(res); return res != nullptr; @@ -226,7 +218,12 @@ napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span static constexpr size_t ETS_ARGS_DISP = IS_STATIC ? 0 : 1; auto const num_args = method->GetNumArgs() - ETS_ARGS_DISP; - auto &ets_args = ctx->GetTempArgs(method->GetNumArgs()); + if (UNLIKELY(num_args != jsargv.size())) { + InteropCtx::ThrowJSTypeError(ctx->GetJSEnv(), std::string("CallEtsFunction: wrong argc")); + return nullptr; + } + + auto ets_args = ctx->GetTempArgs(method->GetNumArgs()); { HandleScope ets_handle_scope(coro); @@ -238,7 +235,7 @@ napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span ASSERT(this_obj == nullptr); } - auto &ets_boxed_args = ctx->GetTempArgs(num_args); + auto ets_boxed_args = ctx->GetTempArgs(num_args); // Convert and box in VMHandle if necessary for (uint32_t arg_idx = 0; arg_idx < num_args; ++arg_idx, it.IncrementWithoutCheck()) { @@ -254,7 +251,7 @@ napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span if (coro->HasPendingException()) { ctx->ForwardEtsException(coro); } - ctx->SanityJSExceptionPending(); + ASSERT(ctx->SanityJSExceptionPending()); INTEROP_LOG(DEBUG) << "EtsCall: exit with pending exception"; return nullptr; } @@ -275,7 +272,7 @@ napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span } } - Value ets_res = method->Invoke(coro, ets_args.data()); + Value ets_res = method->Invoke(coro, ets_args->data()); if (UNLIKELY(coro->HasPendingException())) { ctx->ForwardEtsException(coro); @@ -290,7 +287,7 @@ napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span auto store_res = [&](napi_value res) { js_res = res; }; auto read_val = [&](auto type_tag) { return ets_res.GetAs(); }; if (UNLIKELY(!ConvertEtsVal(ctx, cls_resolver, store_res, type, read_val))) { - ctx->SanityJSExceptionPending(); + ASSERT(ctx->SanityJSExceptionPending()); return nullptr; } } @@ -341,16 +338,9 @@ napi_value CallEtsFunctionImpl(napi_env env, Span jsargv) InteropCtx::ThrowJSError(env, "CallEtsFunction: can't resolve method " + entrypoint); return nullptr; } - auto method = method_res.Value(); - - auto const num_args = method->GetNumArgs(); - if (UNLIKELY(num_args != jsargv.size() - 1)) { - InteropCtx::ThrowJSError(env, std::string("CallEtsFunction: wrong argc")); - return nullptr; - } ScopedManagedCodeThread managed_scope(coro); - auto js_ret = EtsCallImplStatic(coro, ctx, method, jsargv.SubSpan(1)); + auto js_ret = EtsCallImplStatic(coro, ctx, method_res.Value(), jsargv.SubSpan(1)); INTEROP_LOG(DEBUG) << "CallEtsFunction: exit"; return js_ret; } @@ -366,8 +356,8 @@ napi_value EtsLambdaProxyInvoke(napi_env env, napi_callback_info cbinfo) napi_value athis; void *data; NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, nullptr, &athis, &data)); - auto &js_args = ctx->GetTempArgs(argc); - NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, js_args.data(), &athis, &data)); + auto js_args = ctx->GetTempArgs(argc); + NAPI_CHECK_FATAL(napi_get_cb_info(env, cbinfo, &argc, js_args->data(), &athis, &data)); auto ets_ref = static_cast(data); ASSERT(ets_ref != nullptr); @@ -377,13 +367,7 @@ napi_value EtsLambdaProxyInvoke(napi_env env, napi_callback_info cbinfo) auto method = ets_this->GetClass()->GetMethod("invoke"); ASSERT(method != nullptr); - if (UNLIKELY(argc != method->GetNumArgs() - 1)) { - InteropCtx::ThrowJSError(env, std::string("EtsProxyInvoke: wrong argc")); - return nullptr; - } - - auto js_args_span = Span(js_args.data(), js_args.size()); - auto js_ret = EtsCallImplInstance(coro, ctx, method->GetPandaMethod(), js_args_span, ets_this); + auto js_ret = EtsCallImplInstance(coro, ctx, method->GetPandaMethod(), *js_args, ets_this); INTEROP_LOG(DEBUG) << "EtsProxyInvoke: exit"; return js_ret; } @@ -490,7 +474,7 @@ static ALWAYS_INLINE inline uint64_t JSValueJSCallImpl(Method *method, uint8_t * return 0; } - auto &jsargs = ctx->GetTempArgs(num_args); + auto jsargs = ctx->GetTempArgs(num_args); for (uint32_t arg_idx = 0; arg_idx < num_args; ++arg_idx, it.IncrementWithoutCheck()) { auto cls_resolver = [&]() { return resolve_ref_cls(ref_arg_idx++); }; @@ -509,9 +493,9 @@ static ALWAYS_INLINE inline uint64_t JSValueJSCallImpl(Method *method, uint8_t * ScopedNativeCodeThread native_scope(coro); if constexpr (IS_NEWCALL) { - js_status = napi_new_instance(env, js_fn, jsargs.size(), jsargs.data(), &js_ret); + js_status = napi_new_instance(env, js_fn, jsargs->size(), jsargs->data(), &js_ret); } else { - js_status = napi_call_function(env, js_this, js_fn, jsargs.size(), jsargs.data(), &js_ret); + js_status = napi_call_function(env, js_this, js_fn, jsargs->size(), jsargs->data(), &js_ret); } ctx->GetInteropFrames().pop_back(); @@ -542,7 +526,7 @@ static ALWAYS_INLINE inline uint64_t JSValueJSCallImpl(Method *method, uint8_t * if (NapiIsExceptionPending(env)) { ctx->ForwardJSException(coro); } - ctx->SanityETSExceptionPending(); + ASSERT(ctx->SanityETSExceptionPending()); INTEROP_LOG(DEBUG) << "JSValueJSCall: exit with pending exception"; return 0; } @@ -578,8 +562,10 @@ static void InitJSCallSignatures(coretypes::String *cls_str) auto ctx = InteropCtx::Current(coro); auto class_linker = Runtime::GetCurrent()->GetClassLinker(); - const char *class_descriptor = utf::Mutf8AsCString(cls_str->GetDataMUtf8()); - EtsClass *ets_class = coro->GetPandaVM()->GetClassLinker()->GetClass(class_descriptor); + std::string class_descriptor(utf::Mutf8AsCString(cls_str->GetDataMUtf8())); + std::replace(class_descriptor.begin(), class_descriptor.end(), '.', '/'); // #12798 + INTEROP_LOG(DEBUG) << "Intialize jscall signatures for " << class_descriptor; + EtsClass *ets_class = coro->GetPandaVM()->GetClassLinker()->GetClass(class_descriptor.c_str()); INTEROP_FATAL_IF(ets_class == nullptr); auto klass = ets_class->GetRuntimeClass(); diff --git a/plugins/ets/runtime/interop_js/logger.h b/plugins/ets/runtime/interop_js/logger.h new file mode 100644 index 000000000..bcea99060 --- /dev/null +++ b/plugins/ets/runtime/interop_js/logger.h @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_LOGGER_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_LOGGER_H_ + +#include + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define INTEROP_LOG(level) LOG(level, ETS_INTEROP_JS) + +#ifdef PANDA_TARGET_OHOS +#include + +#define INTEROP_LOG_DEBUG(msg) OH_LOG_Print(LOG_APP, LOG_DEBUG, 0xFF00, "ts2ets", "%s", msg) +#define INTEROP_LOG_DEBUG_A(msg, ...) OH_LOG_Print(LOG_APP, LOG_DEBUG, 0xFF00, "ts2ets", msg, __VA_ARGS__) +#define INTEROP_LOG_INFO(msg) OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ts2ets", "%s", msg) +#define INTEROP_LOG_INFO_A(msg, ...) OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ts2ets", msg, __VA_ARGS__) +#define INTEROP_LOG_ERROR(msg) OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "ts2ets", msg) +#define INTEROP_LOG_ERROR_A(msg, ...) OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "ts2ets", msg, __VA_ARGS__) + +#else + +inline std::string EtsLogMakeString(const char *fmt, ...) +{ + va_list ap; // NOLINT(cppcoreguidelines-pro-type-vararg) + va_list ap_copy; // NOLINT(cppcoreguidelines-pro-type-vararg) + va_start(ap, fmt); + va_copy(ap_copy, ap); + + int len = vsnprintf(nullptr, 0, fmt, ap); + if (len < 0) { + LOG(FATAL, ETS) << "interop_js: Cannot convert message to log buffer"; + UNREACHABLE(); + } + + std::string res; + res.resize(static_cast(len)); + vsnprintf(res.data(), len + 1, fmt, ap_copy); + + va_end(ap_copy); + va_end(ap); + return res; +} + +#define TS2ETS_LOGGER(level, ...) \ + do { \ + std::string msg_ts2ets_logger = EtsLogMakeString(__VA_ARGS__); \ + LOG(level, ETS) << "interop_js: " << msg_ts2ets_logger; \ + } while (0) + +#define INTEROP_LOG_DEBUG(msg) TS2ETS_LOGGER(DEBUG, msg) +#define INTEROP_LOG_DEBUG_A(msg, ...) TS2ETS_LOGGER(DEBUG, msg, __VA_ARGS__) +#define INTEROP_LOG_INFO(msg) TS2ETS_LOGGER(INFO, msg) +#define INTEROP_LOG_INFO_A(msg, ...) TS2ETS_LOGGER(INFO, msg, __VA_ARGS__) +#define INTEROP_LOG_ERROR(msg) TS2ETS_LOGGER(INFO, msg) +#define INTEROP_LOG_ERROR_A(msg, ...) TS2ETS_LOGGER(INFO, msg, __VA_ARGS__) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif // PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_LOGGER_H_ diff --git a/plugins/ets/runtime/interop_js/napi_env_scope.h b/plugins/ets/runtime/interop_js/napi_env_scope.h index 1d60e3447..799df850f 100644 --- a/plugins/ets/runtime/interop_js/napi_env_scope.h +++ b/plugins/ets/runtime/interop_js/napi_env_scope.h @@ -26,7 +26,7 @@ class EtsJSNapiEnvScope { public: explicit EtsJSNapiEnvScope(InteropCtx *ctx, napi_env new_env) : ctx_(ctx) { - saved_ = ctx_->GetJSEnv(); + saved_ = ctx_->js_env_; ctx_->SetJSEnv(new_env); } diff --git a/plugins/ets/runtime/interop_js/ts2ets_common.h b/plugins/ets/runtime/interop_js/ts2ets_common.h index 6d4b83fe8..18a8b61e2 100644 --- a/plugins/ets/runtime/interop_js/ts2ets_common.h +++ b/plugins/ets/runtime/interop_js/ts2ets_common.h @@ -13,240 +13,4 @@ * limitations under the License. */ -#ifndef PANDA_PLUGINS_ETS_RUNTIME_TS2ETS_TS2ETS_COMMON_H_ -#define PANDA_PLUGINS_ETS_RUNTIME_TS2ETS_TS2ETS_COMMON_H_ - -#include "runtime/include/thread_scopes.h" -#include "runtime/mem/refstorage/global_object_storage.h" - -#include - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_LOG(level) LOG(level, ETS_INTEROP_JS) - -#ifdef PANDA_TARGET_OHOS -#include - -#define INTEROP_LOG_DEBUG(msg) OH_LOG_Print(LOG_APP, LOG_DEBUG, 0xFF00, "ts2ets", "%s", msg) -#define INTEROP_LOG_DEBUG_A(msg, ...) OH_LOG_Print(LOG_APP, LOG_DEBUG, 0xFF00, "ts2ets", msg, __VA_ARGS__) -#define INTEROP_LOG_INFO(msg) OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ts2ets", "%s", msg) -#define INTEROP_LOG_INFO_A(msg, ...) OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ts2ets", msg, __VA_ARGS__) -#define INTEROP_LOG_ERROR(msg) OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "ts2ets", msg) -#define INTEROP_LOG_ERROR_A(msg, ...) OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "ts2ets", msg, __VA_ARGS__) - -#else - -inline std::string EtsLogMakeString(const char *fmt, ...) -{ - va_list ap; // NOLINT(cppcoreguidelines-pro-type-vararg) - va_list ap_copy; // NOLINT(cppcoreguidelines-pro-type-vararg) - va_start(ap, fmt); - va_copy(ap_copy, ap); - - int len = vsnprintf(nullptr, 0, fmt, ap); - if (len < 0) { - LOG(FATAL, ETS) << "interop_js: Cannot convert message to log buffer"; - UNREACHABLE(); - } - - std::string res; - res.resize(static_cast(len)); - vsnprintf(res.data(), len + 1, fmt, ap_copy); - - va_end(ap_copy); - va_end(ap); - return res; -} - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_LOGGER(level, ...) \ - do { \ - std::string msg_ts2ets_logger = EtsLogMakeString(__VA_ARGS__); \ - LOG(level, ETS) << "interop_js: " << msg_ts2ets_logger; \ - } while (0) - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_LOG_DEBUG(msg) TS2ETS_LOGGER(DEBUG, msg) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_LOG_DEBUG_A(msg, ...) TS2ETS_LOGGER(DEBUG, msg, __VA_ARGS__) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_LOG_INFO(msg) TS2ETS_LOGGER(INFO, msg) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_LOG_INFO_A(msg, ...) TS2ETS_LOGGER(INFO, msg, __VA_ARGS__) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_LOG_ERROR(msg) TS2ETS_LOGGER(INFO, msg) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_LOG_ERROR_A(msg, ...) TS2ETS_LOGGER(INFO, msg, __VA_ARGS__) -#endif - -namespace panda::ets::interop::js { - -[[noreturn]] void NapiFatal(const char *message); -[[noreturn]] void NapiFatal(const std::string &message); - -// Alternative for ASSERT(!expr) with interop stacktraces, enabled in NDEBUG -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_FATAL_IF(expr) \ - do { \ - bool _expr = (expr); \ - if (UNLIKELY(_expr)) { \ - NapiFatal("INTEROP_FATAL: " #expr); \ - UNREACHABLE(); \ - } \ - } while (0) - -#if !defined(NDEBUG) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define NAPI_ASSERT_OK(expr) INTEROP_FATAL_IF(expr) -#else -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define NAPI_ASSERT_OK(expr) \ - do { \ - (expr); \ - } while (0) -#endif - -// Assertion for napi_* calls success, enabled in NDEBUG -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define NAPI_CHECK_FATAL(status) INTEROP_FATAL_IF((status) != napi_ok) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TYPEVIS_NAPI_CHECK(expr) TYPEVIS_CHECK_ERROR((expr) == napi_ok, #expr) - -inline bool NapiThrownGeneric(napi_status rc) -{ - INTEROP_FATAL_IF(rc != napi_ok && rc != napi_generic_failure); - return rc == napi_generic_failure; -} - -void InteropTrace(const char *func, const char *file, int line); - -#ifndef NDEBUG -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_TRACE() \ - do { \ - InteropTrace(__func__, __FILE__, __LINE__); \ - } while (0) -#else -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define INTEROP_TRACE() -#endif - -class NapiScope { -public: - explicit NapiScope(napi_env env) : env_(env) - { - [[maybe_unused]] auto status = napi_open_handle_scope(env_, &scope_); - ASSERT(status == napi_ok); - } - - ~NapiScope() - { - [[maybe_unused]] auto status = napi_close_handle_scope(env_, scope_); - ASSERT(status == napi_ok); - } - - NO_COPY_SEMANTIC(NapiScope); - NO_MOVE_SEMANTIC(NapiScope); - -private: - napi_env env_ {}; - napi_handle_scope scope_ {}; -}; - -class NapiEscapableScope { -public: - explicit NapiEscapableScope(napi_env env) : env_(env) - { - [[maybe_unused]] auto status = napi_open_escapable_handle_scope(env_, &scope_); - ASSERT(status == napi_ok); - } - - void Escape(napi_value &val) - { - [[maybe_unused]] auto status = napi_escape_handle(env_, scope_, val, &val); - ASSERT(status == napi_ok); - } - - ~NapiEscapableScope() - { - [[maybe_unused]] auto status = napi_close_escapable_handle_scope(env_, scope_); - ASSERT(status == napi_ok); - } - - NO_COPY_SEMANTIC(NapiEscapableScope); - NO_MOVE_SEMANTIC(NapiEscapableScope); - -private: - napi_env env_ {}; - napi_escapable_handle_scope scope_ {}; -}; - -struct ScopedManagedCodeThreadSwitch { - explicit ScopedManagedCodeThreadSwitch(panda::ManagedThread *thread) : thread_(thread) - { - if (!thread_->IsManagedCode()) { - thread_->ManagedCodeBegin(); - switched_ = true; - } - } - ~ScopedManagedCodeThreadSwitch() - { - if (switched_) { - thread_->ManagedCodeEnd(); - } - } - - NO_COPY_SEMANTIC(ScopedManagedCodeThreadSwitch); - NO_MOVE_SEMANTIC(ScopedManagedCodeThreadSwitch); - -private: - panda::ManagedThread *thread_ = nullptr; - bool switched_ = false; -}; - -inline napi_valuetype GetValueType(napi_env env, napi_value val) -{ - napi_valuetype vtype; - NAPI_CHECK_FATAL(napi_typeof(env, val, &vtype)); - return vtype; -} - -inline napi_value GetNull(napi_env env) -{ - napi_value js_value_null {}; - NAPI_CHECK_FATAL(napi_get_null(env, &js_value_null)); - return js_value_null; -} - -inline bool IsUndefined(napi_env env, napi_value val) -{ - return GetValueType(env, val) == napi_undefined; -} - -inline bool IsNullOrUndefined(napi_env env, napi_value val) -{ - napi_valuetype vtype = GetValueType(env, val); - return vtype == napi_undefined || vtype == napi_null; -} - -inline std::string GetString(napi_env env, napi_value js_val) -{ - size_t length; - NAPI_CHECK_FATAL(napi_get_value_string_utf8(env, js_val, nullptr, 0, &length)); - std::string value; - value.resize(length); - // +1 for NULL terminated string!!! - NAPI_CHECK_FATAL(napi_get_value_string_utf8(env, js_val, value.data(), value.size() + 1, &length)); - return value; -} - -inline bool NapiIsExceptionPending(napi_env env) -{ - bool pending; - NAPI_CHECK_FATAL(napi_is_exception_pending(env, &pending)); - return pending; -} - -} // namespace panda::ets::interop::js - -#endif // !PANDA_PLUGINS_ETS_RUNTIME_TS2ETS_TS2ETS_COMMON_H_ +/* Compatibility header */ diff --git a/plugins/ets/runtime/interop_js/ts2ets_copy.cpp b/plugins/ets/runtime/interop_js/ts2ets_copy.cpp index a7ec60434..5a5bb4237 100644 --- a/plugins/ets/runtime/interop_js/ts2ets_copy.cpp +++ b/plugins/ets/runtime/interop_js/ts2ets_copy.cpp @@ -15,7 +15,7 @@ #include "plugins/ets/runtime/interop_js/js_value.h" #include "plugins/ets/runtime/interop_js/napi_env_scope.h" -#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/ets_type_visitor-inl.h" #include "plugins/ets/runtime/ets_vm.h" #include "plugins/ets/runtime/types/ets_promise.h" @@ -623,8 +623,7 @@ napi_value InvokeEtsMethodImpl(napi_env env, napi_value *jsargv, uint32_t jsargc auto coro = EtsCoroutine::GetCurrent(); [[maybe_unused]] EtsJSNapiEnvScope scope(InteropCtx::Current(coro), env); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - INTEROP_LOG_INFO("InvokeEtsMethod: enter"); + INTEROP_LOG(INFO) << "InvokeEtsMethod: enter"; if (jsargc < 1) { InteropCtx::ThrowJSError(env, "InvokeEtsMethod: method name required"); @@ -638,8 +637,7 @@ napi_value InvokeEtsMethodImpl(napi_env env, napi_value *jsargv, uint32_t jsargc } // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto method_name = std::string("ETSGLOBAL::") + GetString(env, jsargv[0]); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - INTEROP_LOG_INFO_A("InvokeEtsMethod: method name: %s", method_name.c_str()); + INTEROP_LOG(INFO) << "InvokeEtsMethod: method name: " << method_name.c_str(); auto method_res = panda::Runtime::GetCurrent()->ResolveEntryPoint(method_name); if (!method_res) { @@ -677,8 +675,7 @@ napi_value InvokeEtsMethodImpl(napi_env env, napi_value *jsargv, uint32_t jsargc args = js2ets.GetResult(); auto end = std::chrono::steady_clock::now(); int64_t t = std::chrono::duration_cast(end - begin).count(); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - INTEROP_LOG_INFO_A("InvokeEtsMethod: js2ets elapsed time %s us", std::to_string(t).c_str()); + INTEROP_LOG(INFO) << "InvokeEtsMethod: js2ets elapsed time: " << t << "us"; } panda::Value ets_res = method->Invoke(coro, args.data()); @@ -719,8 +716,7 @@ napi_value InvokeEtsMethodImpl(napi_env env, napi_value *jsargv, uint32_t jsargc js_res = ets2js.GetResult(); auto end = std::chrono::steady_clock::now(); int64_t t = std::chrono::duration_cast(end - begin).count(); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - INTEROP_LOG_INFO_A("InvokeEtsMethod: ets2js elapsed time %s us", std::to_string(t).c_str()); + INTEROP_LOG(INFO) << "InvokeEtsMethod: ets2js elapsed time: " << t << "us"; // Check that the method has a return value panda_file::Type ret_type = method->GetProto().GetReturnType(); diff --git a/plugins/ets/runtime/interop_js/ts2ets_tstype.cpp b/plugins/ets/runtime/interop_js/ts2ets_tstype.cpp index 1d1d6d2f3..ef1e535d2 100644 --- a/plugins/ets/runtime/interop_js/ts2ets_tstype.cpp +++ b/plugins/ets/runtime/interop_js/ts2ets_tstype.cpp @@ -18,17 +18,6 @@ #include "plugins/ets/runtime/interop_js/js_convert.h" #include "runtime/include/class_linker-inl.h" -#ifndef NDEBUG -// Debug log -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TSTYPE_DLOG(msg) INTEROP_LOG_INFO(msg) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TSTYPE_DLOG_A(msg, ...) INTEROP_LOG_INFO_A(msg, __VA_ARGS__) -#else -#define TSTYPE_DLOG(msg) -#define TSTYPE_DLOG_A(msg, ...) -#endif - namespace panda::ets::interop::js { template @@ -50,7 +39,7 @@ extern "C" void TSTypeCallCtorBridge(); extern "C" uint64_t TSTypeCallCtor([[maybe_unused]] Method *method, [[maybe_unused]] uint8_t *args, [[maybe_unused]] uint8_t *in_stack_args) { - NapiFatal("tstype ctor not implemented"); + InteropCtx::Fatal("tstype ctor not implemented"); return 0; } static void *GetTSTypeGetterBridge(Method *method); @@ -70,8 +59,7 @@ static napi_ref TSModuleRequire(napi_env env, const std::string &name) static napi_ref TSNapiModuleRequire(napi_env env, const std::string &name) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG_A("TSNapiModuleRequire: %s", name.c_str()); + INTEROP_LOG(INFO) << "TSNapiModuleRequire: " << name; napi_value js_glob; napi_value require_fn; NAPI_CHECK_FATAL(napi_get_global(env, &js_glob)); @@ -150,8 +138,7 @@ TSTypeNamespace *TSTypeNamespace::Create(panda::Class *klass, TSTypeNamespace *u void TSTypeNamespace::BindMethods() { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG_A("bind methods: %s", klass_->GetName().c_str()); + INTEROP_LOG(INFO) << "bind methods: " << klass_->GetName(); for (auto &method : klass_->GetMethods()) { void *ep = nullptr; if (method.IsInstanceConstructor()) { @@ -168,7 +155,7 @@ void TSTypeNamespace::BindMethods() if (has_prefix(TSTYPE_PREFIX_GETTER)) { ep = GetTSTypeGetterBridge(&method); } else if (has_prefix(TSTYPE_PREFIX_SETTER)) { - NapiFatal("support setter"); + InteropCtx::Fatal("support setter"); } else { ep = reinterpret_cast(TSTypeCallThisBridge); } @@ -182,20 +169,17 @@ napi_value TSTypeNamespace::ResolveValue(napi_env env) { napi_value cur_obj; NAPI_CHECK_FATAL(napi_get_reference_value(env, toplevel_, &cur_obj)); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG_A("get toplevel: %s", klass_->GetName().c_str()); + INTEROP_LOG(INFO) << "get toplevel: " << klass_->GetName(); INTEROP_FATAL_IF(IsUndefined(env, cur_obj)); for (auto const &e : resolv_) { napi_value prop; NAPI_CHECK_FATAL(napi_get_named_property(env, cur_obj, e, &prop)); if (IsUndefined(env, prop)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG_A("lookup namespace %s: failure", e); + INTEROP_LOG(INFO) << "lookup namespace failure: " << e; return nullptr; } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG_A("lookup namespace %s: success", e); + INTEROP_LOG(INFO) << "lookup namespace success: " << e; cur_obj = prop; } return cur_obj; @@ -211,8 +195,7 @@ static uint64_t TSTypeCCtor([[maybe_unused]] Method *method, coretypes::String * auto tklass = ctx->LinkerCtx()->FindClass(descriptor->GetDataMUtf8()); INTEROP_FATAL_IF(tklass == nullptr); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG_A("TSTypeCCtor: %s", tklass->GetName().c_str()); + INTEROP_LOG(INFO) << "TSTypeCCtor: " << tklass->GetName(); TSTypeNamespace *ns; if (enclosing_descriptor != nullptr) { @@ -234,8 +217,7 @@ static uint64_t TSTypeCCtor([[maybe_unused]] Method *method, coretypes::String * template ALWAYS_INLINE inline uint64_t TSTypeCall(Method *method, uint8_t *args, uint8_t *in_stack_args) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG("enter TSTypeCall"); + INTEROP_LOG(INFO) << "enter TSTypeCall"; auto coro = EtsCoroutine::GetCurrent(); auto ctx = InteropCtx::Current(coro); napi_env env = ctx->GetJSEnv(); @@ -265,7 +247,7 @@ ALWAYS_INLINE inline uint64_t TSTypeCall(Method *method, uint8_t *args, uint8_t auto const num_args = method->GetNumArgs() - (IS_STATIC ? 0 : 1); napi_value js_ret {}; - auto &jsargs = ctx->GetTempArgs(num_args); + auto jsargs = ctx->GetTempArgs(num_args); uint32_t ref_arg_idx = !method->GetReturnType().IsPrimitive(); panda_file::ShortyIterator it(method->GetShorty()); ++it; // retval @@ -307,11 +289,11 @@ ALWAYS_INLINE inline uint64_t TSTypeCall(Method *method, uint8_t *args, uint8_t js_val = JSConvertJSValue::Wrap(env, JSValue::FromEtsObject(EtsObject::FromCoreType(value))); break; } - NapiFatal("unsupported reftype"); + InteropCtx::Fatal("unsupported reftype"); break; } default: - NapiFatal("unsupported type"); + InteropCtx::Fatal("unsupported type"); } jsargs[arg_idx] = js_val; } @@ -319,11 +301,10 @@ ALWAYS_INLINE inline uint64_t TSTypeCall(Method *method, uint8_t *args, uint8_t { napi_value js_fn; auto method_name = utf::Mutf8AsCString(method->GetName().data); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG_A("get method %s", method_name); + INTEROP_LOG(INFO) << "get method " << method_name; NAPI_CHECK_FATAL(napi_get_named_property(env, js_this, method_name, &js_fn)); INTEROP_FATAL_IF(IsUndefined(env, js_fn)); - NAPI_CHECK_FATAL(napi_call_function(env, js_this, js_fn, jsargs.size(), jsargs.data(), &js_ret)); + NAPI_CHECK_FATAL(napi_call_function(env, js_this, js_fn, jsargs->size(), jsargs->data(), &js_ret)); } panda::Value ets_ret; @@ -336,7 +317,7 @@ ALWAYS_INLINE inline uint64_t TSTypeCall(Method *method, uint8_t *args, uint8_t case panda::panda_file::Type::TypeId::F64: { auto res = JSConvertF64::Unwrap(ctx, env, js_ret); if (UNLIKELY(!res.has_value())) { - NapiFatal("unwrap failed"); + InteropCtx::Fatal("unwrap failed"); } ets_ret = panda::Value(res.value()); break; @@ -346,7 +327,7 @@ ALWAYS_INLINE inline uint64_t TSTypeCall(Method *method, uint8_t *args, uint8_t if (klass->IsStringClass()) { auto res = JSConvertString::Unwrap(ctx, env, js_ret); if (UNLIKELY(!res.has_value())) { - NapiFatal("unwrap failed"); + InteropCtx::Fatal("unwrap failed"); } ets_ret = panda::Value(res.value()->GetCoreType()); break; @@ -359,15 +340,14 @@ ALWAYS_INLINE inline uint64_t TSTypeCall(Method *method, uint8_t *args, uint8_t ets_ret = panda::Value(value->GetCoreType()); break; } - NapiFatal("unsupported reftype"); + InteropCtx::Fatal("unsupported reftype"); break; } default: - NapiFatal("unsupported type"); + InteropCtx::Fatal("unsupported type"); } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - TSTYPE_DLOG("exit TSTypeCall"); + INTEROP_LOG(INFO) << "exit TSTypeCall"; return static_cast(ets_ret.GetAsLong()); } @@ -391,7 +371,7 @@ static void *GetTSTypeGetterBridge(Method *method) panda_file::Type type = method->GetReturnType(); switch (type.GetId()) { case panda::panda_file::Type::TypeId::VOID: { - NapiFatal("void getter"); + InteropCtx::Fatal("void getter"); } case panda::panda_file::Type::TypeId::F64: { return reinterpret_cast(TSTypeGetter); @@ -412,10 +392,10 @@ static void *GetTSTypeGetterBridge(Method *method) if (klass->IsStringClass()) { return reinterpret_cast(TSTypeGetter); } - NapiFatal("unsupported reftype"); + InteropCtx::Fatal("unsupported reftype"); } default: - NapiFatal("unsupported type"); + InteropCtx::Fatal("unsupported type"); } } diff --git a/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js b/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js index e7ffcdaec..112e970b8 100644 --- a/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js +++ b/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js @@ -13,14 +13,35 @@ * limitations under the License. */ -globalThis.ASSERT_EQ = function(v0, v1) { - if (v0 !== v1) { - let msg = `ASSERT FAILED: ${v0}[${typeof v0}] !== ${v1}[${typeof v1}]`; +globalThis.ASSERT_TRUE = function ASSERT_TRUE(v, msg = "") { + if (v !== true) { + msg = `ASSERTION FAILED: ` + msg; console.error(msg); throw Error(msg); } } +globalThis.ASSERT_EQ = function ASSERT_EQ(v0, v1) { + if (!Object.is(v0, v1)) { + let msg = `ASSERTION FAILED: ${v0}[${typeof v0}] !== ${v1}[${typeof v1}]`; + console.error(msg); + throw Error(msg); + } +} + +globalThis.ASSERT_THROWS = function ASSERT_THROWS(ctor, fn) { + ASSERT_TRUE(ctor != undefined && fn != undefined); + try { + fn(); + } catch (exc) { + if (exc instanceof ctor) { + return; + } + throw Error("ASSERT_THROWS: expected instance of " + ctor.name); + } + throw Error("ASSERT_THROWS: nothing was thrown"); +}; + function main() { // Add 'gtest' object to global space. // This object is used by gtests as storage to save and restore variables diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt index db58791ed..09459b451 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt @@ -11,6 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -add_subdirectory(call_ets_function) -add_subdirectory(use_ets_class) +add_subdirectory(function_proxy) +add_subdirectory(class_proxy) add_subdirectory(mem) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/call_ets_function.test.js b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/call_ets_function.test.js deleted file mode 100644 index 088b17057..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/call_ets_function.test.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const etsVm = require("lib/module/ets_interop_js_napi"); - -let sum_double = etsVm.getFunction("Lcall_ets_function/test/ETSGLOBAL;", "sum_double"); -let point_create = etsVm.getFunction("Lcall_ets_function/test/ETSGLOBAL;", "point_create"); -let point_get_x = etsVm.getFunction("Lcall_ets_function/test/ETSGLOBAL;", "point_get_x"); -let point_get_y = etsVm.getFunction("Lcall_ets_function/test/ETSGLOBAL;", "point_get_y"); - -module.exports = { - sum_double, - point_create, - point_get_x, - point_get_y, -} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/CMakeLists.txt similarity index 75% rename from plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/CMakeLists.txt rename to plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/CMakeLists.txt index 647f18a1f..505dc4ffc 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/CMakeLists.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -panda_ets_interop_js_gtest(ets_interop_js__test_use_ets_class - CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_use_ets_class.cpp - ETS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_use_ets_class.ets +panda_ets_interop_js_gtest(ets_interop_js__test_class_proxy + CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_class_proxy.cpp + ETS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_class_proxy.ets ) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_access_primitives.js b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_access_primitives.js new file mode 100644 index 000000000..0f5c37fef --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_access_primitives.js @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { getTestClass } = require("class_proxy.test.js"); +const PrimitivesAccess = getTestClass("PrimitivesAccess"); +const PrimitivesAccessStatic = getTestClass("PrimitivesAccessStatic"); + +let pa = new PrimitivesAccess(); +let pas = PrimitivesAccessStatic; + +let typelist = [ + "byte", "short", "int", "long", + "float", "double", + "char", "boolean" +]; + +{ // Test property accessors + function TestAccessors(tname, ...values) { + function TestAccessorsOf(o, tname, ...values) { + for (let v of values) { + o["f_" + tname] = v; + ASSERT_EQ(o["getf_" + tname](), v); + o["setf_" + tname](v); + ASSERT_EQ(o["f_" + tname], v) + } + } + TestAccessorsOf(pa, tname, ...values); + TestAccessorsOf(pas, tname, ...values); + } + + function IntBit(exp) { + ASSERT_TRUE(exp < 53, "IntBit overflow"); + return (1 << (exp % 30)) * (1 << (exp - exp % 30)); + } + function TestSInt(tname, bits) { + let msb = bits - 1; + TestAccessors(tname, 0, 1, -1, IntBit(msb) - 1, -IntBit(msb)); + } + function TestUInt(tname, bits) { + let msb = bits - 1; + TestAccessors(tname, 0, 1, IntBit(msb), IntBit(msb + 1) - 1); + } + + TestSInt("byte", 8); + TestSInt("short", 16); + TestSInt("int", 32); + TestSInt("long", 53); + TestAccessors("float", 0, 1, 1.25, 0x1234 / 256, Infinity, NaN); + TestAccessors("double", 0, 1, 1.33333, 0x123456789a / 256, Infinity, NaN); + TestUInt("char", 16); + TestAccessors("boolean", false, true); +} + +{ // Test typechecks + typelist.forEach(function (tname) { + function check(o) { + ASSERT_THROWS(TypeError, () => (o["f_" + tname] = undefined)); + } + check(pa); + check(pas); + }) +} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_access_references.js b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_access_references.js new file mode 100644 index 000000000..470bd322b --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_access_references.js @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { getTestClass } = require("class_proxy.test.js"); +const ReferencesAccess = getTestClass("ReferencesAccess"); +const ReferencesAccessStatic = getTestClass("ReferencesAccessStatic"); +const UClass1 = getTestClass("UClass1"); +const UClass2 = getTestClass("UClass2"); + +let ra = new ReferencesAccess(); +let ras = ReferencesAccessStatic; + +{ // Test property accessors + function TestAccessors(tname, ...values) { + function TestAccessorsOf(o, tname, ...values) { + for (let v of values) { + o["f_" + tname] = v; + ASSERT_EQ(o["getf_" + tname](), v); + o["setf_" + tname](v); + ASSERT_EQ(o["f_" + tname], v) + } + } + TestAccessorsOf(ra, tname, ...values); + TestAccessorsOf(ras, tname, ...values); + } + + TestAccessors("UClass1", null, new UClass1()); + TestAccessors("String", null, "fooo", "0123456789abcdef_"); + TestAccessors("JSValue", null, 1234, "fooo", {}, new UClass1()); + // TestAccessors("Promise", null, new Promise(function () { }, function () { })); // TODO(konstanting): enable + + { + let arr = ["a", "bb", "ccc"]; + ra.f_A_String = arr; + ASSERT_EQ(JSON.stringify(ra.f_A_String), JSON.stringify(arr)); + } + { + let arr = [new UClass1(), new UClass1()]; + ra.f_A_UClass1 = arr; + ASSERT_EQ(ra.f_A_UClass1[0], arr[0]); + ASSERT_EQ(ra.f_A_UClass1[1], arr[1]); + } +} + +{ // Test typechecks + function TestTypecheck(tname, ...values) { + function check(o, v) { + ASSERT_THROWS(TypeError, () => (o["f_" + tname] = v)); + } + for (let v of values) { + check(ra, v); + check(ras, v); + } + } + + TestTypecheck("UClass1", NaN, {}, new UClass2()); + TestTypecheck("String", NaN, {}, new UClass1()); + // TestTypecheck("Promise", NaN, {}, new UClass1()); // TODO(konstanting): enable + + TestTypecheck("A_String", NaN, {}, [1], ["1", 1], [{}], [new UClass2()]); + TestTypecheck("A_UClass1", NaN, {}, [1], ["1", 1], [{}], [new UClass2()]); +} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_proxy_objects.js b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_proxy_objects.js new file mode 100644 index 000000000..68fc9e321 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/check_proxy_objects.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { getTestClass } = require("class_proxy.test.js"); +const ListNode = getTestClass("ListNode"); + +{ // test proxy-ctor capabilities + ASSERT_TRUE(Object.isSealed(ListNode)); + + let expected = ['next', 'tag', 'get_next', 'set_next', 'constructor']; + let props = Object.getOwnPropertyNames(ListNode.prototype); + ASSERT_EQ(JSON.stringify(props.sort()), JSON.stringify(expected.sort())); + + ASSERT_TRUE(!ListNode.hasOwnProperty('')); +} + +{ // test basic proxy-object capabilities + let n1 = new ListNode(1, null); + ASSERT_EQ(n1.tag, 1); + ASSERT_EQ(n1.next, null); + + ASSERT_TRUE(Object.getOwnPropertyNames(n1).length == 0); + ASSERT_TRUE(Object.isSealed(n1)); +} + +{ // test proxy-reference wrap/unwrap invariant + let n1 = new ListNode(1, null); + let n2 = new ListNode(2, n1); + ASSERT_EQ(n2.tag, 2); + ASSERT_EQ(n2.next, n1); + ASSERT_EQ(n2.get_next(), n1); + n2.set_next(n1); + ASSERT_EQ(n2.next, n1); +} + +{ // create loop and walk it + let head = new ListNode(0, null); + head.next = + new ListNode(1, + new ListNode(2, + new ListNode(3, head))); + + for (let n = head, i = 0; i < 256; ++i, n = n.next) { + ASSERT_EQ(n.tag, i % 4); + } +} + +{ // proxy-object escape ctor + const ProxyEscapeCtor = getTestClass("ProxyEscapeCtor"); + let capt; + let obj = new ProxyEscapeCtor((x) => (capt = x)); + ASSERT_EQ(capt, obj); +} + +{ // proxy-method throws + const ProxyClassCalls = getTestClass("ProxyClassCalls"); + class TestErr extends Error { }; + let do_throw = function () { throw new TestErr() }; + + ASSERT_THROWS(TestErr, () => ( + new ProxyClassCalls(do_throw))); + + ASSERT_THROWS(TestErr, () => ( + ProxyClassCalls.call(do_throw))); +} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_primitives.js b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/class_proxy.test.js similarity index 75% rename from plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_primitives.js rename to plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/class_proxy.test.js index 6280bbcd1..6f39b2fce 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_primitives.js +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/class_proxy.test.js @@ -13,6 +13,10 @@ * limitations under the License. */ -const { sum_double } = require("call_ets_function.test.js") +const etsVm = require("lib/module/ets_interop_js_napi"); -ASSERT_EQ(sum_double(3, 4), 3 + 4); +function getTestClass(name) { return etsVm.getClass("Lclass_proxy/test/" + name + ";") } + +module.exports = { + getTestClass: getTestClass, +} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_inheritance.js b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/test_class_proxy.cpp similarity index 56% rename from plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_inheritance.js rename to plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/test_class_proxy.cpp index d4b1c7600..a4dcf3c37 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_inheritance.js +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/test_class_proxy.cpp @@ -13,30 +13,26 @@ * limitations under the License. */ -const { Point } = require("use_ets_class.test.js") +#include +#include "ets_interop_js_gtest.h" +namespace panda::ets::interop::js::testing { -class Point3D extends Point { - constructor(x, y, z) { - super(x, y) +class ClassProxyTest : public EtsInteropTest {}; - this.z = z; - } - get_z() { - return this.z; - } +TEST_F(ClassProxyTest, access_primitives) +{ + RunJsTestSute("check_access_primitives.js"); } -let point3d = new Point3D(6, 9, 7); - -ASSERT_EQ(point3d.x, 6); -ASSERT_EQ(point3d.y, 9); -ASSERT_EQ(point3d.z, 7); +TEST_F(ClassProxyTest, access_references) +{ + RunJsTestSute("check_access_references.js"); +} -point3d.x = 10; -point3d.y = 33; -point3d.z = 16; +TEST_F(ClassProxyTest, proxy_objects) +{ + RunJsTestSute("check_proxy_objects.js"); +} -ASSERT_EQ(point3d.get_x(), 10); -ASSERT_EQ(point3d.get_y(), 33); -ASSERT_EQ(point3d.get_z(), 16); +} // namespace panda::ets::interop::js::testing diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/test_class_proxy.ets b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/test_class_proxy.ets new file mode 100644 index 000000000..3c446cb88 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/class_proxy/test_class_proxy.ets @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package class_proxy.test; + +class jsvars { + static global = JSRuntime.getGlobal(); +}; + +class ListNode { + constructor(tag: double, next: ListNode) { + this.next = next; + this.tag = tag; + } + public get_next(): ListNode { + return this.next; + } + public set_next(v: ListNode): void { + this.next = v; + } + + public next: ListNode; + public tag: double; +}; + +class ProxyEscapeCtor { + constructor(cb: JSValue) { + cb(this); + } +}; + +class ProxyClassCalls { + constructor(cb: JSValue) { + cb(); + } + static call(cb: JSValue) { + cb(); + } +}; + +class PrimitivesAccess { + public getf_byte(): byte { return this.f_byte; } + public getf_short(): short { return this.f_short; } + public getf_int(): int { return this.f_int; } + public getf_long(): long { return this.f_long; } + public getf_float(): float { return this.f_float; } + public getf_double(): double { return this.f_double; } + public getf_char(): char { return this.f_char; } + public getf_boolean(): boolean { return this.f_boolean; } + + public setf_byte(v: byte): void { this.f_byte = v; } + public setf_short(v: short): void { this.f_short = v; } + public setf_int(v: int): void { this.f_int = v; } + public setf_long(v: long): void { this.f_long = v; } + public setf_float(v: float): void { this.f_float = v; } + public setf_double(v: double): void { this.f_double = v; } + public setf_char(v: char): void { this.f_char = v; } + public setf_boolean(v: boolean): void { this.f_boolean = v; } + + public f_byte: byte; + public f_short: short; + public f_int: int; + public f_long: long; + public f_float: float; + public f_double: double; + public f_char: char; + public f_boolean: boolean; +}; + +class PrimitivesAccessStatic { + public static getf_byte(): byte { return PrimitivesAccessStatic.f_byte; } + public static getf_short(): short { return PrimitivesAccessStatic.f_short; } + public static getf_int(): int { return PrimitivesAccessStatic.f_int; } + public static getf_long(): long { return PrimitivesAccessStatic.f_long; } + public static getf_float(): float { return PrimitivesAccessStatic.f_float; } + public static getf_double(): double { return PrimitivesAccessStatic.f_double; } + public static getf_char(): char { return PrimitivesAccessStatic.f_char; } + public static getf_boolean(): boolean { return PrimitivesAccessStatic.f_boolean; } + + public static setf_byte(v: byte): void { PrimitivesAccessStatic.f_byte = v; } + public static setf_short(v: short): void { PrimitivesAccessStatic.f_short = v; } + public static setf_int(v: int): void { PrimitivesAccessStatic.f_int = v; } + public static setf_long(v: long): void { PrimitivesAccessStatic.f_long = v; } + public static setf_float(v: float): void { PrimitivesAccessStatic.f_float = v; } + public static setf_double(v: double): void { PrimitivesAccessStatic.f_double = v; } + public static setf_char(v: char): void { PrimitivesAccessStatic.f_char = v; } + public static setf_boolean(v: boolean): void { PrimitivesAccessStatic.f_boolean = v; } + + public static f_byte: byte; + public static f_short: short; + public static f_int: int; + public static f_long: long; + public static f_float: float; + public static f_double: double; + public static f_char: char; + public static f_boolean: boolean; +} + +class UClass1 { }; +class UClass2 { }; + +class ReferencesAccess { + public getf_UClass1(): UClass1 { return this.f_UClass1; } + public getf_UClass2(): UClass2 { return this.f_UClass2; } + public getf_String(): String { return this.f_String; } + public getf_JSValue(): JSValue { return this.f_JSValue; } + public getf_Promise(): Promise { return this.f_Promise; } + public getf_A_String(): String[] { return this.f_A_String; } + public getf_A_UClass1(): UClass1[] { return this.f_A_UClass1; } + + public setf_UClass1(v: UClass1): void { this.f_UClass1 = v; } + public setf_UClass2(v: UClass2): void { this.f_UClass2 = v; } + public setf_String(v: String): void { this.f_String = v; } + public setf_JSValue(v: JSValue): void { this.f_JSValue = v; } + public setf_Promise(v: Promise): void { this.f_Promise = v; } + public setf_A_String(v: String[]): void { this.f_A_String = v; } + public setf_A_UClass1(v: UClass1[]): void { this.f_A_UClass1 = v; } + + public f_UClass1: UClass1; + public f_UClass2: UClass2; + public f_String: String; + public f_JSValue: JSValue; + public f_Promise: Promise; + public f_A_String: String[]; + public f_A_UClass1: UClass1[]; +}; + +class ReferencesAccessStatic { + public static getf_UClass1(): UClass1 { return ReferencesAccessStatic.f_UClass1; } + public static getf_UClass2(): UClass2 { return ReferencesAccessStatic.f_UClass2; } + public static getf_String(): String { return ReferencesAccessStatic.f_String; } + public static getf_JSValue(): JSValue { return ReferencesAccessStatic.f_JSValue; } + public static getf_Promise(): Promise { return ReferencesAccessStatic.f_Promise; } + + public static setf_UClass1(v: UClass1): void { ReferencesAccessStatic.f_UClass1 = v; } + public static setf_UClass2(v: UClass2): void { ReferencesAccessStatic.f_UClass2 = v; } + public static setf_String(v: String): void { ReferencesAccessStatic.f_String = v; } + public static setf_JSValue(v: JSValue): void { ReferencesAccessStatic.f_JSValue = v; } + public static setf_Promise(v: Promise): void { ReferencesAccessStatic.f_Promise = v; } + + public static f_UClass1: UClass1; + public static f_UClass2: UClass2; + public static f_String: String; + public static f_JSValue: JSValue; + public static f_Promise: Promise; +}; diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/CMakeLists.txt similarity index 73% rename from plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/CMakeLists.txt rename to plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/CMakeLists.txt index ed48d807a..8e35d63de 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/CMakeLists.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -panda_ets_interop_js_gtest(ets_interop_js__test_call_ets_function - CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_call_ets_function.cpp - ETS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_call_ets_function.ets +panda_ets_interop_js_gtest(ets_interop_js__test_function_proxy + CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_function_proxy.cpp + ETS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_function_proxy.ets ) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/check_function_proxy.js b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/check_function_proxy.js new file mode 100644 index 000000000..0fc64165c --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/check_function_proxy.js @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { + getTestFunction +} = require("function_proxy.test.js") + +{ // test simple function-proxy capabilities + const sum_double = getTestFunction("sum_double"); + + ASSERT_TRUE(Object.isSealed(sum_double)); + ASSERT_EQ(sum_double(3, 4), 3 + 4); + + ASSERT_THROWS(TypeError, () => (sum_double(1))); + ASSERT_THROWS(TypeError, () => (sum_double(1, 2, 3, 4))); + ASSERT_THROWS(TypeError, () => (sum_double(1, true))); +} + +{ // test object-proxy + const point_create = getTestFunction("point_create"); + const point_get_x = getTestFunction("point_get_x"); + const point_get_y = getTestFunction("point_get_y"); + + let p = point_create(2, 6); + let x = point_get_x(p); + let y = point_get_y(p); + + ASSERT_EQ(x, 2); + ASSERT_EQ(y, 6); +} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_objects.js b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/function_proxy.test.js similarity index 74% rename from plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_objects.js rename to plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/function_proxy.test.js index 2ab42c3b7..28efa3f62 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_objects.js +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/function_proxy.test.js @@ -13,16 +13,10 @@ * limitations under the License. */ -const { - point_create, - point_get_x, - point_get_y -} = require("call_ets_function.test.js") +const etsVm = require("lib/module/ets_interop_js_napi"); +function getTestFunction(name) { return etsVm.getFunction("Lfunction_proxy/test/ETSGLOBAL;", name) } -let p = point_create(2, 6); -let x = point_get_x(p); -let y = point_get_y(p); - -ASSERT_EQ(x, 2); -ASSERT_EQ(y, 6); +module.exports = { + getTestFunction, +} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.cpp b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/test_function_proxy.cpp similarity index 72% rename from plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.cpp rename to plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/test_function_proxy.cpp index 0a45f129e..4216bf56c 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.cpp +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/test_function_proxy.cpp @@ -18,17 +18,11 @@ namespace panda::ets::interop::js::testing { -class CallEtsFunctionTest : public EtsInteropTest {}; +class FunctionProxyTest : public EtsInteropTest {}; -TEST_F(CallEtsFunctionTest, check_primitives) +TEST_F(FunctionProxyTest, function_proxy) { - RunJsTestSute("check_primitives.js"); -} - -// TODO(v.cherkashin): Enable when implemented, #12573 -TEST_F(CallEtsFunctionTest, DISABLED_check_objects) -{ - RunJsTestSute("check_objects.js"); + RunJsTestSute("check_function_proxy.js"); } } // namespace panda::ets::interop::js::testing diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.ets b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/test_function_proxy.ets similarity index 97% rename from plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.ets rename to plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/test_function_proxy.ets index 85dd65c9d..0e6df03a9 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.ets +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/function_proxy/test_function_proxy.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -package call_ets_function.test; +package function_proxy.test; class Point { diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/CMakeLists.txt index 5b9104e73..e64695ec0 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/CMakeLists.txt @@ -11,6 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -add_subdirectory(ets_object_wrappers_storage_1) -add_subdirectory(ets_object_wrappers_storage_2) +add_subdirectory(proxy_reference_storage_1) +add_subdirectory(proxy_reference_storage_2) add_subdirectory(items_pool) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_1/CMakeLists.txt similarity index 71% rename from plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/CMakeLists.txt rename to plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_1/CMakeLists.txt index 5bf976bb8..8e3222d51 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_1/CMakeLists.txt @@ -11,8 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -panda_ets_interop_js_gtest(ets_interop_js__test_ets_object_wrappers_storage_1 - CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_ets_object_wrappers_storage_1.cpp - ETS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_ets_object_wrappers_storage_1.ets +panda_ets_interop_js_gtest(ets_interop_js__test_proxy_reference_storage_1 + CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_proxy_reference_storage_1.cpp + ETS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_proxy_reference_storage_1.ets LIBRARIES ets_interop_js_napi ) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.cpp b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_1/test_proxy_reference_storage_1.cpp similarity index 31% rename from plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.cpp rename to plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_1/test_proxy_reference_storage_1.cpp index c6108988f..3c2be2397 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.cpp +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_1/test_proxy_reference_storage_1.cpp @@ -14,17 +14,19 @@ */ #include "ets_interop_js_gtest.h" -#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h" #include "plugins/ets/runtime/interop_js/interop_context.h" // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-pro-type-cstyle-cast) namespace panda::ets::interop::js::ets_proxy::testing { -class EtsObjectWrappersStorage1GTest : public js::testing::EtsInteropTest { +class SharedReferenceStorage1GTest : public js::testing::EtsInteropTest { public: void SetUp() override { - storage_ = EtsObjectWrappersStorage::Create(); + storage_ = SharedReferenceStorage::Create(); + auto ctx = InteropCtx::Current(); + ctx->SetJSEnv(GetJsEnv()); memset(&object_array_, 0, sizeof(object_array_)); next_free_idx_ = 0; @@ -39,19 +41,36 @@ public: return EtsObject::FromCoreType(&object_array_[next_free_idx_++]); } - EtsObjectWrapper *CreateWrapper(EtsObject *ets_object) + SharedReference *CreateReference(EtsObject *ets_object) { - EtsObjectWrapper *wrapper = storage_->CreateObjectEtsWrapper(ets_object); + napi_value js_obj; + NAPI_CHECK_FATAL(napi_create_object(GetJsEnv(), &js_obj)); + SharedReference *ref = storage_->CreateReference(InteropCtx::Current(), ets_object, js_obj); // Emulate wrappper usage - ((uintptr_t *)wrapper)[0] = 0xcc00ff23; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - ((uintptr_t *)wrapper)[1] = 0xdd330047; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + ((uintptr_t *)ref)[0] = 0xcc00ff23deadbeef; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + ((uintptr_t *)ref)[1] = 0xdd330047beefdead; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - return wrapper; + return ref; + } + + SharedReference *GetReference(void *data) + { + return storage_->GetReference(data); + } + + void RemoveReference(SharedReference *ref) + { + return storage_->RemoveReference(ref); + } + + bool CheckAlive(void *data) + { + return storage_->CheckAlive(data); } protected: - std::unique_ptr storage_ {}; // NOLINT(misc-non-private-member-variables-in-classes) + std::unique_ptr storage_ {}; // NOLINT(misc-non-private-member-variables-in-classes) private: static constexpr size_t MAX_OBJECTS = 128; @@ -59,93 +78,82 @@ private: size_t next_free_idx_ {}; }; -TEST_F(EtsObjectWrappersStorage1GTest, test_0) +TEST_F(SharedReferenceStorage1GTest, test_0) { EtsObject *ets_object = NewEtsObject(); - ASSERT_EQ(storage_->HasEtsObjectWrapper(ets_object), false); - EtsObjectWrapper *wrapper = CreateWrapper(ets_object); - ASSERT_EQ(storage_->HasEtsObjectWrapper(ets_object), true); + ASSERT_EQ(storage_->HasReference(ets_object), false); + SharedReference *ref = CreateReference(ets_object); + ASSERT_EQ(storage_->HasReference(ets_object), true); - EtsObjectWrapper *wrapper_x = storage_->GetEtsObjectWrapper(ets_object); - EtsObjectWrapper *wrapper_y = storage_->GetEtsObjectWrapperOrNull((void *)wrapper); + SharedReference *ref_x = storage_->GetReference(ets_object); + SharedReference *ref_y = GetReference((void *)ref); - ASSERT_EQ(wrapper, wrapper_x); - ASSERT_EQ(wrapper, wrapper_y); + ASSERT_EQ(ref, ref_x); + ASSERT_EQ(ref, ref_y); - storage_->RemoveEtsObjectWrapper(wrapper); + RemoveReference(ref); } -TEST_F(EtsObjectWrappersStorage1GTest, test_1) +TEST_F(SharedReferenceStorage1GTest, test_1) { EtsObject *ets_object = NewEtsObject(); - EtsObjectWrapper *wrapper = CreateWrapper(ets_object); - - // Check allocated wrapper - EtsObjectWrapper *wrapper_0 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 0)); - ASSERT_EQ(wrapper_0, wrapper); - - // Check unaligned address - EtsObjectWrapper *wrapper_1 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 1)); - EtsObjectWrapper *wrapper_2 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 2)); - EtsObjectWrapper *wrapper_3 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 3)); - EtsObjectWrapper *wrapper_4 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 4)); - EtsObjectWrapper *wrapper_5 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 5)); - EtsObjectWrapper *wrapper_6 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 6)); - EtsObjectWrapper *wrapper_7 = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 7)); - ASSERT_EQ(wrapper_1, nullptr); - ASSERT_EQ(wrapper_2, nullptr); - ASSERT_EQ(wrapper_3, nullptr); - ASSERT_EQ(wrapper_4, nullptr); - ASSERT_EQ(wrapper_5, nullptr); - ASSERT_EQ(wrapper_6, nullptr); - ASSERT_EQ(wrapper_7, nullptr); - - // Check next unallocated wrapper - constexpr size_t WRAPPER_SIZE = sizeof(EtsObjectWrapper); - EtsObjectWrapper *wrapper_x = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + WRAPPER_SIZE)); - ASSERT_EQ(wrapper_x, nullptr); - - // Check physical unmapped space - EtsObjectWrapper *wrapper_y = storage_->GetEtsObjectWrapperOrNull((void *)(uintptr_t(wrapper) + 16 * 4096)); - ASSERT_EQ(wrapper_y, nullptr); - - storage_->RemoveEtsObjectWrapper(wrapper); + SharedReference *ref = CreateReference(ets_object); + + // Check allocated ref + SharedReference *ref_0 = GetReference((void *)(uintptr_t(ref) + 0)); + ASSERT_EQ(ref_0, ref); + + // Check unaligned address); + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + 1)), false); + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + 2)), false); + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + 3)), false); + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + 4)), false); + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + 5)), false); + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + 6)), false); + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + 7)), false); + + // Check next unallocated reference + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + sizeof(SharedReference))), false); + + // Check virtually unmapped space + ASSERT_EQ(CheckAlive((void *)(uintptr_t(ref) + 16 * 4096)), false); + + RemoveReference(ref); } -TEST_F(EtsObjectWrappersStorage1GTest, test_2) +TEST_F(SharedReferenceStorage1GTest, test_2) { EtsObject *ets_object = NewEtsObject(); - EtsObjectWrapper *wrapper = CreateWrapper(ets_object); + SharedReference *ref = CreateReference(ets_object); EtsObject *ets_object_1 = NewEtsObject(); - EtsObjectWrapper *wrapper_1 = CreateWrapper(ets_object_1); + SharedReference *ref_1 = CreateReference(ets_object_1); EtsObject *ets_object_2 = NewEtsObject(); - EtsObjectWrapper *wrapper_2 = CreateWrapper(ets_object_2); + SharedReference *ref_2 = CreateReference(ets_object_2); - EtsObjectWrapper *wrapper_1a = storage_->GetEtsObjectWrapperOrNull((void *)wrapper_1); - ASSERT_EQ(wrapper_1a, wrapper_1); + SharedReference *ref_1a = GetReference((void *)ref_1); + ASSERT_EQ(ref_1a, ref_1); - storage_->RemoveEtsObjectWrapper(wrapper_1); + RemoveReference(ref_1); - // Check deallocated wrapper - EtsObjectWrapper *wrapper_1b = storage_->GetEtsObjectWrapperOrNull((void *)wrapper_1); - ASSERT_EQ(wrapper_1b, nullptr); + // Check deallocated ref + ASSERT_EQ(CheckAlive((void *)ref_1), false); // Check EtsObject *ets_object_4 = NewEtsObject(); - EtsObjectWrapper *wrapper_4 = CreateWrapper(ets_object_4); - ASSERT_EQ(wrapper_4, wrapper_1); + SharedReference *ref_4 = CreateReference(ets_object_4); + ASSERT_EQ(ref_4, ref_1); - storage_->RemoveEtsObjectWrapper(wrapper); - storage_->RemoveEtsObjectWrapper(wrapper_2); - storage_->RemoveEtsObjectWrapper(wrapper_4); + RemoveReference(ref); + RemoveReference(ref_2); + RemoveReference(ref_4); - ASSERT_EQ(storage_->GetEtsObjectWrapperOrNull((void *)wrapper), nullptr); - ASSERT_EQ(storage_->GetEtsObjectWrapperOrNull((void *)wrapper_2), nullptr); - ASSERT_EQ(storage_->GetEtsObjectWrapperOrNull((void *)wrapper_4), nullptr); + ASSERT_EQ(CheckAlive((void *)ref), false); + ASSERT_EQ(CheckAlive((void *)ref_2), false); + ASSERT_EQ(CheckAlive((void *)ref_4), false); } } // namespace panda::ets::interop::js::ets_proxy::testing diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.ets b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_1/test_proxy_reference_storage_1.ets similarity index 100% rename from plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_1/test_ets_object_wrappers_storage_1.ets rename to plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_1/test_proxy_reference_storage_1.ets diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/CMakeLists.txt similarity index 71% rename from plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/CMakeLists.txt rename to plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/CMakeLists.txt index 92f6fc441..e18915159 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/CMakeLists.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -panda_ets_interop_js_test(ets_interop_js__test_ets_object_wrappers_storage_2 - ETS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/test_ets_object_wrappers_storage_2.ets - JS_LAUNCHER ${CMAKE_CURRENT_LIST_DIR}/test_ets_object_wrappers_storage_2.js +panda_ets_interop_js_test(ets_interop_js__test_proxy_reference_storage_2 + ETS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/test_proxy_reference_storage_2.ets + JS_LAUNCHER ${CMAKE_CURRENT_LIST_DIR}/test_proxy_reference_storage_2.js ) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.ets b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/test_proxy_reference_storage_2.ets similarity index 94% rename from plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.ets rename to plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/test_proxy_reference_storage_2.ets index 0a0f29856..641e8f880 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.ets +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/test_proxy_reference_storage_2.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -package ets_object_wrappers_storage_2.test; +package proxy_reference_storage_2.test; class Point { constructor(x: double, y: double) { diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.js b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/test_proxy_reference_storage_2.js similarity index 82% rename from plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.js rename to plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/test_proxy_reference_storage_2.js index a3da90c22..ef16f2834 100644 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/mem/ets_object_wrappers_storage_2/test_ets_object_wrappers_storage_2.js +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/mem/proxy_reference_storage_2/test_proxy_reference_storage_2.js @@ -31,14 +31,14 @@ if (!res) { } -const Point = etsVm.getClass("Lets_object_wrappers_storage_2/test/Point;"); +const Point = etsVm.getClass("Lproxy_reference_storage_2/test/Point;"); -// Call EtsObjectWrappersStorage::CreateObjectEtsWrapper() method +// Call SharedReferenceStorage::CreateObjectEtsWrapper() method for (let i = 0; i < 4*4096; ++i) { let p = new Point(i, i); } -// Method EtsObjectWrappersStorage::RemoveEtsObjectWrapper() will be called before the virtual machine is terminated +// Method SharedReferenceStorage::RemoveSharedReference() will be called before the virtual machine is terminated diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_builtin_fields.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_builtin_fields.js deleted file mode 100644 index 044ce43bb..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_builtin_fields.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { BuiltinFields } = require("use_ets_class.test.js") - -let b = new BuiltinFields(); - - -// Check access to the 'String' type field -ASSERT_EQ(b.string_val, null); - -b.string_val = "Hello World!"; -ASSERT_EQ(b.get_string_val(), "Hello World!"); - -b.set_string_val(null); -ASSERT_EQ(b.set_string_val, null); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_primitive_fields.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_primitive_fields.js deleted file mode 100644 index 1edf86f9b..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_primitive_fields.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { PrimitiveFields } = require("use_ets_class.test.js") - - -let pf = new PrimitiveFields(); - - -// Check access to the 'boolean' type field -ASSERT_EQ(pf.boolean_val, false); - -pf.set_boolean_val(true); -ASSERT_EQ(pf.boolean_val, true); - -pf.boolean_val = false; -ASSERT_EQ(pf.get_boolean_val(), false); - - -// Check access to the 'int' type field -ASSERT_EQ(pf.int_val, 0); - -pf.set_int_val(-235346); -ASSERT_EQ(pf.int_val, -235346); - -pf.int_val = 36034; -ASSERT_EQ(pf.get_int_val(), 36034); - - -// Check access to the 'float' type field -ASSERT_EQ(pf.float_val, 0); - -pf.set_float_val(53.34455); -ASSERT_EQ(pf.float_val.toFixed(5), "53.34455"); - -pf.float_val = -0.003451; -ASSERT_EQ(pf.get_float_val().toFixed(6), "-0.003451"); - - -// Check access to the 'double' type field -ASSERT_EQ(pf.double_val, 0); - -pf.set_double_val(34.56642095); -ASSERT_EQ(pf.double_val, 34.56642095); - -pf.double_val = -0.09234; -ASSERT_EQ(pf.get_double_val(), -0.09234); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_reference_fields.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_reference_fields.js deleted file mode 100644 index 479917023..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_reference_fields.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { Line, Point } = require("use_ets_class.test.js") - -let p0 = new Point(6, 9); -let p1 = new Point(1, 5); -let line = new Line(p0, p1); -ASSERT_EQ(line.get_dx(), 5); -ASSERT_EQ(line.p0, p0); -ASSERT_EQ(line.p1, p1); - - -line.p0 = p1; -line.p1 = p0; -ASSERT_EQ(line.get_dx(), 5); -ASSERT_EQ(line.get_p0(), p1); -ASSERT_EQ(line.get_p1(), p0); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_primitive_fields.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_primitive_fields.js deleted file mode 100644 index 293d4082f..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_primitive_fields.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -const { StaticPrimitiveFields } = require("use_ets_class.test.js") - -// Check access to the 'boolean' type field -ASSERT_EQ(StaticPrimitiveFields.boolean_val, false); - -StaticPrimitiveFields.set_boolean_val(true); -ASSERT_EQ(StaticPrimitiveFields.boolean_val, true); - -StaticPrimitiveFields.boolean_val = false; -ASSERT_EQ(StaticPrimitiveFields.get_boolean_val(), false); - - -// Check access to the 'int' type field -ASSERT_EQ(StaticPrimitiveFields.int_val, 0); - -StaticPrimitiveFields.set_int_val(-536323); -ASSERT_EQ(StaticPrimitiveFields.int_val, -536323); - -StaticPrimitiveFields.int_val = 23647; -ASSERT_EQ(StaticPrimitiveFields.get_int_val(), 23647); - - -// Check access to the 'float' type field -ASSERT_EQ(StaticPrimitiveFields.float_val, 0); - -StaticPrimitiveFields.set_float_val(23.54632); -ASSERT_EQ(StaticPrimitiveFields.float_val.toFixed(5), "23.54632"); - -StaticPrimitiveFields.float_val = -0.035784; -ASSERT_EQ(StaticPrimitiveFields.get_float_val().toFixed(6), "-0.035784"); - - -// Check access to the 'double' type field -ASSERT_EQ(StaticPrimitiveFields.double_val, 0); - -StaticPrimitiveFields.set_double_val(546.236345); -ASSERT_EQ(StaticPrimitiveFields.double_val, 546.236345); - -StaticPrimitiveFields.double_val = -0.0023405; -ASSERT_EQ(StaticPrimitiveFields.get_double_val(), -0.0023405); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_reference_fields.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_reference_fields.js deleted file mode 100644 index de4a2bea4..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_reference_fields.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { Line, Point, StaticReferenceFields } = require("use_ets_class.test.js") - - -let p0 = new Point(3, 9); -let p1 = new Point(7, 2); -let line = new Line(p0, p1); - - -// Check 'point_val' field -ASSERT_EQ(StaticReferenceFields.point_val, null); - -StaticReferenceFields.set_point_val(p0); -ASSERT_EQ(StaticReferenceFields.point_val, p0); - -StaticReferenceFields.point_val = null; -ASSERT_EQ(StaticReferenceFields.get_point_val(), null); - - -// Check 'line_val' field -ASSERT_EQ(StaticReferenceFields.line_val, null); - -StaticReferenceFields.set_line_val(line); -ASSERT_EQ(StaticReferenceFields.line_val, line); - -StaticReferenceFields.line_val = null; -ASSERT_EQ(StaticReferenceFields.get_line_val(), null); - - -// Check throwing an exception if the argument is of the wrong type -let is_throwed = false; -try { - StaticReferenceFields.line_val = p0; -} catch (error) { - is_throwed = true; -} -ASSERT_EQ(is_throwed, true); - -is_throwed = false; -try { - StaticReferenceFields.set_point_val(line); -} catch (error) { - is_throwed = true; -} -ASSERT_EQ(is_throwed, true); - -is_throwed = false; -try { - StaticReferenceFields.set_point_val({}); -} catch (error) { - is_throwed = true; -} -ASSERT_EQ(is_throwed, true); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_primitives.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_primitives.js deleted file mode 100644 index 40fefbedf..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_primitives.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { Point } = require("use_ets_class.test.js") - - -let point = new Point(6, 9); - -ASSERT_EQ(point.get_x(), 6); -ASSERT_EQ(point.get_y(), 9); - -point.set_x(10); -point.set_y(33); - -ASSERT_EQ(point.get_x(), 10); -ASSERT_EQ(point.get_y(), 33); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_references.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_references.js deleted file mode 100644 index 57281735e..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_references.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { Line, Point } = require("use_ets_class.test.js") - -let p0 = new Point(6, 9); -let p1 = new Point(1, 5); -let line = new Line(p0, p1); - -ASSERT_EQ(line.get_dx(), 5); -ASSERT_EQ(line.get_p0(), p0); -ASSERT_EQ(line.get_p1(), p1); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_null_object.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_null_object.js deleted file mode 100644 index a273c8568..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_null_object.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { Point, Line } = require("use_ets_class.test.js") - - -let p = new Point(6, 9); -let line = new Line(p, null); - -ASSERT_EQ(line.get_p0(), p); -ASSERT_EQ(line.p0, p); -ASSERT_EQ(line.get_p1(), null); -ASSERT_EQ(line.p1, null); - -line.set_p0(null); -line.set_p1(p); - -ASSERT_EQ(line.get_p0(), null); -ASSERT_EQ(line.p0, null); -ASSERT_EQ(line.get_p1(), p); -ASSERT_EQ(line.p1, p); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_the_same_ets_object.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_the_same_ets_object.js deleted file mode 100644 index 6ff771ee2..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_the_same_ets_object.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { Point, Line } = require("use_ets_class.test.js") - - -let p0 = new Point(6, 9); -let p1 = new Point(0, 3); -let line = new Line(p0, p1); - -let p0a = line.get_p0(); -let p0b = line.p0; - - -// Check that p0 is the same p0a -ASSERT_EQ(p0.x, p0a.x); -ASSERT_EQ(p0.y, p0a.y); -ASSERT_EQ(p0, p0a); - - -// Check that p0 is the same p0b -ASSERT_EQ(p0.x, p0b.x); -ASSERT_EQ(p0.y, p0b.y); -ASSERT_EQ(p0, p0b); - - -// Check that p0a is the same p0b -ASSERT_EQ(p0a.x, p0b.x); -ASSERT_EQ(p0a.y, p0b.y); -ASSERT_EQ(p0a, p0b); diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.cpp b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.cpp deleted file mode 100644 index a22257477..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "ets_interop_js_gtest.h" - -namespace panda::ets::interop::js::testing { - -class UseEtsClassTest : public EtsInteropTest {}; - -TEST_F(UseEtsClassTest, check_call_with_primitives) -{ - RunJsTestSute("check_call_with_primitives.js"); -} - -TEST_F(UseEtsClassTest, check_call_with_references) -{ - RunJsTestSute("check_call_with_references.js"); -} - -TEST_F(UseEtsClassTest, check_access_to_primitive_fields) -{ - RunJsTestSute("check_access_to_primitive_fields.js"); -} - -TEST_F(UseEtsClassTest, check_access_to_reference_fields) -{ - RunJsTestSute("check_access_to_reference_fields.js"); -} - -TEST_F(UseEtsClassTest, check_inheritance) -{ - RunJsTestSute("check_inheritance.js"); -} - -TEST_F(UseEtsClassTest, check_the_same_ets_object) -{ - RunJsTestSute("check_the_same_ets_object.js"); -} - -TEST_F(UseEtsClassTest, check_null_object) -{ - RunJsTestSute("check_null_object.js"); -} - -TEST_F(UseEtsClassTest, check_access_to_static_primitive_fields) -{ - RunJsTestSute("check_access_to_static_primitive_fields.js"); -} - -TEST_F(UseEtsClassTest, check_access_to_static_reference_fields) -{ - RunJsTestSute("check_access_to_static_reference_fields.js"); -} - -// TODO(v.cherkashin): Enable when implemented, #12573 -TEST_F(UseEtsClassTest, DISABLED_check_access_to_builtin_fields) -{ - RunJsTestSute("check_access_to_builtin_fields.js"); -} - -} // namespace panda::ets::interop::js::testing diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.ets b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.ets deleted file mode 100644 index 87cd5dc9a..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.ets +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package use_ets_class.test; - -class Point { - constructor(x: double, y: double) { - this.x = x; - this.y = y; - } - public get_x(): double { - return this.x; - } - public get_y(): double { - return this.y; - } - public set_x(x: double) { - this.x = x; - } - public set_y(y: double) { - this.y = y; - } - public x: double; - public y: double; -} - -class Line { - constructor(p0: Point, p1: Point) { - this.p0 = p0; - this.p1 = p1; - } - get_p0(): Point { - return this.p0; - } - set_p0(p: Point): void { - this.p0 = p; - } - get_p1(): Point { - return this.p1; - } - set_p1(p: Point): void { - this.p1 = p; - } - get_dx(): double { - let x0: double = this.p0.get_x(); - let x1: double = this.p1.get_x(); - - if (x0 > x1) { - return x0 - x1; - } else { - return x1 - x0; - } - } - public p0: Point; - public p1: Point; -} - -class PrimitiveFields { - public set_boolean_val(v: boolean): void { - this.boolean_val = v; - } - public get_boolean_val(): boolean { - return this.boolean_val; - } - - public set_int_val(v: int) : void { - this.int_val = v; - } - public get_int_val(): int { - return this.int_val; - } - - public set_float_val(v: float) { - this.float_val = v; - } - public get_float_val(): float { - return this.float_val; - } - - public set_double_val(v: double) { - this.double_val = v; - } - public get_double_val(): double { - return this.double_val; - } - - // TODO(v.cherkashin): Add tests for other primitive types, #12573 - public boolean_val: boolean; - // public char_val: char; - // public byte_val: byte; - // public ubyte_val: ubyte; - // public short_val: short; - // public ushort_val: ushort; - public int_val: int; - // public uint_val: uint; - // public long_val: long; - // public ulong_val: ulong; - public float_val: float; - public double_val: double; -} - - -class StaticPrimitiveFields { - public static set_boolean_val(v: boolean): void { - StaticPrimitiveFields.boolean_val = v; - } - public static get_boolean_val(): boolean { - return StaticPrimitiveFields.boolean_val; - } - - public static set_int_val(v: int) : void { - StaticPrimitiveFields.int_val = v; - } - public static get_int_val(): int { - return StaticPrimitiveFields.int_val; - } - - public static set_float_val(v: float) { - StaticPrimitiveFields.float_val = v; - } - public static get_float_val(): float { - return StaticPrimitiveFields.float_val; - } - - public static set_double_val(v: double) { - StaticPrimitiveFields.double_val = v; - } - public static get_double_val(): double { - return StaticPrimitiveFields.double_val; - } - - // TODO(v.cherkashin): Add tests for other primitive types, #12573 - public static boolean_val: boolean; - // public static char_val: char; - // public static byte_val: byte; - // public static ubyte_val: ubyte; - // public static short_val: short; - // public static ushort_val: ushort; - public static int_val: int; - // public static uint_val: uint; - // public static long_val: long; - // public static ulong_val: ulong; - public static float_val: float; - public static double_val: double; -} - - -class StaticReferenceFields { - public static set_point_val(p: Point): void { - StaticReferenceFields.point_val = p; - } - public static get_point_val(): Point { - return StaticReferenceFields.point_val; - } - - public static set_line_val(l: Line): void { - StaticReferenceFields.line_val = l; - } - public static get_line_val(): Line { - return StaticReferenceFields.line_val; - } - - public static point_val: Point; - public static line_val: Line; -} - - -class BuiltinFields { - public set_string_val(s: String): void { - this.string_val = s; - } - public get_string_val(): String { - return this.string_val; - } - - public string_val: String; -} diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/use_ets_class.test.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/use_ets_class.test.js deleted file mode 100644 index 0af9461ce..000000000 --- a/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/use_ets_class.test.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const etsVm = require("lib/module/ets_interop_js_napi"); - -let Point = etsVm.getClass("Luse_ets_class/test/Point;"); -let Line = etsVm.getClass("Luse_ets_class/test/Line;"); -let PrimitiveFields = etsVm.getClass("Luse_ets_class/test/PrimitiveFields;"); -let StaticPrimitiveFields = etsVm.getClass("Luse_ets_class/test/StaticPrimitiveFields;"); -let StaticReferenceFields = etsVm.getClass("Luse_ets_class/test/StaticReferenceFields;"); -let BuiltinFields = etsVm.getClass("Luse_ets_class/test/BuiltinFields;"); - -module.exports = { - Point, - Line, - PrimitiveFields, - StaticPrimitiveFields, - StaticReferenceFields, - BuiltinFields, -} diff --git a/plugins/ets/tests/interop_js/tests/proxy_types/proxy_types_test.js b/plugins/ets/tests/interop_js/tests/proxy_types/proxy_types_test.js index 755118714..e425eae07 100644 --- a/plugins/ets/tests/interop_js/tests/proxy_types/proxy_types_test.js +++ b/plugins/ets/tests/interop_js/tests/proxy_types/proxy_types_test.js @@ -60,21 +60,18 @@ function testPrimitives(etsVm) { assertEq(Primitives.STATIC_FLOAT.toFixed(3), (1.1).toFixed(3)); assertEq(Primitives.STATIC_DOUBLE.toFixed(3), (2.2).toFixed(3)); assertEq(Primitives.STATIC_INT, 3); - // TODO(v.cherkashin): Uncomment when implemented, #12573 - // assertEq(Primitives.STATIC_STRING, "Panda static string!"); + assertEq(Primitives.STATIC_STRING, "Panda static string!"); Primitives.STATIC_BOOLEAN = false; Primitives.STATIC_FLOAT = 7.7; Primitives.STATIC_DOUBLE = 8.8; Primitives.STATIC_INT = 9; - // TODO(v.cherkashin): Uncomment when implemented, #12573 - // Primitives.STATIC_STRING = "JS static string"; + Primitives.STATIC_STRING = "JS static string"; assertEq(Primitives.STATIC_BOOLEAN, false); assertEq(Primitives.STATIC_FLOAT.toFixed(3), (7.7).toFixed(3)); assertEq(Primitives.STATIC_DOUBLE.toFixed(3), (8.8).toFixed(3)); assertEq(Primitives.STATIC_INT, 9); - // TODO(v.cherkashin): Uncomment when implemented, #12573 - // assertEq(Primitives.STATIC_STRING, "JS static string"); + assertEq(Primitives.STATIC_STRING, "JS static string"); // Non-static fields const primitives = new Primitives(); @@ -82,21 +79,18 @@ function testPrimitives(etsVm) { assertEq(primitives.FLOAT.toFixed(3), (4.4).toFixed(3)); assertEq(primitives.DOUBLE.toFixed(3), (5.5).toFixed(3)); assertEq(primitives.INT, 6); - // TODO(v.cherkashin): Uncomment when implemented, #12573 - // assertEq(primitives.STRING, "Panda string!!"); + assertEq(primitives.STRING, "Panda string!!"); primitives.BOOLEAN = false; primitives.FLOAT = 7.7; primitives.DOUBLE = 8.8; primitives.INT = 9; - // TODO(v.cherkashin): Uncomment when implemented, #12573 - // primitives.STRING = "JS string"; + primitives.STRING = "JS string"; assertEq(primitives.BOOLEAN, false); assertEq(primitives.FLOAT.toFixed(3), (7.7).toFixed(3)); assertEq(primitives.DOUBLE.toFixed(3), (8.8).toFixed(3)); assertEq(primitives.INT, 9); - // TODO(v.cherkashin): Uncomment when implemented, #12573 - // assertEq(primitives.STRING, "JS string"); + assertEq(primitives.STRING, "JS string"); } function testObjects(etsVm) { @@ -109,14 +103,12 @@ function testMethods(etsVm) { const Methods = etsVm.getClass('Lproxy_types_test/Methods;'); assertEq(Methods.staticSumDouble(3, 0.22).toFixed(3), (3.22).toFixed(3)); - // TODO(v.cherkashin): Uncomment when implemented, #12573 - // assertEq(Methods.staticSumString("Hello from ", "Panda!!!"), "Hello from Panda!!!"); + assertEq(Methods.staticSumString("Hello from ", "Panda!!!"), "Hello from Panda!!!"); assertEq(Methods.staticIsTrue(true), true); const o = new Methods(); assertEq(o.sumDouble(3, 0.22).toFixed(3), (3.22).toFixed(3)); - // TODO(v.cherkashin): Uncomment when implemented, #12573 - // assertEq(o.sumString("Hello from ", "Panda!!!!"), "Hello from Panda!!!!"); + assertEq(o.sumString("Hello from ", "Panda!!!!"), "Hello from Panda!!!!"); assertEq(o.isTrue(false), false); } diff --git a/runtime/mem/vm_handle.h b/runtime/mem/vm_handle.h index 3a9946584..030372b98 100644 --- a/runtime/mem/vm_handle.h +++ b/runtime/mem/vm_handle.h @@ -45,6 +45,11 @@ public: } } + template >> + inline explicit VMHandle(const VMHandle

&other) : HandleBase(other.GetAddress()) + { + } + template >> inline explicit VMHandle(const LocalObjectHandle

&other); -- Gitee