From b538da49a5f959e971742ddfa546fd25176d0495 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Thu, 15 Jun 2023 14:48:26 +0300 Subject: [PATCH 1/2] interop_js: Reimplement access to ets from js Also this commit has the following changes: * Fix passing arguments when creating the EtsVM * Add ability passing null to EtsCallImpl() Signed-off-by: Vyacheslav Cherkashin --- plugins/ets/runtime/interop_js/BUILD.gn | 6 + plugins/ets/runtime/interop_js/CMakeLists.txt | 6 + .../ets_proxy/ets_class_wrapper.cpp | 227 ++++++++++++++++++ .../interop_js/ets_proxy/ets_class_wrapper.h | 100 ++++++++ .../ets_proxy/ets_field_wrapper.cpp | 223 +++++++++++++++++ .../interop_js/ets_proxy/ets_field_wrapper.h | 70 ++++++ .../ets_proxy/ets_method_wrapper.cpp | 125 ++++++++++ .../interop_js/ets_proxy/ets_method_wrapper.h | 76 ++++++ .../ets_proxy/ets_object_reference.h | 48 ++++ .../ets_proxy/ets_object_wrapper.cpp | 70 ++++++ .../interop_js/ets_proxy/ets_object_wrapper.h | 65 +++++ .../ets_proxy/ets_object_wrappers_storage.cpp | 87 +++++++ .../ets_proxy/ets_object_wrappers_storage.h | 63 +++++ .../interop_js/ets_proxy/ets_proxy.cpp | 69 ++++++ .../runtime/interop_js/ets_proxy/ets_proxy.h | 29 +++ .../interop_js/ets_proxy/js_convert_object.h | 107 +++++++++ .../ets_proxy/lazy_ets_class_wrapper_link.h | 31 +++ .../interop_js/ets_proxy/typed_pointer.h | 83 +++++++ .../interop_js/ets_proxy/wrappers_cache.h | 63 +++++ .../ets/runtime/interop_js/ets_vm_plugin.cpp | 87 ++++--- .../ets/runtime/interop_js/interop_context.h | 42 +++- .../ets/runtime/interop_js/js_refconvert.h | 2 +- .../ets/runtime/interop_js/js_value_call.cpp | 54 ++++- .../ets/runtime/interop_js/js_value_call.h | 15 ++ .../ets/runtime/interop_js/ts2ets_common.h | 12 +- plugins/ets/runtime/types/ets_method.h | 5 + plugins/ets/runtime/types/ets_object.h | 19 +- .../interop_js/cmake/interop_js_tests.cmake | 4 +- .../interop_js/gtest_plugin/CMakeLists.txt | 6 +- .../gtest_plugin/ets_interop_js_gtest.h | 45 +++- .../interop_js/gtest_plugin/gtest_launcher.js | 35 +-- .../ets/tests/interop_js/tests/CMakeLists.txt | 1 + .../interop_js/tests/ets_proxy/CMakeLists.txt | 15 ++ .../call_ets_function/CMakeLists.txt | 17 ++ .../call_ets_function.test.js | 28 +++ .../call_ets_function/check_objects.js | 28 +++ .../call_ets_function/check_primitives.js | 18 ++ .../test_call_ets_function.cpp | 34 +++ .../test_call_ets_function.ets | 43 ++++ .../ets_proxy/use_ets_class/CMakeLists.txt | 17 ++ .../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_inheritance.js | 42 ++++ .../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 +++ .../number_subtypes/number_subtypes.cpp.erb | 4 +- .../tests/proxy_types/proxy_types_test.js | 36 +-- 55 files changed, 2717 insertions(+), 102 deletions(-) create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.cpp create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.cpp create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/lazy_ets_class_wrapper_link.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h create mode 100644 plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/CMakeLists.txt create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/call_ets_function.test.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_objects.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_primitives.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.cpp create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.ets create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/CMakeLists.txt create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_builtin_fields.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_primitive_fields.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_reference_fields.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_primitive_fields.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_reference_fields.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_primitives.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_references.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_inheritance.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_null_object.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_the_same_ets_object.js create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.cpp create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.ets create mode 100644 plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/use_ets_class.test.js diff --git a/plugins/ets/runtime/interop_js/BUILD.gn b/plugins/ets/runtime/interop_js/BUILD.gn index 47e27021b..d6af09c8b 100644 --- a/plugins/ets/runtime/interop_js/BUILD.gn +++ b/plugins/ets/runtime/interop_js/BUILD.gn @@ -31,6 +31,12 @@ ohos_shared_library("ets_interop_js_napi") { sources = [ "ets_vm_plugin.cpp", "js_value.cpp", + "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/ets_proxy.cpp", "interop_context.cpp", "intrinsics_api_impl.cpp", "js_value_call.cpp", diff --git a/plugins/ets/runtime/interop_js/CMakeLists.txt b/plugins/ets/runtime/interop_js/CMakeLists.txt index 85a6810d1..1ba175058 100644 --- a/plugins/ets/runtime/interop_js/CMakeLists.txt +++ b/plugins/ets/runtime/interop_js/CMakeLists.txt @@ -30,6 +30,12 @@ panda_ets_interop_js_plugin(ets_interop_js_napi interop_context.cpp ets_vm_plugin.cpp intrinsics_api_impl.cpp + 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/ets_proxy.cpp js_value.cpp js_refconvert.cpp js_value_call.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 new file mode 100644 index 000000000..34ab9d16f --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.cpp @@ -0,0 +1,227 @@ +/** + * 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 "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h" + +#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/js_value_call.h" +#include "plugins/ets/runtime/interop_js/napi_env_scope.h" + +namespace panda::ets::interop::js::ets_proxy { + +class JSRefConvertObj : public JSRefConvert { +public: + explicit JSRefConvertObj(EtsClassWrapper *ets_class_wrapper) + : JSRefConvert(this), ets_class_wrapper_(ets_class_wrapper) + { + } + + napi_value WrapImpl(InteropCtx *ctx, EtsObject *ets_object) const + { + return JSConvertOBJECT::Wrap(ctx, ctx->GetJSEnv(), ets_class_wrapper_, ets_object); + } + + EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value js_value) const + { + 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(); + } + +private: + EtsClassWrapper *ets_class_wrapper_ {}; +}; + +/*static*/ +EtsClassWrapper *EtsClassWrapper::Get(napi_env env, EtsClass *ets_class) +{ + // TODO(vpukhov): Use JSRefConvertCache instead of EtsClassWrappersCache, #12573 + EtsClassWrappersCache *cache = InteropCtx::Current()->GetEtsClassWrappersCache(); + + ASSERT(ets_class != nullptr); + EtsClassWrapper *ets_class_wrapper = cache->Lookup(ets_class); + if (LIKELY(ets_class_wrapper != nullptr)) { + return ets_class_wrapper; + } + + std::unique_ptr ets_class_wrapper_u = EtsClassWrapper::Create(env, ets_class); + if (UNLIKELY(ets_class_wrapper_u.get() == nullptr)) { + return nullptr; + } + ets_class_wrapper = cache->Insert(ets_class, std::move(ets_class_wrapper_u)); + + InteropCtx *ctx = InteropCtx::Current(); + ctx->GetRefConvertCache()->Insert(ets_class->GetRuntimeClass(), + std::make_unique(ets_class_wrapper)); + return ets_class_wrapper; +} + +/*static*/ +std::unique_ptr EtsClassWrapper::Create(napi_env env, EtsClass *ets_class) +{ + // NOLINTNEXTLINE(readability-identifier-naming) + auto _this = std::unique_ptr(new EtsClassWrapper(ets_class)); + + 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 '' + + std::vector js_props; + 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)); + } + + 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)); + } + + // 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) { + auto name = method.GetName().data; + auto it = names.insert(name); + NAPI_FATAL_IF(!it.second); // no overloading + if (UNLIKELY(method.IsInstanceConstructor())) { + _this->ets_ctor_wrapper_ = LazyEtsMethodWrapperLink(&method); + continue; + } + if (UNLIKELY(method.IsConstructor())) { + // 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); + 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); + + 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_create_reference(env, js_ctor, 1, &_this->js_ctor_ref_)); + return _this; +} + +/*static*/ +napi_value EtsClassWrapper::CtorCallback(napi_env env, napi_callback_info cinfo) +{ + NapiScope scope(env); + + size_t argc; + napi_value js_this; + 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)) { + if (UNLIKELY(storage->CreateObjectEtsWrapper(env, ets_object, js_this, std::move(ets_ref), + ets_class_wrapper) == nullptr)) { + InteropCtx::ThrowJSError(env, "Cannot create ObjectEtsWrapper"); + 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); + + EtsMethodWrapper *ets_ctor_wrapper = EtsMethodWrapper::ResolveLazyLink(env, ets_ctor_wrapper_); + if (UNLIKELY(ets_ctor_wrapper == 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)); + + ScopedManagedCodeThread managed_scope(coro); + if (!coro->GetPandaVM()->GetClassLinker()->InitializeClass(coro, ets_class_)) { + ctx->ForwardEtsException(coro); + return nullptr; + } + + EtsHandleScope scope(coro); + EtsHandle ets_object(coro, EtsObject::Create(ets_class_)); + + EtsObjectWrappersStorage *storage = ctx->GetEtsObjectWrappersStorage(); + if (!storage->HasEtsObjectWrapper(ets_object.GetPtr())) { + // Create New EtsObjectWrapper + UniqueEtsObjectReference ets_ref( + ctx->Refstor()->Add(ets_object->GetCoreType(), mem::Reference::ObjectType::GLOBAL)); + if (UNLIKELY(storage->CreateObjectEtsWrapper(env, ets_object.GetPtr(), js_this, std::move(ets_ref), this) == + nullptr)) { + InteropCtx::ThrowJSError(env, "Cannot create ObjectEtsWrapper"); + return nullptr; + } + } + + Method *method = ets_ctor_wrapper->GetEtsMethod()->GetPandaMethod(); + ASSERT(method->IsStatic() == false); + ASSERT(method->IsInstanceConstructor() == true); + EtsCallImpl(coro, ctx, method, {js_args.data(), js_args.size()}, ets_object.GetPtr()); + + return 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 new file mode 100644 index 000000000..cb9a28053 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h @@ -0,0 +1,100 @@ +/** + * 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_CLASS_WRAPPER_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_CLASS_WRAPPER_H_ + +#include "libpandabase/macros.h" +#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/ts2ets_common.h" + +#include +#include + +namespace panda::ets { +class EtsClass; +} // namespace panda::ets + +namespace panda::ets::interop::js::ets_proxy { + +using EtsClassWrappersCache = WrappersCache; + +class EtsClassWrapper { +public: + // clang-format off + static constexpr auto FIELD_ATTR = static_cast(napi_writable | napi_enumerable); + static constexpr auto METHOD_ATTR = napi_default; + static constexpr auto STATIC_FIELD_ATTR = static_cast(napi_writable | napi_enumerable | napi_static); // NOLINT(hicpp-signed-bitwise) + static constexpr auto STATIC_METHOD_ATTR = napi_static; + // 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 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; + } + + EtsClass *GetEtsClass() + { + return ets_class_; + } + + napi_value GetJsCtor(napi_env env) const + { + napi_value ctor; + NAPI_CHECK_FATAL(napi_get_reference_value(env, js_ctor_ref_, &ctor)); + return ctor; + } + +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); + + EtsClass *ets_class_ {}; + LazyEtsMethodWrapperLink ets_ctor_wrapper_ {}; + 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_; +}; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_CLASS_WRAPPER_H_ 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 new file mode 100644 index 000000000..1d1899e97 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.cpp @@ -0,0 +1,223 @@ +/** + * 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 "plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h" + +#include "libpandafile/file.h" +#include "plugins/ets/runtime/ets_class_root.h" +#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/types/ets_object.h" + +namespace panda::ets::interop::js::ets_proxy { + +template +static napi_value EtsFieldGetter(napi_env env, napi_callback_info cinfo) +{ + size_t argc = 0; + napi_value js_this; + void *data; + NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, &js_this, &data)); + if (UNLIKELY(argc != 0)) { + InteropCtx::ThrowJSError(env, "getter called in wrong context"); + return napi_value {}; + } + + auto ets_field_wrapper = reinterpret_cast(data); + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + InteropCtx *ctx = InteropCtx::Current(coro); + 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()); + } + return FieldAccessor::Getter(ctx, env, std::move(ets_object), ets_field_wrapper); +} + +template +static napi_value EtsFieldSetter(napi_env env, napi_callback_info cinfo) +{ + size_t argc = 1; + napi_value js_value; + napi_value js_this; + void *data; + NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, &js_value, &js_this, &data)); + if (UNLIKELY(argc != 1)) { + InteropCtx::ThrowJSError(env, "setter called in wrong context"); + return napi_value {}; + } + + auto ets_field_wrapper = reinterpret_cast(data); + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + InteropCtx *ctx = InteropCtx::Current(coro); + 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()); + } + FieldAccessor::Setter(ctx, env, std::move(ets_object), ets_field_wrapper, js_value); + return napi_value {}; +} + +struct EtsFieldAccessorOBJECT { + static napi_value Getter(InteropCtx *ctx, napi_env env, EtsHandle 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); + } + + static void 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()); + } + } +}; + +template +struct EtsFieldAccessorPRIMITIVE { + using PrimitiveType = typename Convertor::cpptype; + + static napi_value Getter(InteropCtx * /*ctx*/, napi_env env, EtsHandle 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, + 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()); + } + } +}; + +EtsClassWrapper *EtsFieldWrapper::GetEtsClassWrapper(napi_env env) +{ + if (LIKELY(lazy_ets_class_wrapper_link_.IsResolved())) { + return lazy_ets_class_wrapper_link_.GetResolved(); + } + + const Field *field = lazy_ets_class_wrapper_link_.GetUnresolved(); + if (UNLIKELY(field->GetTypeId() != panda_file::Type::TypeId::REFERENCE)) { + return nullptr; + } + + EtsClass *ets_class = EtsClass::FromRuntimeClass(field->ResolveTypeClass()); + EtsClassWrapper *ets_class_wrapper = EtsClassWrapper::Get(env, ets_class); + if (UNLIKELY(ets_class_wrapper == nullptr)) { + return nullptr; + } + + // Update lazy_link + lazy_ets_class_wrapper_link_ = FieldLazyEtsClassWrapperLink(ets_class_wrapper); + return ets_class_wrapper; +} + +template +static napi_property_descriptor DoMakeNapiProperty(const Field &field, void *prop_data) +{ + 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; + + 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; + } + } + return prop; +} + +napi_property_descriptor EtsFieldWrapper::MakeNapiProperty(const Field &field) +{ + return DoMakeNapiProperty(field, this); +} + +napi_property_descriptor EtsFieldWrapper::MakeNapiStaticProperty(const Field &field) +{ + offset_ = field.GetOffset() + EtsClass::GetRuntimeClassOffset(); + return DoMakeNapiProperty(field, this); +} + +} // namespace panda::ets::interop::js::ets_proxy 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 new file mode 100644 index 000000000..6650af4b8 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h @@ -0,0 +1,70 @@ +/** + * 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_FIELD_WRAPPER_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_FIELD_WRAPPER_H_ + +#include +#include + +#include "plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h" +#include "runtime/include/field.h" + +namespace panda { +class Field; +} // namespace panda + +namespace panda::ets::interop::js::ets_proxy { + +class EtsClassWrapper; + +class EtsFieldWrapper { +public: + static std::unique_ptr Create(EtsClassWrapper *owner, const Field *field) + { + return std::unique_ptr(new EtsFieldWrapper(owner, field)); + } + + EtsClassWrapper *GetOwner() + { + return owner_; + } + + uint32_t GetOffset() const + { + return offset_; + } + + EtsClassWrapper *GetEtsClassWrapper(napi_env env); + + 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()) + { + } + + EtsClassWrapper *owner_ {}; + FieldLazyEtsClassWrapperLink lazy_ets_class_wrapper_link_ {}; + uint32_t offset_ {}; +}; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_FIELD_WRAPPER_H_ 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 new file mode 100644 index 000000000..03fb668a5 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.cpp @@ -0,0 +1,125 @@ +/** + * 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 "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/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/js_value_call.h" +#include "plugins/ets/runtime/interop_js/napi_env_scope.h" + +namespace panda::ets::interop::js::ets_proxy { + +/*static*/ +std::unique_ptr EtsMethodWrapper::Create(napi_env env, 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_)); + + return std::unique_ptr(_this); +} + +/*static*/ +EtsMethodWrapper *EtsMethodWrapper::Get(napi_env env, 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; + } + + 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; +} + +/* static */ +napi_property_descriptor EtsMethodWrapper::MakeNapiProperty(Method *method, LazyEtsMethodWrapperLink *lazy_link_space) +{ + napi_callback callback {}; + if (method->IsStatic()) { + callback = EtsMethodWrapper::EtsMethodCallHandler; + } else { + callback = EtsMethodWrapper::EtsMethodCallHandler; + } + + napi_property_descriptor prop {}; + 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; + + return prop; +} + +template +/*static*/ +napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env env, napi_callback_info cinfo) +{ + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + 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); + napi_value js_this; + void *data; + NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, js_args.data(), &js_this, &data)); + + EtsMethodWrapper *_this; // NOLINT(readability-identifier-naming) + + if constexpr (IS_FUNC) { + _this = reinterpret_cast(data); + } else { + auto lazy_link = reinterpret_cast(data); + _this = EtsMethodWrapper::ResolveLazyLink(env, *lazy_link); + if (UNLIKELY(_this == nullptr)) { + return nullptr; + } + } + + Method *method = _this->ets_method_->GetPandaMethod(); + EtsClass *ets_class = _this->ets_method_->GetClass(); + if constexpr (IS_STATIC) { + ScopedManagedCodeThread managed_scope(coro); + if (UNLIKELY(!coro->GetPandaVM()->GetClassLinker()->InitializeClass(coro, ets_class))) { + ctx->ForwardEtsException(coro); + return nullptr; + } + return EtsCallImpl(coro, ctx, method, {js_args.data(), js_args.size()}, nullptr); + } + + EtsClassWrapper *ets_class_wrapper = EtsClassWrapper::Get(env, ets_class); + ScopedManagedCodeThread managed_scope(coro); + std::optional this_ets_object = JSConvertOBJECT::UnWrap(ctx, env, ets_class_wrapper, js_this); + ASSERT(this_ets_object.has_value()); + return EtsCallImpl(coro, ctx, method, {js_args.data(), js_args.size()}, this_ets_object.value()); +} + +// Explicit instantiation +template napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env, napi_callback_info); +template napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env, napi_callback_info); +template napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env, napi_callback_info); +template napi_value EtsMethodWrapper::EtsMethodCallHandler(napi_env, napi_callback_info); + +} // namespace panda::ets::interop::js::ets_proxy 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 new file mode 100644 index 000000000..d8b36df0c --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h @@ -0,0 +1,76 @@ +/** + * 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_METHOD_WRAPPER_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_METHOD_WRAPPER_H_ + +#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/types/ets_method.h" + +#include + +namespace panda::ets::interop::js::ets_proxy { + +class EtsMethodWrapper; + +using LazyEtsMethodWrapperLink = TypedPointer; +using EtsMethodWrappersCache = WrappersCache; + +class EtsMethodWrapper { +public: + static EtsMethodWrapper *Get(napi_env env, EtsMethod *ets_method); + + napi_value GetJsValue() const + { + return js_value_; + } + + EtsMethod *GetEtsMethod() const + { + return ets_method_; + } + + static inline EtsMethodWrapper *ResolveLazyLink(napi_env env, /* 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)) { + return nullptr; + } + // Update lazy_link + lazy_link = LazyEtsMethodWrapperLink(ets_method_wrapper); + return ets_method_wrapper; + } + + static napi_property_descriptor MakeNapiProperty(Method *method, LazyEtsMethodWrapperLink *lazy_link_space); + + template + static napi_value EtsMethodCallHandler(napi_env env, napi_callback_info cinfo); + +private: + static std::unique_ptr Create(napi_env env, EtsMethod *method); + + mutable EtsMethod *ets_method_ {}; + napi_value js_value_ {}; + napi_ref js_ref_ {}; // only for functions (ETSGLOBAL::) +}; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_METHOD_WRAPPER_H_ 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 new file mode 100644 index 000000000..6fad0f6b3 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h @@ -0,0 +1,48 @@ +/** + * 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_OBJECT_REFERENCE_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_OBJECT_REFERENCE_H_ + +#include "libpandabase/macros.h" +#include "libpandabase/utils/logger.h" +#include "plugins/ets/runtime/interop_js/ts2ets_common.h" +#include + +namespace panda::mem { +class Reference; +} // namespace panda::mem + +namespace panda::ets::interop::js::ets_proxy { + +class EtsObjectReferenceDeleter { +public: + constexpr EtsObjectReferenceDeleter() noexcept = default; + ~EtsObjectReferenceDeleter() noexcept = default; + EtsObjectReferenceDeleter(const EtsObjectReferenceDeleter &) noexcept = default; + EtsObjectReferenceDeleter &operator=(const EtsObjectReferenceDeleter &) noexcept = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(EtsObjectReferenceDeleter); + + void operator()(mem::Reference *ref) const noexcept + { + INTEROP_LOG(FATAL) << "UniqueEtsObjectReference shouldn't delete automaticly, ref=" << ref; + } +}; + +using UniqueEtsObjectReference = std::unique_ptr; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_OBJECT_REFERENCE_H_ diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp new file mode 100644 index 000000000..7083433e8 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.cpp @@ -0,0 +1,70 @@ +/** + * 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 "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" + +#include "plugins/ets/runtime/ets_coroutine.h" +#include "plugins/ets/runtime/ets_handle.h" +#include "plugins/ets/runtime/ets_handle_scope.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h" +#include "plugins/ets/runtime/interop_js/interop_context.h" +#include "plugins/ets/runtime/types/ets_object.h" +#include "runtime/include/thread_scopes.h" + +#include + +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) +{ + 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)); + + napi_value proto_ctor; + NAPI_CHECK_FATAL(napi_get_named_property(env, proto, "constructor", &proto_ctor)); + + 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 + } + } +} + +/*static*/ +void EtsObjectWrapper::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); +} + +} // 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/ets_object_wrapper.h new file mode 100644 index 000000000..52c7d2188 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h @@ -0,0 +1,65 @@ +/** + * 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_OBJECT_WRAPPER_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_OBJECT_WRAPPER_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" +#include "plugins/ets/runtime/interop_js/interop_context.h" + +#include + +namespace panda::mem { +class Reference; +} // namespace panda::mem + +namespace panda::ets::interop::js::ets_proxy { + +class EtsClassWrapper; + +class EtsObjectWrapper { +public: + static void CreateInplace(EtsObjectWrapper *mem_this, napi_env env, napi_value js_object, + UniqueEtsObjectReference ets_ref, EtsClassWrapper *ets_class_wrapper); + + mem::Reference *GetEtsRef() + { + return ets_ref_.get(); + } + + EtsObject *GetEtsObject(InteropCtx *ctx) const + { + ASSERT_MANAGED_CODE(); + return EtsObject::FromCoreType(ctx->Refstor()->Get(ets_ref_.get())); + } + + napi_value GetJsValue(napi_env env) const + { + napi_value js_value; + NAPI_CHECK_FATAL(napi_get_reference_value(env, js_ref_, &js_value)); + return js_value; + } + +private: + static void FinalizeCB(napi_env env, void *data, void *hint); + + UniqueEtsObjectReference ets_ref_ {}; + napi_ref js_ref_ {}; +}; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_OBJECT_WRAPPER_H_ diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp new file mode 100644 index 000000000..60ec40c46 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.cpp @@ -0,0 +1,87 @@ +/** + * 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 "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h" + +#include "libpandabase/macros.h" +#include "libpandabase/utils/logger.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrapper.h" +#include "plugins/ets/runtime/types/ets_object.h" + +namespace panda::ets::interop::js::ets_proxy { + +constexpr size_t NUMBER_OF_WRAPPERS = 512; + +EtsObjectWrappersStorage::EtsObjectWrappersStorage() +{ + data_ = new EtsObjectWrapper[NUMBER_OF_WRAPPERS]; + data_end_ = data_ + NUMBER_OF_WRAPPERS; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) +} + +EtsObjectWrapper *EtsObjectWrappersStorage::GetEtsObjectWrapper(EtsObject *ets_object) +{ + ASSERT(ets_object->IsHashed() == true); + + uint32_t idx = ets_object->GetHash(); + ASSERT(idx < NUMBER_OF_WRAPPERS); + return GetWrapperByIndex(idx); +} + +EtsObjectWrapper *EtsObjectWrappersStorage::GetEtsObjectWrapperOrNull(void *data) +{ + if (UNLIKELY(!IsAligned(uintptr_t(data)))) { + return nullptr; + } + + auto *ets_object_wrapper = reinterpret_cast(data); + if (UNLIKELY(ets_object_wrapper >= data_end_)) { + return nullptr; + } + + return ets_object_wrapper; +} + +EtsObjectWrapper *EtsObjectWrappersStorage::CreateObjectEtsWrapper(napi_env env, EtsObject *ets_object, + napi_value js_object, + UniqueEtsObjectReference ets_ref, + EtsClassWrapper *ets_class_wrapper) +{ + ASSERT(ets_object->IsHashed() == false); + + uint32_t new_idx = ++next_; + if (new_idx >= NUMBER_OF_WRAPPERS) { + // Out of memory to create EtsObjectWrapper + return nullptr; + } + + ets_object->SetHash(new_idx); + EtsObjectWrapper *mem = GetWrapperByIndex(new_idx); + + EtsObjectWrapper::CreateInplace(mem, env, js_object, std::move(ets_ref), ets_class_wrapper); + return mem; +} + +void EtsObjectWrappersStorage::RemoveEtsObjectWrapper(EtsObjectWrapper *ets_object_wrapper) +{ + (void)ets_object_wrapper; + INTEROP_LOG(ERROR) << "EtsObjectWrappersStorage::RemoveEtsObjectWrapper: Not implemented"; +} + +inline EtsObjectWrapper *EtsObjectWrappersStorage::GetWrapperByIndex(uint32_t idx) +{ + return &data_[idx]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) +} + +} // 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/ets_object_wrappers_storage.h new file mode 100644 index 000000000..7c0c521d3 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_object_wrappers_storage.h @@ -0,0 +1,63 @@ +/** + * 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_ETS_OBJECT_WRAPPERS_STORAGE_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_ETS_OBJECT_WRAPPERS_STORAGE_H_ + +#include "libpandabase/macros.h" +#include "libpandabase/utils/logger.h" + +#include "plugins/ets/runtime/types/ets_object.h" +#include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h" + +#include + +namespace panda::ets { +class EtsObject; +} // namespace panda::ets + +namespace panda::ets::interop::js::ets_proxy { + +class EtsClassWrapper; +class EtsObjectWrapper; + +class EtsObjectWrappersStorage { +public: + EtsObjectWrappersStorage(); + + EtsObjectWrapper *GetEtsObjectWrapper(EtsObject *ets_object); + + inline bool HasEtsObjectWrapper(EtsObject *ets_object) + { + return ets_object->IsHashed(); + } + + EtsObjectWrapper *GetEtsObjectWrapperOrNull(void *data); + EtsObjectWrapper *CreateObjectEtsWrapper(napi_env env, EtsObject *ets_object, napi_value js_object, + UniqueEtsObjectReference ets_ref, EtsClassWrapper *ets_class_wrapper); + + void RemoveEtsObjectWrapper(EtsObjectWrapper *ets_object_wrapper); + +private: + inline EtsObjectWrapper *GetWrapperByIndex(uint32_t idx); + + EtsObjectWrapper *data_; + EtsObjectWrapper *data_end_; + uint32_t next_ {0}; +}; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_ETS_OBJECT_WRAPPERS_STORAGE_H_ diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp b/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp new file mode 100644 index 000000000..f0d1fc015 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.cpp @@ -0,0 +1,69 @@ +/** + * 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 "plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.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/interop_context.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(); + ScopedManagedCodeThread managed_scope(coro); + + EtsClass *ets_class = coro->GetPandaVM()->GetClassLinker()->GetClass(class_descriptor.data()); + if (UNLIKELY(ets_class == nullptr)) { + InteropCtx::ThrowJSError(env, "GetETSFunction: unresolved class " + std::string(class_descriptor)); + return nullptr; + } + + EtsMethod *ets_method = ets_class->GetDirectMethod(method_name.data()); + if (UNLIKELY(ets_method == nullptr)) { + InteropCtx::ThrowJSError(env, "GetETSFunction: class " + std::string(class_descriptor) + " doesn't contain " + + std::string(method_name) + " method"); + return nullptr; + } + + EtsMethodWrapper *ets_method_wrapper = EtsMethodWrapper::Get(env, 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(); +} + +napi_value GetETSClass(napi_env env, std::string_view class_descriptor) +{ + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + 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); + if (UNLIKELY(ets_class_wrapper == nullptr)) { + return nullptr; + } + + return ets_class_wrapper->GetJsCtor(env); +} + +} // namespace panda::ets::interop::js::ets_proxy diff --git a/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h b/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h new file mode 100644 index 000000000..e119b8b1c --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/ets_proxy.h @@ -0,0 +1,29 @@ +/** + * 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_ETS_PROXY_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_ETS_PROXY_H_ + +#include +#include + +namespace panda::ets::interop::js::ets_proxy { + +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); + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_ETS_PROXY_H_ 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 new file mode 100644 index 000000000..8cf5b6595 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/js_convert_object.h @@ -0,0 +1,107 @@ +/** + * 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(); + UniqueEtsObjectReference ets_ref(ctx->Refstor()->Add(obj, mem::Reference::ObjectType::GLOBAL)); + 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 new file mode 100644 index 000000000..7f161b38c --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/lazy_ets_class_wrapper_link.h @@ -0,0 +1,31 @@ +/** + * 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/typed_pointer.h b/plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h new file mode 100644 index 000000000..cc774b89a --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/typed_pointer.h @@ -0,0 +1,83 @@ +/** + * 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_TYPED_POINTER_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_TYPED_POINTER_H_ + +#include "libpandabase/macros.h" + +namespace panda::ets::interop::js::ets_proxy { + +template +struct TypedPointer { + TypedPointer() : TypedPointer(static_cast(nullptr)) {} + + explicit TypedPointer(U *ptr) + { + static_assert(alignof(U) >= (1U << 1U)); + Set(ptr, true); + ASSERT(!IsResolved()); + } + explicit TypedPointer(R *ptr) + { + static_assert(alignof(R) >= (1U << 1U)); + Set(ptr, false); + ASSERT(IsResolved()); + } + + bool IsResolved() const + { + return !GetTag(); + } + + U *GetUnresolved() const + { + ASSERT(!IsResolved()); + return reinterpret_cast(GetUPtr()); + } + + R *GetResolved() const + { + ASSERT(IsResolved()); + return reinterpret_cast(ptr_); + } + +private: + uintptr_t static constexpr MASK = ~static_cast(1); + + bool GetTag() const + { + return static_cast(ptr_ & ~MASK); + } + + uintptr_t GetUPtr() const + { + return ptr_ & MASK; + } + + template + void Set(T *ptr, bool tag) + { + ptr_ = reinterpret_cast(ptr); + ASSERT(!GetTag()); + ptr_ |= static_cast(tag); + } + + uintptr_t ptr_ {}; +}; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_TYPED_POINTER_H_ diff --git a/plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h b/plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h new file mode 100644 index 000000000..477700661 --- /dev/null +++ b/plugins/ets/runtime/interop_js/ets_proxy/wrappers_cache.h @@ -0,0 +1,63 @@ +/** + * 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_WRAPPERS_CACHE_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_WRAPPERS_CACHE_H_ + +#include "libpandabase/macros.h" + +#include +#include + +namespace panda::ets::interop::js::ets_proxy { + +template +class WrappersCache { +public: + WrappersCache() = default; + ~WrappersCache() = default; + NO_COPY_SEMANTIC(WrappersCache); + NO_MOVE_SEMANTIC(WrappersCache); + + Wrapper *Lookup(Key key) + { + auto it = cache_.find(key); + if (UNLIKELY(it == cache_.end())) { + return nullptr; + } + return it->second.get(); + } + + Wrapper *Insert(Key key, std::unique_ptr &&wrapper) + { + ASSERT(Lookup(key) == nullptr); + auto [it, inserted] = cache_.insert_or_assign(key, std::move(wrapper)); + ASSERT(inserted); + return it->second.get(); + } + + void Remove(Key key) + { + ASSERT(Lookup(key) != nullptr); + cache_.erase(key); + } + +private: + std::unordered_map> cache_; +}; + +} // namespace panda::ets::interop::js::ets_proxy + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_PROXY_WRAPPERS_CACHE_H_ diff --git a/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp b/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp index 9ac7791df..16e50f611 100644 --- a/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp +++ b/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp @@ -17,6 +17,7 @@ #include "plugins/ets/runtime/ets_panda_file_items.h" #include "plugins/ets/runtime/ets_vm_api.h" #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/ts2ets_copy.h" @@ -49,6 +50,55 @@ static napi_value Version(napi_env env, [[maybe_unused]] napi_callback_info info return result; } +static napi_value GetEtsFunction(napi_env env, napi_callback_info info) +{ + size_t js_argc = 0; + NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &js_argc, nullptr, nullptr, nullptr)); + if (js_argc != 2) { + InteropCtx::ThrowJSError(env, "GetEtsFunction: bad args, actual args count: " + std::to_string(js_argc)); + return nullptr; + } + + std::array js_argv {}; + ASSERT(js_argc == js_argv.size()); + NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &js_argc, js_argv.data(), nullptr, nullptr)); + + napi_value js_class_descriptor = js_argv[0]; + napi_value js_function_name = js_argv[1]; + + if (GetValueType(env, js_class_descriptor) != napi_string) { + InteropCtx::ThrowJSError(env, "GetEtsFunction: class descriptor is not a string"); + } + + if (GetValueType(env, js_function_name) != napi_string) { + InteropCtx::ThrowJSError(env, "GetEtsFunction: function name is not a string"); + return nullptr; + } + + std::string class_descriptor = GetString(env, js_class_descriptor); + std::string method_name = GetString(env, js_function_name); + + return ets_proxy::GetETSFunction(env, class_descriptor, method_name); +} + +static napi_value GetEtsClass(napi_env env, napi_callback_info info) +{ + size_t js_argc = 0; + NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &js_argc, nullptr, nullptr, nullptr)); + + if (js_argc != 1) { + InteropCtx::ThrowJSError(env, "GetEtsClass: bad args, actual args count: " + std::to_string(js_argc)); + return nullptr; + } + + napi_value js_class_descriptor {}; + ASSERT(js_argc == 1); + NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &js_argc, &js_class_descriptor, nullptr, nullptr)); + + std::string class_descriptor = GetString(env, js_class_descriptor); + return ets_proxy::GetETSClass(env, class_descriptor); +} + static napi_value Call(napi_env env, napi_callback_info info) { size_t argc = 0; @@ -111,14 +161,14 @@ static napi_value CreateEtsRuntime(napi_env env, napi_callback_info info) LogError("CreateEtsRuntime: first argument is not a string"); return napi_false; } - auto index_path = GetString(env, argv[0]); + auto stdlib_path = GetString(env, argv[0]); napi_typeof(env, argv[1], &type); if (type != napi_string) { LogError("CreateEtsRuntime: second argument is not a string"); return napi_false; } - auto stdlib_path = GetString(env, argv[1]); + auto index_path = GetString(env, argv[1]); napi_typeof(env, argv[2], &type); if (type != napi_boolean) { @@ -229,35 +279,6 @@ static napi_value CreateRuntime(napi_env env, napi_callback_info info) return napi_res; } -static napi_value RegisterETSFunction(napi_env env, napi_callback_info info) -{ - size_t argc = 0; - [[maybe_unused]] napi_status status; - NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr)); - - auto coro = EtsCoroutine::GetCurrent(); - auto &argv = InteropCtx::Current(coro)->GetTempArgs(argc); - napi_value this_arg {}; - void *data = nullptr; - NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &argc, argv.data(), &this_arg, &data)); - - return RegisterETSFunctionImpl(env, argv.data(), argc); -} - -static napi_value RegisterETSClass(napi_env env, napi_callback_info info) -{ - size_t argc = 0; - NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr)); - - auto coro = EtsCoroutine::GetCurrent(); - auto &argv = InteropCtx::Current(coro)->GetTempArgs(argc); - napi_value this_arg {}; - void *data = nullptr; - NAPI_CHECK_FATAL(napi_get_cb_info(env, info, &argc, argv.data(), &this_arg, &data)); - - return RegisterETSClassImpl(env, argv.data(), argc); -} - static napi_value Init(napi_env env, napi_value exports) { const std::array desc = { @@ -266,8 +287,8 @@ static napi_value Init(napi_env env, napi_value exports) napi_property_descriptor {"callWithCopy", 0, CallWithCopy, 0, 0, 0, napi_enumerable, 0}, napi_property_descriptor {"createEtsRuntime", 0, CreateEtsRuntime, 0, 0, 0, napi_enumerable, 0}, napi_property_descriptor {"createRuntime", 0, CreateRuntime, 0, 0, 0, napi_enumerable, 0}, - napi_property_descriptor {"registerEtsFunction", 0, RegisterETSFunction, 0, 0, 0, napi_enumerable, 0}, - napi_property_descriptor {"registerEtsClass", 0, RegisterETSClass, 0, 0, 0, napi_enumerable, 0}, + napi_property_descriptor {"getFunction", 0, GetEtsFunction, 0, 0, 0, napi_enumerable, 0}, + napi_property_descriptor {"getClass", 0, GetEtsClass, 0, 0, 0, napi_enumerable, 0}, }; NAPI_CHECK_FATAL(napi_define_properties(env, exports, desc.size(), desc.data())); diff --git a/plugins/ets/runtime/interop_js/interop_context.h b/plugins/ets/runtime/interop_js/interop_context.h index f55ae533b..75e3021e4 100644 --- a/plugins/ets/runtime/interop_js/interop_context.h +++ b/plugins/ets/runtime/interop_js/interop_context.h @@ -16,18 +16,21 @@ #ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_CONTEXT_H_ #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_CONTEXT_H_ -#include "plugins/ets/runtime/ets_vm.h" +#include "libpandabase/macros.h" #include "plugins/ets/runtime/ets_coroutine.h" +#include "plugins/ets/runtime/ets_vm.h" #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/js_job_queue.h" #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h" -#include "libpandabase/macros.h" +#include "plugins/ets/runtime/interop_js/intrinsics/std_js_jsruntime.h" #include "runtime/include/value.h" + #include #include -#include "plugins/ets/runtime/interop_js/intrinsics/std_js_jsruntime.h" - namespace panda { class Class; @@ -209,6 +212,31 @@ public: Fatal(msg.c_str()); } + void SetNewInstance(ets_proxy::UniqueEtsObjectReference new_instance) + { + new_instance_ = std::move(new_instance); + } + + ets_proxy::UniqueEtsObjectReference GetNewInstance() + { + return std::move(new_instance_); + } + + ets_proxy::EtsMethodWrappersCache *GetEtsMethodWrappersCache() + { + return &ets_method_wrappers_cache_; + } + + ets_proxy::EtsClassWrappersCache *GetEtsClassWrappersCache() + { + return &ets_class_wrappers_cache_; + } + + ets_proxy::EtsObjectWrappersStorage *GetEtsObjectWrappersStorage() + { + return &ets_object_wrappers_storage_; + } + private: explicit InteropCtx(EtsCoroutine *coro); @@ -232,6 +260,12 @@ private: Method *jsvalue_fqueue_register_ {}; + // ets_proxy data + ets_proxy::UniqueEtsObjectReference new_instance_ {}; + ets_proxy::EtsMethodWrappersCache ets_method_wrappers_cache_ {}; + ets_proxy::EtsClassWrappersCache ets_class_wrappers_cache_ {}; + ets_proxy::EtsObjectWrappersStorage ets_object_wrappers_storage_ {}; + friend class EtsJSNapiEnvScope; }; diff --git a/plugins/ets/runtime/interop_js/js_refconvert.h b/plugins/ets/runtime/interop_js/js_refconvert.h index da3b4930a..06faf52fe 100644 --- a/plugins/ets/runtime/interop_js/js_refconvert.h +++ b/plugins/ets/runtime/interop_js/js_refconvert.h @@ -78,7 +78,7 @@ public: return entry->value; } auto value = LookupFull(klass); - *entry = {klass, value}; + *entry = {klass, value}; // Update dircache_ return value; } diff --git a/plugins/ets/runtime/interop_js/js_value_call.cpp b/plugins/ets/runtime/interop_js/js_value_call.cpp index dcee95a0e..61002261f 100644 --- a/plugins/ets/runtime/interop_js/js_value_call.cpp +++ b/plugins/ets/runtime/interop_js/js_value_call.cpp @@ -15,6 +15,7 @@ #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" @@ -171,6 +172,19 @@ static ALWAYS_INLINE inline void ConvertEtsVal(InteropCtx *ctx, [[maybe_unused]] } // start slowpath auto refconv = JSRefConvertResolve(ctx, klass); + if (UNLIKELY(refconv == nullptr)) { + // NOTE: + // Create ets_proxy::EtsClassWrapper for klass that automatically adds to RefConvertCache + EtsClass *ets_class = EtsClass::FromRuntimeClass(klass); + ets_proxy::EtsClassWrapper *ets_class_wrapper = ets_proxy::EtsClassWrapper::Get(env, ets_class); + if (UNLIKELY(ets_class_wrapper == nullptr)) { + INTEROP_LOG(FATAL) << "ConvertEtsVal: Cannot create EtsClassWrapper for class: " + << ets_class->GetDescriptor(); + } + + refconv = JSRefConvertResolve(ctx, klass); + ASSERT(refconv != nullptr); + } store_res(refconv->Wrap(ctx, EtsObject::FromCoreType(ref))); return; } @@ -185,10 +199,10 @@ static ALWAYS_INLINE inline void ConvertEtsVal(InteropCtx *ctx, [[maybe_unused]] using ArgValueBox = std::variant; template -static ALWAYS_INLINE inline napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, - Span jsargv, mem::Reference *ets_fnobj_ref) +napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span jsargv, + EtsObject *this_ets_object) { - ScopedManagedCodeThread managed_scope(coro); + ASSERT_MANAGED_CODE(); auto class_linker = Runtime::GetCurrent()->GetClassLinker(); @@ -222,8 +236,12 @@ static ALWAYS_INLINE inline napi_value EtsCallImpl(EtsCoroutine *coro, InteropCt auto cls_resolver = [&]() { return resolve_ref_cls(ref_arg_idx++); }; auto store_prim = [&](uint64_t val) { ets_boxed_args[arg_idx] = val; }; auto store_ref = [&](ObjectHeader *obj) { - uintptr_t addr = VMHandle(coro, obj).GetAddress(); - ets_boxed_args[arg_idx] = reinterpret_cast(addr); + ObjectHeader **ref = nullptr; + if (obj != nullptr) { + uintptr_t addr = VMHandle(coro, obj).GetAddress(); + ref = reinterpret_cast(addr); + } + ets_boxed_args[arg_idx] = ref; }; if (UNLIKELY(!ConvertNapiVal(ctx, cls_resolver, store_prim, store_ref, type, js_val))) { INTEROP_LOG(DEBUG) << "EtsCall: exit with pending exception"; @@ -235,17 +253,22 @@ static ALWAYS_INLINE inline napi_value EtsCallImpl(EtsCoroutine *coro, InteropCt for (size_t i = 0; i < num_args; ++i) { auto const &box = ets_boxed_args[i]; if (std::holds_alternative(box)) { - ASSERT(std::get<1>(box) != nullptr); - ets_args[ETS_ARGS_DISP + i] = Value(*std::get<1>(box)); + ObjectHeader **ref = std::get<1>(box); + ObjectHeader *obj = nullptr; + if (ref != nullptr) { + obj = *ref; + } + ets_args[ETS_ARGS_DISP + i] = Value(obj); } else { ets_args[ETS_ARGS_DISP + i] = Value(std::get<0>(box)); } } } if constexpr (!IS_STATIC) { - ets_args[0] = Value(ctx->Refstor()->Get(ets_fnobj_ref)); + ets_args[0] = Value(this_ets_object->GetCoreType()); } else { - (void)ets_fnobj_ref; + (void)this_ets_object; + ASSERT(this_ets_object == nullptr); } Value ets_res = method->Invoke(coro, ets_args.data()); @@ -267,6 +290,12 @@ static ALWAYS_INLINE inline napi_value EtsCallImpl(EtsCoroutine *coro, InteropCt return js_res; } +// Explicit instantiation +template napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span jsargv, + EtsObject *this_ets_object); +template napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span jsargv, + EtsObject *this_ets_object); + napi_value CallEtsFunctionImpl(napi_env env, Span jsargv) { auto coro = EtsCoroutine::GetCurrent(); @@ -300,7 +329,8 @@ napi_value CallEtsFunctionImpl(napi_env env, Span jsargv) return nullptr; } - auto js_ret = EtsCallImpl(coro, ctx, method, jsargv.SubSpan(1), nullptr); + ScopedManagedCodeThread managed_scope(coro); + auto js_ret = EtsCallImpl(coro, ctx, method, jsargv.SubSpan(1), nullptr); INTEROP_LOG(DEBUG) << "CallEtsFunction: exit"; return js_ret; } @@ -330,8 +360,10 @@ napi_value EtsLambdaProxyInvoke(napi_env env, napi_callback_info cbinfo) return nullptr; } + ScopedManagedCodeThread managed_scope(coro); auto js_args_span = Span(js_args.data(), js_args.size()); - auto js_ret = EtsCallImpl(coro, ctx, method->GetPandaMethod(), js_args_span, ets_ref); + auto this_ets_object = EtsObject::FromCoreType(ctx->Refstor()->Get(ets_ref)); + auto js_ret = EtsCallImpl(coro, ctx, method->GetPandaMethod(), js_args_span, this_ets_object); INTEROP_LOG(DEBUG) << "EtsProxyInvoke: exit"; return js_ret; } diff --git a/plugins/ets/runtime/interop_js/js_value_call.h b/plugins/ets/runtime/interop_js/js_value_call.h index 135b2305f..94645525d 100644 --- a/plugins/ets/runtime/interop_js/js_value_call.h +++ b/plugins/ets/runtime/interop_js/js_value_call.h @@ -19,11 +19,22 @@ #include #include "utils/span.h" +namespace panda { +class Method; +} // namespace panda + +namespace panda::mem { +class Reference; +} // namespace panda::mem + namespace panda::ets { +class EtsObject; class EtsString; +class EtsCoroutine; } // namespace panda::ets namespace panda::ets::interop::js { +class InteropCtx; napi_value CallEtsFunctionImpl(napi_env env, Span jsargv); napi_value EtsLambdaProxyInvoke(napi_env env, napi_callback_info cbinfo); @@ -31,6 +42,10 @@ napi_value EtsLambdaProxyInvoke(napi_env env, napi_callback_info cbinfo); uint8_t JSRuntimeInitJSCallClass(EtsString *cls_str); uint8_t JSRuntimeInitJSNewClass(EtsString *cls_str); +template +napi_value EtsCallImpl(EtsCoroutine *coro, InteropCtx *ctx, Method *method, Span jsargv, + EtsObject *this_ets_object); + } // namespace panda::ets::interop::js #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JSVALUE_CALL_H_ diff --git a/plugins/ets/runtime/interop_js/ts2ets_common.h b/plugins/ets/runtime/interop_js/ts2ets_common.h index 6cc14ea19..271e97eb6 100644 --- a/plugins/ets/runtime/interop_js/ts2ets_common.h +++ b/plugins/ets/runtime/interop_js/ts2ets_common.h @@ -209,16 +209,16 @@ inline napi_valuetype GetValueType(napi_env env, napi_value val) return vtype; } -inline bool IsUndefined(napi_env env, napi_value val) +inline napi_value GetNull(napi_env env) { - return GetValueType(env, val) == napi_undefined; + napi_value js_value_null {}; + NAPI_CHECK_FATAL(napi_get_null(env, &js_value_null)); + return js_value_null; } -inline napi_value GetNull(napi_env env) +inline bool IsUndefined(napi_env env, napi_value val) { - napi_value val; - NAPI_CHECK_FATAL(napi_get_null(env, &val)); - return val; + return GetValueType(env, val) == napi_undefined; } inline bool IsNullOrUndefined(napi_env env, napi_value val) diff --git a/plugins/ets/runtime/types/ets_method.h b/plugins/ets/runtime/types/ets_method.h index 2d3853c5b..178b9824f 100644 --- a/plugins/ets/runtime/types/ets_method.h +++ b/plugins/ets/runtime/types/ets_method.h @@ -94,6 +94,11 @@ public: return utf::Mutf8AsCString(GetPandaMethod()->GetName().data); } + PandaString GetFullName(bool with_signature = false) const + { + return GetPandaMethod()->GetFullName(with_signature); + } + EtsString *GetNameString() { auto name_data = GetPandaMethod()->GetName(); diff --git a/plugins/ets/runtime/types/ets_object.h b/plugins/ets/runtime/types/ets_object.h index 12c883339..545555d3f 100644 --- a/plugins/ets/runtime/types/ets_object.h +++ b/plugins/ets/runtime/types/ets_object.h @@ -189,9 +189,24 @@ public: return GetClass()->IsArrayClass(); } - uint32_t GetHashCode() + uint32_t GetHashCode() = delete; + + inline bool IsHashed() const + { + return GetMark().GetState() == MarkWord::STATE_HASHED; + } + + inline uint32_t GetHash() const + { + ASSERT(IsHashed()); + return GetMark().GetHash(); + } + + inline void SetHash(uint32_t hash) { - return GetCoreType()->GetHashCode(); + MarkWord mark = GetMark().DecodeFromHash(hash); + ASSERT(mark.GetState() == MarkWord::STATE_HASHED); + SetMark(mark); } EtsObject() = delete; diff --git a/plugins/ets/tests/interop_js/cmake/interop_js_tests.cmake b/plugins/ets/tests/interop_js/cmake/interop_js_tests.cmake index 81a202dd3..de61cc2a7 100644 --- a/plugins/ets/tests/interop_js/cmake/interop_js_tests.cmake +++ b/plugins/ets/tests/interop_js/cmake/interop_js_tests.cmake @@ -69,9 +69,11 @@ function(panda_ets_interop_js_gtest TARGET) NO_EXECUTABLE NO_CORES CUSTOM_PRERUN_ENVIRONMENT + "NODE_PATH=${PANDA_BINARY_ROOT}:${CMAKE_CURRENT_SOURCE_DIR}" "ARK_ETS_STDLIB_PATH=${PANDA_BINARY_ROOT}/plugins/ets/etsstdlib.abc" "ARK_ETS_INTEROP_JS_GTEST_ABC_PATH=${PANDA_BINARY_ROOT}/abc-gtests/${TARGET_GTEST_PACKAGE}.zip" - "ARK_ETS_INTEROP_JS_GTEST_SOURCES=${PANDA_ETS_PLUGIN_SOURCE}/tests/interop_js/tests/" + "ARK_ETS_INTEROP_JS_GTEST_SOURCES=${CMAKE_CURRENT_SOURCE_DIR}" + "ARR_ETS_INTEROP_JS_GTEST_DIR=${INTEROP_TESTS_DIR}" LAUNCHER ${NODE_BINARY} gtest_launcher.js ${TARGET} DEPS_TARGETS ${TARGET} ets_interop_js_gtest_launcher TEST_RUN_DIR ${INTEROP_TESTS_DIR} diff --git a/plugins/ets/tests/interop_js/gtest_plugin/CMakeLists.txt b/plugins/ets/tests/interop_js/gtest_plugin/CMakeLists.txt index eddcf549d..1bcbbe265 100644 --- a/plugins/ets/tests/interop_js/gtest_plugin/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/gtest_plugin/CMakeLists.txt @@ -26,13 +26,13 @@ else() endif() -# Copy gtest_launcher.js to /tests/ets_interop_js/ +# Link gtest_launcher.js to /tests/ets_interop_js/ set(GTEST_LAUNCHER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/gtest_launcher.js") set(GTEST_LAUNCHER "${PANDA_BINARY_ROOT}/tests/ets_interop_js/gtest_launcher.js") add_custom_command( OUTPUT ${GTEST_LAUNCHER} - COMMENT "Copy ${GTEST_LAUNCHER_SOURCE} to ${GTEST_LAUNCHER}" - COMMAND cp ${GTEST_LAUNCHER_SOURCE} ${GTEST_LAUNCHER} + COMMENT "Link ${GTEST_LAUNCHER_SOURCE} to ${GTEST_LAUNCHER}" + COMMAND ln -sf ${GTEST_LAUNCHER_SOURCE} ${GTEST_LAUNCHER} DEPENDS ${GTEST_LAUNCHER_SOURCE} ) add_custom_target(ets_interop_js_gtest_launcher diff --git a/plugins/ets/tests/interop_js/gtest_plugin/ets_interop_js_gtest.h b/plugins/ets/tests/interop_js/gtest_plugin/ets_interop_js_gtest.h index 608b45f85..57ed4af98 100644 --- a/plugins/ets/tests/interop_js/gtest_plugin/ets_interop_js_gtest.h +++ b/plugins/ets/tests/interop_js/gtest_plugin/ets_interop_js_gtest.h @@ -51,6 +51,11 @@ public: return DoRunJsScriptByPath(js_env_, path); } + void RunJsTestSute(const std::string &path) + { + DoRunJsTestSute(js_env_, path); + } + template static R CallEtsMethod(std::string_view fn_name, Args &&...args) { @@ -64,11 +69,22 @@ public: void LoadModuleAs(const std::string &module_name, const std::string &module_path) { - RunJsScript("gtest_env." + module_name + " = gtest.require(\"" + interop_js_test_path_ + module_path + + RunJsScript("gtest_env." + module_name + " = require(\"" + interop_js_test_path_ + "/" + module_path + "\");\n"); } private: + void DoRunJsTestSute(napi_env env, const std::string &path) + { + std::string full_path = interop_js_test_path_ + "/" + path; + std::string test_souce_code = ReadFile(full_path); + + // NOTE: + // We wrap the test source code to anonymous lambda function to avoid pollution the global namespace. + // We also set the 'use strict' mode, because right now we are checking interop only in the strict mode. + DoRunJsScript(env, "(() => { \n'use strict'\n" + test_souce_code + "\n})()"); + } + static void DoRunJsScript(napi_env env, const std::string &script) { [[maybe_unused]] napi_status status; @@ -89,23 +105,34 @@ private: // Unreachable code std::abort(); } - ASSERT_EQ(status, napi_ok); + assert(status == napi_ok); } - template - T DoRunJsScriptByPath(napi_env env, const std::string &path) + static std::string ReadFile(const std::string &full_path) { - std::ifstream js_source_file(interop_js_test_path_ + path); - assert(js_source_file.is_open()); + std::ifstream js_source_file(full_path); + if (!js_source_file.is_open()) { + std::cerr << __func__ << ": Cannot open file: " << full_path << std::endl; + std::abort(); + } std::ostringstream string_stream; string_stream << js_source_file.rdbuf(); + return string_stream.str(); + } + + template + T DoRunJsScriptByPath(napi_env env, const std::string &path) + { + std::string full_path = interop_js_test_path_ + "/" + path; - DoRunJsScript(env, string_stream.str()); + DoRunJsScript(env, ReadFile(full_path)); - // Get globalThis.gtest.ret + [[maybe_unused]] napi_status status {}; napi_value js_ret_value {}; - [[maybe_unused]] napi_status status = napi_get_named_property(env, GetJsGtestObject(env), "ret", &js_ret_value); + status = napi_get_named_property(env, GetJsGtestObject(env), "ret", &js_ret_value); assert(status == napi_ok); + + // Get globalThis.gtest.ret return GetRetValue(env, js_ret_value); } 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 125769f9d..e7ffcdaec 100644 --- a/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js +++ b/plugins/ets/tests/interop_js/gtest_plugin/gtest_launcher.js @@ -13,26 +13,25 @@ * limitations under the License. */ +globalThis.ASSERT_EQ = function(v0, v1) { + if (v0 !== v1) { + let msg = `ASSERT FAILED: ${v0}[${typeof v0}] !== ${v1}[${typeof v1}]`; + console.error(msg); + throw Error(msg); + } +} function main() { - // Init 'gtestRootPath' - let gtestRootPath = (() => { - let path = process.env.ETS_GTEST_ROOT_PATH; - if (path == undefined) { - return __dirname + "/../.."; - } - return path; - })(); - // Add 'gtest' object to global space. // This object is used by gtests as storage to save and restore variables - gtest = {} + globalThis.gtest = {}; + globalThis.gtest.ret = 0; // load ets_interop_js_napi to globalThis.gtest.etsVm - gtest.etsVm = require(gtestRootPath + "/lib/module/ets_interop_js_napi"); + globalThis.gtest.etsVm = require("lib/module/ets_interop_js_napi"); let penv = process.env; - const etsVmRes = gtest.etsVm.createRuntime({ + const etsVmRes = globalThis.gtest.etsVm.createRuntime({ "log-level": "info", "log-components": "ets_interop_js", "boot-panda-files": penv.ARK_ETS_STDLIB_PATH + ":" + penv.ARK_ETS_INTEROP_JS_GTEST_ABC_PATH, @@ -48,8 +47,7 @@ function main() { return 1; } - // 'gtest.require' is used by gtests to load the node modules - gtest.require = require; + // 'globalThis.require' is used by gtests to load the node modules globalThis.require = require; let gtestName = process.argv[2]; @@ -58,11 +56,16 @@ function main() { return 1; } + let gtestDir = process.env.ARR_ETS_INTEROP_JS_GTEST_DIR; + if (gtestDir == undefined) { + throw Error(`ARR_ETS_INTEROP_JS_GTEST_DIR is not set`); + } + // Run gtest console.log(`Run ets_interop_js_gtest module: ${gtestName}`) - var ets_gtest = require(`./lib/module/${gtestName}`); + var etsGtest = require(`${gtestDir}/lib/module/${gtestName}`); let args = process.argv.slice(2); - let ret = ets_gtest.main(args); + let ret = etsGtest.main(args); return ret; } diff --git a/plugins/ets/tests/interop_js/tests/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/CMakeLists.txt index 71b2516b2..3b6caafbe 100644 --- a/plugins/ets/tests/interop_js/tests/CMakeLists.txt +++ b/plugins/ets/tests/interop_js/tests/CMakeLists.txt @@ -76,3 +76,4 @@ panda_ets_interop_js_gtest(ets_interop_js_frontend_class_operations add_subdirectory(promise) add_subdirectory(proxy_types) +add_subdirectory(ets_proxy) diff --git a/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt b/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt new file mode 100644 index 000000000..7e16e2287 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2021-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. + +add_subdirectory(call_ets_function) +add_subdirectory(use_ets_class) 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/call_ets_function/CMakeLists.txt new file mode 100644 index 000000000..ed48d807a --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/CMakeLists.txt @@ -0,0 +1,17 @@ +# 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. + +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 +) 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 new file mode 100644 index 000000000..088b17057 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/call_ets_function.test.js @@ -0,0 +1,28 @@ +/** + * 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/call_ets_function/check_objects.js b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_objects.js new file mode 100644 index 000000000..2ab42c3b7 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_objects.js @@ -0,0 +1,28 @@ +/** + * 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_create, + point_get_x, + point_get_y +} = require("call_ets_function.test.js") + + +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_primitives.js b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_primitives.js new file mode 100644 index 000000000..6280bbcd1 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/check_primitives.js @@ -0,0 +1,18 @@ +/** + * 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 { sum_double } = require("call_ets_function.test.js") + +ASSERT_EQ(sum_double(3, 4), 3 + 4); 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/call_ets_function/test_call_ets_function.cpp new file mode 100644 index 000000000..0a45f129e --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.cpp @@ -0,0 +1,34 @@ +/** + * 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 CallEtsFunctionTest : public EtsInteropTest {}; + +TEST_F(CallEtsFunctionTest, check_primitives) +{ + RunJsTestSute("check_primitives.js"); +} + +// TODO(v.cherkashin): Enable when implemented, #12573 +TEST_F(CallEtsFunctionTest, DISABLED_check_objects) +{ + RunJsTestSute("check_objects.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/call_ets_function/test_call_ets_function.ets new file mode 100644 index 000000000..85dd65c9d --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/call_ets_function/test_call_ets_function.ets @@ -0,0 +1,43 @@ +/** + * 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 call_ets_function.test; + + +class Point { + constructor(x: double, y: double) { + this.x = x; + this.y = y; + } + public x: double; + public y: double; +} + +function point_create(x: double, y: double): Point { + return new Point(x, y) +} + +function point_get_x(p: Point): double { + return p.x; +} + +function point_get_y(p: Point): double { + return p.y; +} + + +function sum_double(v0: double, v1: double): double { + return v0 + v1; +} 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/use_ets_class/CMakeLists.txt new file mode 100644 index 000000000..647f18a1f --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/CMakeLists.txt @@ -0,0 +1,17 @@ +# 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. + +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 +) 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 new file mode 100644 index 000000000..044ce43bb --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_builtin_fields.js @@ -0,0 +1,28 @@ +/** + * 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 new file mode 100644 index 000000000..1edf86f9b --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_primitive_fields.js @@ -0,0 +1,59 @@ +/** + * 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 new file mode 100644 index 000000000..479917023 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_reference_fields.js @@ -0,0 +1,30 @@ +/** + * 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 new file mode 100644 index 000000000..293d4082f --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_primitive_fields.js @@ -0,0 +1,56 @@ +/** + * 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 new file mode 100644 index 000000000..de4a2bea4 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_access_to_static_reference_fields.js @@ -0,0 +1,67 @@ +/** + * 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 new file mode 100644 index 000000000..40fefbedf --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_primitives.js @@ -0,0 +1,28 @@ +/** + * 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 new file mode 100644 index 000000000..57281735e --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_call_with_references.js @@ -0,0 +1,24 @@ +/** + * 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_inheritance.js b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_inheritance.js new file mode 100644 index 000000000..d4b1c7600 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_inheritance.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 { Point } = require("use_ets_class.test.js") + + +class Point3D extends Point { + constructor(x, y, z) { + super(x, y) + + this.z = z; + } + get_z() { + return this.z; + } +} + +let point3d = new Point3D(6, 9, 7); + +ASSERT_EQ(point3d.x, 6); +ASSERT_EQ(point3d.y, 9); +ASSERT_EQ(point3d.z, 7); + +point3d.x = 10; +point3d.y = 33; +point3d.z = 16; + +ASSERT_EQ(point3d.get_x(), 10); +ASSERT_EQ(point3d.get_y(), 33); +ASSERT_EQ(point3d.get_z(), 16); 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 new file mode 100644 index 000000000..a273c8568 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_null_object.js @@ -0,0 +1,33 @@ +/** + * 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 new file mode 100644 index 000000000..6ff771ee2 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/check_the_same_ets_object.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 { 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 new file mode 100644 index 000000000..a22257477 --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.cpp @@ -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. + */ + +#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 new file mode 100644 index 000000000..87cd5dc9a --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/test_use_ets_class.ets @@ -0,0 +1,189 @@ +/** + * 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 new file mode 100644 index 000000000..0af9461ce --- /dev/null +++ b/plugins/ets/tests/interop_js/tests/ets_proxy/use_ets_class/use_ets_class.test.js @@ -0,0 +1,32 @@ +/** + * 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/number_subtypes/number_subtypes.cpp.erb b/plugins/ets/tests/interop_js/tests/number_subtypes/number_subtypes.cpp.erb index 6449fd0f4..05a5e1ea4 100644 --- a/plugins/ets/tests/interop_js/tests/number_subtypes/number_subtypes.cpp.erb +++ b/plugins/ets/tests/interop_js/tests/number_subtypes/number_subtypes.cpp.erb @@ -20,11 +20,11 @@ namespace panda::ets::interop::js::testing { class EtsInteropNumberSubtypesTest : public EtsInteropTest { void SetUp() override - { + { interop_js_test_path_ = std::getenv("ARK_ETS_INTEROP_JS_GTEST_SOURCES"); // This object is used to save global js names RunJsScript("var gtest_env = {};\n"); - LoadModuleAs("module", "number_subtypes/module.js"); + LoadModuleAs("module", "module.js"); } }; 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 ff4441c69..755118714 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 @@ -38,7 +38,7 @@ function runTest() { process.exit(1); } - etsVm.registerEtsFunction('Lproxy_types_test/ETSGLOBAL;', 'main')(); + etsVm.getFunction('Lproxy_types_test/ETSGLOBAL;', 'main')(); testFunction(etsVm); testPrimitives(etsVm); @@ -47,31 +47,34 @@ function runTest() { } function testFunction(etsVm) { - const sumDouble = etsVm.registerEtsFunction('Lproxy_types_test/ETSGLOBAL;', 'SumDouble'); + const sumDouble = etsVm.getFunction('Lproxy_types_test/ETSGLOBAL;', 'SumDouble'); assertEq(sumDouble(3, 0.22).toFixed(3), (3.22).toFixed(3)); - const sumString = etsVm.registerEtsFunction('Lproxy_types_test/ETSGLOBAL;', 'SumString'); + const sumString = etsVm.getFunction('Lproxy_types_test/ETSGLOBAL;', 'SumString'); assertEq(sumString("Hello from ", "Panda!!"), "Hello from Panda!!"); } function testPrimitives(etsVm) { // Static fields - const Primitives = etsVm.registerEtsClass('Lproxy_types_test/Primitives;'); + const Primitives = etsVm.getClass('Lproxy_types_test/Primitives;'); assertEq(Primitives.STATIC_BOOLEAN, true); 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); - assertEq(Primitives.STATIC_STRING, "Panda static string!"); + // TODO(v.cherkashin): Uncomment when implemented, #12573 + // 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; - Primitives.STATIC_STRING = "JS static string"; + // TODO(v.cherkashin): Uncomment when implemented, #12573 + // 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); - assertEq(Primitives.STATIC_STRING, "JS static string"); + // TODO(v.cherkashin): Uncomment when implemented, #12573 + // assertEq(Primitives.STATIC_STRING, "JS static string"); // Non-static fields const primitives = new Primitives(); @@ -79,36 +82,41 @@ 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); - assertEq(primitives.STRING, "Panda string!!"); + // TODO(v.cherkashin): Uncomment when implemented, #12573 + // assertEq(primitives.STRING, "Panda string!!"); primitives.BOOLEAN = false; primitives.FLOAT = 7.7; primitives.DOUBLE = 8.8; primitives.INT = 9; - primitives.STRING = "JS string"; + // TODO(v.cherkashin): Uncomment when implemented, #12573 + // 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); - assertEq(primitives.STRING, "JS string"); + // TODO(v.cherkashin): Uncomment when implemented, #12573 + // assertEq(primitives.STRING, "JS string"); } function testObjects(etsVm) { - const External = etsVm.registerEtsClass('Lproxy_types_test/External;'); + const External = etsVm.getClass('Lproxy_types_test/External;'); const o = new External(); assertEq(o.inner.val, 322); } function testMethods(etsVm) { - const Methods = etsVm.registerEtsClass('Lproxy_types_test/Methods;'); + const Methods = etsVm.getClass('Lproxy_types_test/Methods;'); assertEq(Methods.staticSumDouble(3, 0.22).toFixed(3), (3.22).toFixed(3)); - assertEq(Methods.staticSumString("Hello from ", "Panda!!!"), "Hello from Panda!!!"); + // TODO(v.cherkashin): Uncomment when implemented, #12573 + // 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)); - assertEq(o.sumString("Hello from ", "Panda!!!!"), "Hello from Panda!!!!"); + // TODO(v.cherkashin): Uncomment when implemented, #12573 + // assertEq(o.sumString("Hello from ", "Panda!!!!"), "Hello from Panda!!!!"); assertEq(o.isTrue(false), false); } -- Gitee From f799f547136749521a1b5ce816ab7af15c71447c Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Wed, 28 Jun 2023 17:13:13 +0300 Subject: [PATCH 2/2] interop_js: Remove ts2ets_proxy Signed-off-by: Vyacheslav Cherkashin --- plugins/ets/runtime/interop_js/BUILD.gn | 2 - plugins/ets/runtime/interop_js/CMakeLists.txt | 1 - .../ets/runtime/interop_js/ets_vm_plugin.cpp | 1 - .../ets/runtime/interop_js/ts2ets_proxy.cpp | 1661 ----------------- plugins/ets/runtime/interop_js/ts2ets_proxy.h | 43 - 5 files changed, 1708 deletions(-) delete mode 100644 plugins/ets/runtime/interop_js/ts2ets_proxy.cpp delete mode 100644 plugins/ets/runtime/interop_js/ts2ets_proxy.h diff --git a/plugins/ets/runtime/interop_js/BUILD.gn b/plugins/ets/runtime/interop_js/BUILD.gn index d6af09c8b..0162528d8 100644 --- a/plugins/ets/runtime/interop_js/BUILD.gn +++ b/plugins/ets/runtime/interop_js/BUILD.gn @@ -43,7 +43,6 @@ ohos_shared_library("ets_interop_js_napi") { "ts2ets_common.cpp", "ts2ets_copy.cpp", "ts2ets_tstype.cpp", - "ts2ets_proxy.cpp", "js_job_queue.cpp", "pending_promise_listener.cpp" ] @@ -61,4 +60,3 @@ ohos_shared_library("ets_interop_js_napi") { deps = [ "$ark_root/runtime:libarkruntime" ] subsystem_name = "$ark_subsystem_name" } - diff --git a/plugins/ets/runtime/interop_js/CMakeLists.txt b/plugins/ets/runtime/interop_js/CMakeLists.txt index 1ba175058..f928446ed 100644 --- a/plugins/ets/runtime/interop_js/CMakeLists.txt +++ b/plugins/ets/runtime/interop_js/CMakeLists.txt @@ -42,7 +42,6 @@ panda_ets_interop_js_plugin(ets_interop_js_napi ts2ets_common.cpp ts2ets_copy.cpp ts2ets_tstype.cpp - ts2ets_proxy.cpp js_job_queue.cpp pending_promise_listener.cpp ${ETS_INTEROP_ARCH_SOURCES} diff --git a/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp b/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp index 16e50f611..c0214c30a 100644 --- a/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp +++ b/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp @@ -21,7 +21,6 @@ #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/ts2ets_copy.h" -#include "plugins/ets/runtime/interop_js/ts2ets_proxy.h" #include "generated/base_options.h" #ifdef PANDA_TARGET_OHOS diff --git a/plugins/ets/runtime/interop_js/ts2ets_proxy.cpp b/plugins/ets/runtime/interop_js/ts2ets_proxy.cpp deleted file mode 100644 index a5ac3952d..000000000 --- a/plugins/ets/runtime/interop_js/ts2ets_proxy.cpp +++ /dev/null @@ -1,1661 +0,0 @@ -/** - * 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. - */ - -#include "runtime/include/coretypes/class.h" -#include "plugins/ets/runtime/interop_js/interop_context.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/ets_type_visitor-inl.h" -#include "plugins/ets/runtime/types/ets_method.h" -#include "runtime/handle_scope-inl.h" - -namespace panda::ets::interop::js { - -#ifndef NDEBUG -// Debug log -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define PROXY_DLOG(msg) INTEROP_LOG_INFO(msg) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define PROXY_DLOG_A(msg, ...) INTEROP_LOG_INFO_A(msg, __VA_ARGS__) -// Wrappers cache log -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define PROXY_CLOG(msg) INTEROP_LOG_INFO(msg) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define PROXY_CLOG_A(msg, ...) INTEROP_LOG_INFO_A(msg, __VA_ARGS__) -#else -#define PROXY_DLOG(msg) -#define PROXY_DLOG_A(msg, ...) -#define PROXY_CLOG(msg) -#define PROXY_CLOG_A(msg, ...) -#endif - -// Lazy tagged pointer -template -struct WrapperLink { - static_assert(alignof(U) >= (1U << 1U)); - - WrapperLink() = default; - - explicit WrapperLink(U *ptr) - { - Set(ptr, true); - ASSERT(!IsResolved()); - } - explicit WrapperLink(R *ptr) - { - Set(ptr, false); - ASSERT(IsResolved()); - } - - bool IsResolved() const - { - return !GetTag(); - } - - auto GetUnresolved() const - { - ASSERT(!IsResolved()); - return reinterpret_cast(GetUPtr()); - } - - auto GetResolved() const - { - ASSERT(IsResolved()); - return reinterpret_cast(ptr_); - } - -private: - auto static constexpr MASK = ~static_cast(1); - - bool GetTag() const - { - return static_cast(ptr_ & ~MASK); - } - - uintptr_t GetUPtr() const - { - return ptr_ & MASK; - } - - template - void Set(T *ptr, bool tag) - { - ptr_ = reinterpret_cast(ptr); - ASSERT(!GetTag()); - ptr_ |= static_cast(tag); - } - - uintptr_t ptr_ {}; -}; - -struct WrapperClass; -struct WrapperMethod; -using WrapperClassLink = WrapperLink; -using WrapperMethodLink = WrapperLink; - -struct WrapperField { - WrapperClass *wclass_owner {}; - WrapperClassLink wclass_field {}; // used if reftype - uint32_t ets_offs {}; -}; - -// NOLINTBEGIN(misc-non-private-member-variables-in-classes) -struct WrapperClass { - panda::Class *ets_klass {}; - WrapperMethodLink ets_init {}; - napi_ref js_ctor {}; - panda::ObjectHeader *pending_ets_obj {}; // passed to js_ctor - std::vector wfields {}; // invalidates cached values if reallocated - std::vector wmethods {}; // invalidates cached values if reallocated - - bool shareable {}; // subtype of Shareable, enables reference tracking for ETS objects - - NO_COPY_SEMANTIC(WrapperClass); - NO_MOVE_SEMANTIC(WrapperClass); - - explicit WrapperClass() = default; - ~WrapperClass() = default; -}; -// NOLINTEND(misc-non-private-member-variables-in-classes) - -class WrapperClassCache { -public: - static WrapperClass *Lookup(panda::Class *klass) - { - auto it = cache_.find(klass); - if (UNLIKELY(it == cache_.end())) { - return nullptr; - } - return &it->second; - } - - static WrapperClass *Insert(panda::Class *klass) - { - ASSERT(Lookup(klass) == nullptr); - auto [it, found] = - cache_.emplace(std::piecewise_construct, std::forward_as_tuple(klass), std::forward_as_tuple()); - ASSERT(found); - - auto &res = it->second; - - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperClassCache: inserted: %p -> %p", (void *)klass, (void *)&res); - - return &res; - } - - static void Remove(panda::Class *klass) - { - ASSERT(Lookup(klass) != nullptr); - cache_.erase(klass); - } - - WrapperClassCache() = delete; - -private: - // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) - static std::unordered_map cache_; -}; - -// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) -std::unordered_map WrapperClassCache::cache_; - -struct WrapperRef { - // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) - panda::mem::Reference *ets_ref {}; - // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) - napi_ref js_ref {}; - - static constexpr size_t ETS_SHAREABLE_DATA_OFFSET = sizeof(panda::ObjectHeader); - - void LinkShareable(panda::ObjectHeader *ets_obj) - { - auto val = reinterpret_cast(this); - ets_obj->SetFieldPrimitive(ETS_SHAREABLE_DATA_OFFSET, val); - } - - static void UnlinkShareable(panda::ObjectHeader *ets_obj) - { - uint64_t val = 0; - ets_obj->SetFieldPrimitive(ETS_SHAREABLE_DATA_OFFSET, val); - } - - static WrapperRef *FromShareable(panda::ObjectHeader *ets_obj) - { - auto val = ets_obj->GetFieldPrimitive(ETS_SHAREABLE_DATA_OFFSET); - return reinterpret_cast(val); - } -}; - -class WrapperRefCache { -public: - static WrapperRef *Alloc() - { - // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) - void *mem = malloc(sizeof(WrapperClass)); - assert(mem); - return new (mem) WrapperRef {}; - } - - static void Free(WrapperRef *ref) - { - ref->~WrapperRef(); - // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) - free(ref); - } - - WrapperRefCache() = delete; -}; - -struct WrapperType; - -// NOLINTBEGIN(misc-non-private-member-variables-in-classes) -struct WrapperValueConvertors { - napi_value (*wrap)(napi_env env, WrapperType *wtype, panda::Value ets_val); - bool (*unwrap)(napi_env env, WrapperType *wtype, napi_value js_val, panda::Value &ets_val); - WrapperValueConvertors() = default; -}; - -struct WrapperType { - WrapperValueConvertors conv {}; - WrapperClass *wclass {}; // used if reftype - WrapperType() = default; -}; - -struct WrapperMethod { - panda::Method *ets_method {}; - napi_ref js_func {}; // only for functions (ETSGLOBAL::) - WrapperType wclass_this {}; // this class wrapper, only for instance methods - WrapperType wclass_ret {}; // return class value wrapper - std::vector wclass_args {}; // invalidates cached values if reallocated -}; -// NOLINTEND(misc-non-private-member-variables-in-classes) - -class WrapperMethodCache { -public: - static WrapperMethod *Lookup(panda::Method *method) - { - auto it = cache_.find(method); - if (UNLIKELY(it == cache_.end())) { - return nullptr; - } - return &it->second; - } - - static WrapperMethod *Insert(panda::Method *method) - { - ASSERT(Lookup(method) == nullptr); - auto [it, found] = - cache_.emplace(std::piecewise_construct, std::forward_as_tuple(method), std::forward_as_tuple()); - ASSERT(found); - auto &res = it->second; - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperMethodCache: inserted: %p -> %p", (void *)method, (void *)&res); - return &res; - } - - static void Remove(panda::Method *method) - { - ASSERT(Lookup(method) != nullptr); - cache_.erase(method); - } - - WrapperMethodCache() = delete; - -private: - // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) - static std::unordered_map cache_; -}; - -// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) -std::unordered_map WrapperMethodCache::cache_; - -// unwrap object with typecheck -static inline WrapperRef *WrapperObjUnwrap(napi_env env, napi_value jsval) -{ - void *data; - NAPI_CHECK_FATAL(napi_unwrap(env, jsval, &data)); - ASSERT(data != nullptr); - return reinterpret_cast(data); -} - -static WrapperClass *GetWrapperClass(napi_env env, panda::Class *klass); - -// allocate js wrapper for existing ets object -static napi_value WrapperCreateForExisting(napi_env env, WrapperClass *wclass, panda::ObjectHeader *ets_obj) -{ - napi_value js_ctor {}; - napi_value js_val {}; - NAPI_CHECK_FATAL(napi_get_reference_value(env, wclass->js_ctor, &js_ctor)); - wclass->pending_ets_obj = ets_obj; - NAPI_CHECK_FATAL(napi_new_instance(env, js_ctor, 0, nullptr, &js_val)); - return js_val; -} - -#ifndef NDEBUG -static void DumpField(WrapperField *wfield) -{ - [[maybe_unused]] panda::Class *owner_klass = wfield->wclass_owner->ets_klass; - panda::Class *field_class; - auto wclass_field = wfield->wclass_field; - bool resolved = wclass_field.IsResolved(); - if (resolved) { - field_class = wclass_field.GetResolved()->ets_klass; - } else { - field_class = wclass_field.GetUnresolved(); - } - - if (field_class != nullptr) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("DumpField: own_cls=%p offs=%u field_cls=%p", (void *)owner_klass, wfield->ets_offs, - (void *)field_class); - } else { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("DumpField: own_cls=%p offs=%u ", (void *)owner_klass, wfield->ets_offs); - } -} -#else -static void DumpField([[maybe_unused]] WrapperField *wfield) {} -#endif - -// getter owner check -static bool WrapperFieldGetCheck(napi_env env, napi_callback_info cinfo, WrapperField *&wfield, WrapperRef *&wref) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("WrapperFieldGetCheck"); - - size_t argc = 0; - napi_value athis; - void *data; - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, &athis, &data)); - if (UNLIKELY(argc != 0)) { - InteropCtx::ThrowJSError(env, "getter called in wrong context"); - return false; - } - - wfield = reinterpret_cast(data); - DumpField(wfield); - wref = WrapperObjUnwrap(env, athis); - return wref != nullptr; -} - -// setter owner check -static bool WrapperFieldSetCheck(napi_env env, napi_callback_info cinfo, WrapperField *&wfield, WrapperRef *&wref, - napi_value &js_val) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("WrapperFieldSetCheck"); - - size_t argc = 1; - napi_value athis; - void *data; - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, &js_val, &athis, &data)); - if (UNLIKELY(argc != 1)) { - InteropCtx::ThrowJSError(env, "setter called in wrong context"); - return false; - } - - wfield = reinterpret_cast(data); - DumpField(wfield); - wref = WrapperObjUnwrap(env, athis); - return wref != nullptr; -} - -// static getter owner check -static bool WrapperStaticFieldGetCheck(napi_env env, napi_callback_info cinfo, WrapperField *&wfield) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("WrapperStaticFieldGetCheck"); - - size_t argc = 0; - napi_value athis; - void *data; - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, &athis, &data)); - if (UNLIKELY(argc != 0)) { - InteropCtx::ThrowJSError(env, "getter called in wrong context"); - return false; - } - - wfield = reinterpret_cast(data); - DumpField(wfield); - return true; -} - -// static setter owner check -static bool WrapperStaticFieldSetCheck(napi_env env, napi_callback_info cinfo, WrapperField *&wfield, - napi_value &js_val) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("WrapperStaticFieldSetCheck"); - - size_t argc = 1; - napi_value athis; - void *data; - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, &js_val, &athis, &data)); - if (UNLIKELY(argc != 1)) { - InteropCtx::ThrowJSError(env, "setter called in wrong context"); - return false; - } - - wfield = reinterpret_cast(data); - DumpField(wfield); - return true; -} - -WrapperClass *WrapperClassLinkResolve(napi_env env, WrapperClassLink &link) -{ - if (LIKELY(link.IsResolved())) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperClassLinkResolve: hit: %p -> %p", (void *)link.GetResolved()->ets_klass, - (void *)link.GetResolved()); - return link.GetResolved(); - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_CLOG_A("WrapperClassLinkResolve: miss: %p", (void *)link.GetUnresolved()); - WrapperClass *wclass = GetWrapperClass(env, link.GetUnresolved()); - if (UNLIKELY(wclass == nullptr)) { - return nullptr; - } - link = WrapperClassLink(wclass); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperClassLinkResolve: resolved: %p -> %p", (void *)link.GetResolved()->ets_klass, - (void *)link.GetResolved()); - return wclass; -} - -// internal ets->js -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_WRAPPER_WRAP(type, cpptype) \ - static inline napi_value WrapperValueWrapImpl##type(napi_env env, [[maybe_unused]] WrapperClass *wclass, \ - cpptype ets_val); \ - static napi_value WrapperValueWrap##type(napi_env env, WrapperType *wtype, panda::Value ets_val) \ - { \ - return WrapperValueWrapImpl##type(env, wtype->wclass, ets_val.GetAs()); \ - } \ - static inline napi_value WrapperValueWrapImpl##type( \ - [[maybe_unused]] napi_env env, [[maybe_unused]] WrapperClass *wclass, [[maybe_unused]] cpptype ets_val) - -// internal ets->js -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_WRAPPER_UNWRAP(type, cpptype) \ - static inline bool WrapperValueUnwrapImpl##type(napi_env env, [[maybe_unused]] WrapperClass *wclass, \ - napi_value js_val, cpptype &ets_val); \ - static bool WrapperValueUnwrap##type(napi_env env, WrapperType *wtype, napi_value js_val, panda::Value &ets_val) \ - { \ - cpptype val; \ - if (UNLIKELY(!WrapperValueUnwrapImpl##type(env, wtype->wclass, js_val, val))) { \ - return false; \ - } \ - ets_val = panda::Value(val); \ - return true; \ - } \ - /* so wrap wase also defined */ \ - [[maybe_unused]] static constexpr WrapperValueConvertors WrapperValueConvertors##type {WrapperValueWrap##type, \ - WrapperValueUnwrap##type}; \ - static inline bool WrapperValueUnwrapImpl##type([[maybe_unused]] napi_env env, \ - [[maybe_unused]] WrapperClass *wclass, \ - [[maybe_unused]] napi_value js_val, cpptype &ets_val) - -// getter accessor -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_WRAPPER_FIELD_GET(type) \ - static inline napi_value WrapperFieldGetImpl##type(napi_env env, WrapperField *wfield, WrapperRef *wref); \ - static napi_value WrapperFieldGet##type(napi_env env, napi_callback_info cinfo) \ - { \ - auto coro = EtsCoroutine::GetCurrent(); \ - ScopedManagedCodeThreadSwitch managed_scope(coro); \ - WrapperField *wfield; \ - WrapperRef *wref; \ - if (UNLIKELY(!WrapperFieldGetCheck(env, cinfo, wfield, wref))) { \ - return napi_value {nullptr}; \ - } \ - return WrapperFieldGetImpl##type(env, wfield, wref); \ - } \ - static inline napi_value WrapperFieldGetImpl##type(napi_env env, WrapperField *wfield, WrapperRef *wref) - -// setter accessor -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_WRAPPER_FIELD_SET(type) \ - static inline void WrapperFieldSetImpl##type(napi_env env, WrapperField *wfield, WrapperRef *wref, \ - napi_value js_val); \ - static napi_value WrapperFieldSet##type(napi_env env, napi_callback_info cinfo) \ - { \ - auto coro = EtsCoroutine::GetCurrent(); \ - ScopedManagedCodeThreadSwitch managed_scope(coro); \ - WrapperField *wfield; \ - WrapperRef *wref; \ - napi_value js_val; \ - if (UNLIKELY(!WrapperFieldSetCheck(env, cinfo, wfield, wref, js_val))) { \ - return napi_value {nullptr}; \ - } \ - WrapperFieldSetImpl##type(env, wfield, wref, js_val); \ - return napi_value {nullptr}; \ - } \ - static inline void WrapperFieldSetImpl##type(napi_env env, WrapperField *wfield, WrapperRef *wref, \ - napi_value js_val) - -// static getter accessor -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_WRAPPER_STATIC_FIELD_GET(type) \ - static inline napi_value WrapperStaticFieldGetImpl##type(napi_env env, WrapperField *wfield); \ - static napi_value WrapperStaticFieldGet##type(napi_env env, napi_callback_info cinfo) \ - { \ - auto coro = EtsCoroutine::GetCurrent(); \ - ScopedManagedCodeThreadSwitch managed_scope(coro); \ - WrapperField *wfield; \ - if (UNLIKELY(!WrapperStaticFieldGetCheck(env, cinfo, wfield))) { \ - return napi_value {nullptr}; \ - } \ - return WrapperStaticFieldGetImpl##type(env, wfield); \ - } \ - static inline napi_value WrapperStaticFieldGetImpl##type(napi_env env, WrapperField *wfield) - -// static setter accessor -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_WRAPPER_STATIC_FIELD_SET(type) \ - static inline void WrapperStaticFieldSetImpl##type(napi_env env, WrapperField *wfield, napi_value &js_val); \ - static napi_value WrapperStaticFieldSet##type(napi_env env, napi_callback_info cinfo) \ - { \ - auto coro = EtsCoroutine::GetCurrent(); \ - ScopedManagedCodeThreadSwitch managed_scope(coro); \ - WrapperField *wfield; \ - napi_value js_val; \ - if (UNLIKELY(!WrapperStaticFieldSetCheck(env, cinfo, wfield, js_val))) { \ - return napi_value {nullptr}; \ - } \ - WrapperStaticFieldSetImpl##type(env, wfield, js_val); \ - return napi_value {nullptr}; \ - } \ - static inline void WrapperStaticFieldSetImpl##type([[maybe_unused]] napi_env env, \ - [[maybe_unused]] WrapperField *wfield, napi_value &js_val) - -// accessors for primitive fields -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_WRAPPER_FIELD_ACCESSOR_PRIMITIVE(type, cpptype) \ - TS2ETS_WRAPPER_FIELD_GET(type) \ - { \ - auto ets_obj = InteropCtx::Current()->Refstor()->Get(wref->ets_ref); \ - auto ets_val = ets_obj->GetFieldPrimitive(wfield->ets_offs); \ - return WrapperValueWrapImpl##type(env, nullptr, ets_val); \ - } \ - TS2ETS_WRAPPER_FIELD_SET(type) \ - { \ - cpptype ets_val; \ - if (UNLIKELY(!WrapperValueUnwrapImpl##type(env, nullptr, js_val, ets_val))) { \ - InteropCtx::ThrowJSError(env, "unwrap type mismatch, " #type " expected"); \ - return; \ - } \ - auto ets_obj = InteropCtx::Current()->Refstor()->Get(wref->ets_ref); \ - ets_obj->SetFieldPrimitive(wfield->ets_offs, ets_val); \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TS2ETS_WRAPPER_STATIC_FIELD_ACCESSOR_PRIMITIVE(type, cpptype) \ - TS2ETS_WRAPPER_STATIC_FIELD_GET(type) \ - { \ - auto ets_val = wfield->wclass_owner->ets_klass->GetFieldPrimitive(wfield->ets_offs); \ - return WrapperValueWrapImpl##type(env, nullptr, ets_val); \ - } \ - TS2ETS_WRAPPER_STATIC_FIELD_SET(type) \ - { \ - cpptype ets_val; \ - if (UNLIKELY(!WrapperValueUnwrapImpl##type(env, nullptr, js_val, ets_val))) { \ - InteropCtx::ThrowJSError(env, "unwrap type mismatch, " #type " expected"); \ - return; \ - } \ - wfield->wclass_owner->ets_klass->SetFieldPrimitive(wfield->ets_offs, ets_val); \ - } - -TS2ETS_WRAPPER_WRAP(VOID, int8_t) -{ - napi_value js_val; - NAPI_CHECK_FATAL(napi_get_undefined(env, &js_val)); - return js_val; -} -TS2ETS_WRAPPER_UNWRAP(VOID, int8_t) -{ - static_cast(ets_val); - UNREACHABLE(); // "unwrap void" shouldnt happen -} - -TS2ETS_WRAPPER_WRAP(F32, float) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_WRAP: float %g", ets_val); - napi_value js_val; - NAPI_CHECK_FATAL(napi_create_double(env, static_cast(ets_val), &js_val)); - return js_val; -} -TS2ETS_WRAPPER_UNWRAP(F32, float) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { - InteropCtx::ThrowJSError(env, "unwrap type mismatch, number expected"); - return false; - } - double val; - NAPI_CHECK_FATAL(napi_get_value_double(env, js_val, &val)); - ets_val = val; - return true; -} -TS2ETS_WRAPPER_FIELD_ACCESSOR_PRIMITIVE(F32, float) -TS2ETS_WRAPPER_STATIC_FIELD_ACCESSOR_PRIMITIVE(F32, float) - -TS2ETS_WRAPPER_WRAP(U1, bool) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_WRAP: bool %g", ets_val); - napi_value js_val; - NAPI_CHECK_FATAL(napi_get_boolean(env, static_cast(ets_val), &js_val)); - return js_val; -} -TS2ETS_WRAPPER_UNWRAP(U1, bool) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_boolean)) { - InteropCtx::ThrowJSError(env, "unwrap type mismatch, number expected"); - return false; - } - bool val; - NAPI_CHECK_FATAL(napi_get_value_bool(env, js_val, &val)); - ets_val = val; - return true; -} -TS2ETS_WRAPPER_FIELD_ACCESSOR_PRIMITIVE(U1, bool) -TS2ETS_WRAPPER_STATIC_FIELD_ACCESSOR_PRIMITIVE(U1, bool) - -TS2ETS_WRAPPER_WRAP(F64, double) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_WRAP: double %lg", ets_val); - napi_value js_val; - NAPI_CHECK_FATAL(napi_create_double(env, static_cast(ets_val), &js_val)); - return js_val; -} -TS2ETS_WRAPPER_UNWRAP(F64, double) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { - InteropCtx::ThrowJSError(env, "unwrap type mismatch, number expected"); - return false; - } - double val; - NAPI_CHECK_FATAL(napi_get_value_double(env, js_val, &val)); - ets_val = val; - return true; -} -TS2ETS_WRAPPER_FIELD_ACCESSOR_PRIMITIVE(F64, double) -TS2ETS_WRAPPER_STATIC_FIELD_ACCESSOR_PRIMITIVE(F64, double) - -TS2ETS_WRAPPER_WRAP(I32, int32_t) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_WRAP: int %d", ets_val); - napi_value js_val; - NAPI_CHECK_FATAL(napi_create_int32(env, static_cast(ets_val), &js_val)); - return js_val; -} -TS2ETS_WRAPPER_UNWRAP(I32, int32_t) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { - InteropCtx::ThrowJSError(env, "unwrap type mismatch, number expected"); - return false; - } - int32_t val; - NAPI_CHECK_FATAL(napi_get_value_int32(env, js_val, &val)); - ets_val = val; - return true; -} -TS2ETS_WRAPPER_FIELD_ACCESSOR_PRIMITIVE(I32, int32_t) -TS2ETS_WRAPPER_STATIC_FIELD_ACCESSOR_PRIMITIVE(I32, int32_t) - -TS2ETS_WRAPPER_WRAP(I64, int64_t) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_WRAP: long %ld", ets_val); - napi_value js_val; - NAPI_CHECK_FATAL(napi_create_int64(env, static_cast(ets_val), &js_val)); - return js_val; -} -TS2ETS_WRAPPER_UNWRAP(I64, int64_t) -{ - if (UNLIKELY(GetValueType(env, js_val) != napi_number)) { - InteropCtx::ThrowJSError(env, "unwrap type mismatch, number expected"); - return false; - } - int64_t val; - NAPI_CHECK_FATAL(napi_get_value_int64(env, js_val, &val)); - ets_val = val; - return true; -} -TS2ETS_WRAPPER_FIELD_ACCESSOR_PRIMITIVE(I64, int64_t) -TS2ETS_WRAPPER_STATIC_FIELD_ACCESSOR_PRIMITIVE(I64, int64_t) - -TS2ETS_WRAPPER_WRAP(OBJ, panda::ObjectHeader *) -{ - if (UNLIKELY(ets_val == nullptr)) { - napi_value js_val; - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("TS2ETS_WRAPPER_WRAP: null reference"); - NAPI_CHECK_FATAL(napi_get_null(env, &js_val)); - return js_val; - } - - if (LIKELY(wclass->shareable)) { - auto wref = WrapperRef::FromShareable(ets_val); - if (LIKELY(wref != nullptr)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_CLOG_A("TS2ETS_WRAPPER_WRAP: shareable&hit: oh=%p", (void *)ets_val); - napi_value js_val; - NAPI_CHECK_FATAL(napi_get_reference_value(env, wref->js_ref, &js_val)); - return js_val; - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_CLOG_A("TS2ETS_WRAPPER_WRAP: shareable&miss: oh=%p", (void *)ets_val); - } - - return WrapperCreateForExisting(env, wclass, ets_val); -} -TS2ETS_WRAPPER_UNWRAP(OBJ, panda::ObjectHeader *) -{ - auto wref = WrapperObjUnwrap(env, js_val); - if (UNLIKELY(wref == nullptr)) { - return false; - } - ets_val = InteropCtx::Current()->Refstor()->Get(wref->ets_ref); - return true; -} - -TS2ETS_WRAPPER_FIELD_GET(OBJ) -{ - DumpField(wfield); - - auto wclass_field = WrapperClassLinkResolve(env, wfield->wclass_field); - if (UNLIKELY(wclass_field == nullptr)) { - return nullptr; - } - auto ets_obj = InteropCtx::Current()->Refstor()->Get(wref->ets_ref); - auto ets_val = ets_obj->GetFieldObject(wfield->ets_offs); - - if (UNLIKELY(ets_val == nullptr)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_FIELD_GET: null from wref=%p offs=%u", (void *)wref, wfield->ets_offs); - } - return WrapperValueWrapImplOBJ(env, wclass_field, ets_val); -} -TS2ETS_WRAPPER_FIELD_SET(OBJ) -{ - DumpField(wfield); - - auto wclass_field = WrapperClassLinkResolve(env, wfield->wclass_field); - if (UNLIKELY(wclass_field == nullptr)) { - return; - } - - panda::ObjectHeader *ets_val; - if (UNLIKELY(!WrapperValueUnwrapImplOBJ(env, wclass_field, js_val, ets_val))) { - return; - } - - auto ets_obj = InteropCtx::Current()->Refstor()->Get(wref->ets_ref); - if (ets_val == nullptr) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_FIELD_SET: null to wref=%p offs=%u", (void *)wref, wfield->ets_offs); - } - ets_obj->SetFieldObject(wfield->ets_offs, ets_val); -} -TS2ETS_WRAPPER_WRAP(STRING, panda::ObjectHeader *) -{ - napi_value js_val; - if (UNLIKELY(ets_val == nullptr)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("TS2ETS_WRAPPER_WRAP: null reference"); - NAPI_CHECK_FATAL(napi_get_null(env, &js_val)); - - return js_val; - } - - ASSERT(wclass == nullptr && "No wrapper class for strings"); - auto ets_string = EtsString::FromEtsObject(EtsObject::FromCoreType(ets_val)); - if (ets_string->IsUtf16()) { - auto str = reinterpret_cast(ets_string->GetDataUtf16()); - auto length = ets_string->GetUtf16Length() - 1; - NAPI_CHECK_FATAL(napi_create_string_utf16(env, reinterpret_cast(str), length, &js_val)); - } else { - auto str = utf::Mutf8AsCString(ets_string->GetDataMUtf8()); - auto length = ets_string->GetMUtf8Length() - 1; - NAPI_CHECK_FATAL(napi_create_string_utf8(env, str, length, &js_val)); - } - - return js_val; -} -TS2ETS_WRAPPER_UNWRAP(STRING, panda::ObjectHeader *) -{ - size_t length; - NAPI_CHECK_FATAL(napi_get_value_string_utf16(env, js_val, nullptr, 0, &length)); - std::vector buf; - buf.resize(length + 1); - NAPI_CHECK_FATAL(napi_get_value_string_utf16(env, js_val, buf.data(), buf.size(), &length)); - ets_val = EtsString::CreateFromUtf16(reinterpret_cast(buf.data()), length)->AsObject()->GetCoreType(); - return true; -} - -TS2ETS_WRAPPER_FIELD_GET(STRING) -{ - DumpField(wfield); - - auto ets_obj = InteropCtx::Current()->Refstor()->Get(wref->ets_ref); - auto ets_val = ets_obj->GetFieldObject(wfield->ets_offs); - - if (UNLIKELY(ets_val == nullptr)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_FIELD_GET: null from wref=%p offs=%u", (void *)wref, wfield->ets_offs); - } - return WrapperValueWrapImplSTRING(env, nullptr, ets_val); -} -TS2ETS_WRAPPER_FIELD_SET(STRING) -{ - DumpField(wfield); - - panda::ObjectHeader *ets_val; - if (UNLIKELY(!WrapperValueUnwrapImplSTRING(env, nullptr, js_val, ets_val))) { - return; - } - - auto ets_obj = InteropCtx::Current()->Refstor()->Get(wref->ets_ref); - if (ets_val == nullptr) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_FIELD_SET: null to wref=%p offs=%u", (void *)wref, wfield->ets_offs); - } - ets_obj->SetFieldObject(wfield->ets_offs, ets_val); -} -TS2ETS_WRAPPER_STATIC_FIELD_GET(STRING) -{ - DumpField(wfield); - - auto ets_val = wfield->wclass_owner->ets_klass->GetFieldObject(wfield->ets_offs); - - if (UNLIKELY(ets_val == nullptr)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_STATIC_FIELD_GET: null from offs=%u", wfield->ets_offs); - } - return WrapperValueWrapImplSTRING(env, nullptr, ets_val); -} -TS2ETS_WRAPPER_STATIC_FIELD_SET(STRING) -{ - DumpField(wfield); - - panda::ObjectHeader *ets_val; - if (UNLIKELY(!WrapperValueUnwrapImplSTRING(env, nullptr, js_val, ets_val))) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("TS2ETS_WRAPPER_STATIC_FIELD_SET: null to offs=%u", wfield->ets_offs); - } - wfield->wclass_owner->ets_klass->SetFieldObject(wfield->ets_offs, ets_val); -} - -static void WrapperFinalize([[maybe_unused]] napi_env env, void *data, [[maybe_unused]] void *hint) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperFinalize: %p", (void *)data); - auto coro = EtsCoroutine::GetCurrent(); - ScopedManagedCodeThreadSwitch managed_scope(coro); - auto wref = reinterpret_cast(data); - InteropCtx::Current(coro)->Refstor()->Remove(wref->ets_ref); - WrapperRefCache::Free(wref); -} - -static void WrapperFinalizeShareable([[maybe_unused]] napi_env env, void *data, [[maybe_unused]] void *hint) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperFinalizeShareable: %p", (void *)data); - auto coro = EtsCoroutine::GetCurrent(); - auto ctx = InteropCtx::Current(coro); - ScopedManagedCodeThreadSwitch managed_scope(coro); - auto wref = reinterpret_cast(data); - WrapperRef::UnlinkShareable(ctx->Refstor()->Get(wref->ets_ref)); - ctx->Refstor()->Remove(wref->ets_ref); - WrapperRefCache::Free(wref); -} - -static bool WrapperCtorInit(napi_env env, napi_callback_info cinfo, size_t argc, WrapperClass *wclass, - mem::Reference *ets_ref); - -// napi_wrap existing or newly allocated ets object -static napi_value WrapperCtor(napi_env env, napi_callback_info cinfo) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("WrapperCtor"); - auto coro = EtsCoroutine::GetCurrent(); - ScopedManagedCodeThreadSwitch managed_scope(coro); - size_t argc = 0; - napi_value athis; - void *data; - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, nullptr, &athis, &data)); - - auto wclass = reinterpret_cast(data); - panda::ObjectHeader *ets_obj; - WrapperRef *wref = WrapperRefCache::Alloc(); - auto ctx = InteropCtx::Current(coro); - - if (wclass->pending_ets_obj != nullptr) { - // nonexisting ref for existing ets obj - ets_obj = wclass->pending_ets_obj; - wclass->pending_ets_obj = nullptr; - // commit changes - wref->ets_ref = ctx->Refstor()->Add(ets_obj, panda::mem::Reference::ObjectType::GLOBAL); - } else { - // nonexisting ets object ~ allocate and call - ets_obj = panda::ObjectHeader::Create(coro, wclass->ets_klass); - if (UNLIKELY(ets_obj == nullptr)) { - WrapperRefCache::Free(wref); - return nullptr; - } - // commit changes - wref->ets_ref = ctx->Refstor()->Add(ets_obj, panda::mem::Reference::ObjectType::GLOBAL); - if (UNLIKELY(!WrapperCtorInit(env, cinfo, argc, wclass, wref->ets_ref))) { - ctx->Refstor()->Remove(wref->ets_ref); - WrapperRefCache::Free(wref); - return nullptr; - } - } - - // link wref with js and ets refs - if (LIKELY(wclass->shareable)) { - wref->LinkShareable(ctx->Refstor()->Get(wref->ets_ref)); - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperCtor: add finalizer for %p", (void *)wref); - - auto cb = wclass->shareable ? WrapperFinalizeShareable : WrapperFinalize; - - NAPI_CHECK_FATAL( - napi_wrap(env, athis, wref, cb, wref, nullptr)); // can't create weakref with napi_wrap in ace napi - NAPI_CHECK_FATAL(napi_create_reference(env, athis, 0, &wref->js_ref)); - NAPI_CHECK_FATAL(napi_object_seal(env, athis)); // TODO(vpukhov): broken in ace napi - return athis; -} - -template -napi_value CallWrapperMethod(napi_env env, napi_callback_info cinfo); - -class WrapperClassCreator final : public EtsTypeVisitor { - using Base = EtsTypeVisitor; - -public: - WrapperClassCreator(napi_env env, panda::Class *klass) : env_(env) - { - ASSERT(WrapperClassCache::Lookup(klass) == nullptr); - - wclass_ = WrapperClassCache::Insert(klass); - wclass_->ets_klass = klass; - } - - WrapperClass *Process() - { - auto res = ProcessInternal(); - if (Error()) { - WrapperClassCache::Remove(wclass_->ets_klass); - return nullptr; - } - return res; - } - -private: - static constexpr auto METHOD_ATTR = static_cast(0); - // TODO(vpukhov): napi_static methods are added to instance - static constexpr auto STATIC_METHOD_ATTR = static_cast(napi_static); - static constexpr auto FIELD_ATTR = static_cast(napi_writable | napi_enumerable); - // TODO(vpukhov): napi_static fields are added to instance - static constexpr auto STATIC_FIELD_ATTR = static_cast( - napi_static | napi_writable | napi_enumerable); // NOLINT(hicpp-signed-bitwise) - static constexpr const char *INIT_NAME = ""; - - WrapperClass *ProcessInternal() - { - auto klass = wclass_->ets_klass; - auto klass_name = klass->GetName(); // mangled name - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperClassCreator: %p %s", (void *)klass, klass_name.c_str()); - ASSERT(!klass->IsPrimitive()); - if (klass->IsArrayClass()) { - Error() = "arrays are not supported"; - return nullptr; - } - - if (klass->IsStringClass()) { - NapiFatal("Unreachable"); - } - - auto const n_fields = klass->GetFields().size(); - auto const n_methods = klass->GetMethods().size(); - wclass_->wfields.reserve(n_fields); - wclass_->wmethods.reserve(n_methods); - js_props_.reserve(n_fields + n_methods); - - VisitClass(wclass_->ets_klass); - if (Error()) { - return nullptr; - } - ProcessMethods(); - if (Error()) { - return nullptr; - } - HandleShareable(); - - napi_value js_ctor; - NAPI_CHECK_FATAL(napi_define_class(env_, klass_name.c_str(), NAPI_AUTO_LENGTH, WrapperCtor, wclass_, - js_props_.size(), js_props_.data(), &js_ctor)); - NAPI_CHECK_FATAL(napi_create_reference(env_, js_ctor, 1, &wclass_->js_ctor)); - return wclass_; - } - - void HandleShareable() - { - if (UNLIKELY(shareable_klass_ == nullptr)) { - PandaEtsVM *vm = EtsCoroutine::GetCurrent()->GetPandaVM(); - shareable_klass_ = vm->GetClassLinker()->GetClass("Lstd/interop/js/Shareable;"); - NAPI_FATAL_IF(shareable_klass_ == nullptr); // Shareable must be defined somewhere - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("HandleShareable: klass is: %p %s", (void *)shareable_klass_, - shareable_klass_->GetDescriptor()); - } - - auto klass = EtsClass::FromRuntimeClass(wclass_->ets_klass); - NAPI_FATAL_IF(klass == shareable_klass_); // user cannot construct shareable directly - - for (auto k = klass; k != nullptr; k = k->GetBase()) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("HandleShareable: test for: %p %s", (void *)k, k->GetDescriptor()); - if (k == shareable_klass_) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("HandleShareable: is shareable: %p", (void *)klass); - wclass_->shareable = true; - break; - } - } - } - - // For object fields klass_field != nullptr - void AddField(panda::Class *klass_field, napi_callback getter, napi_callback setter) - { - { - WrapperField wfield {}; - wfield.wclass_owner = wclass_; - wfield.wclass_field = WrapperClassLink(klass_field); // lazy - wfield.ets_offs = ets_field_->GetOffset(); - wclass_->wfields.push_back(wfield); - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("AddField: lazy: %p %p", (void *)klass_field, - (void *)wclass_->wfields.back().wclass_field.GetUnresolved()); - { - napi_property_descriptor prop {}; - prop.utf8name = reinterpret_cast(ets_field_->GetName().data); - prop.getter = getter; - prop.setter = setter; - prop.attributes = InStatic() ? STATIC_FIELD_ATTR : FIELD_ATTR; - prop.data = &wclass_->wfields.back(); - js_props_.emplace_back(prop); - } - DumpField(&wclass_->wfields.back()); - } - - void AddMethod(panda::Method *method) - { - auto name = reinterpret_cast(method->GetName().data); - - napi_callback impl; - if (method->IsStatic()) { - impl = CallWrapperMethod; - } else { - impl = CallWrapperMethod; - } - - wclass_->wmethods.emplace_back(WrapperMethodLink(method)); // lazy - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("AddMethod: lazy: %s %p", name, (void *)wclass_->wmethods.back().GetUnresolved()); - - { - napi_property_descriptor prop {}; - prop.utf8name = name; - prop.method = impl; - prop.attributes = method->IsStatic() ? STATIC_METHOD_ATTR : METHOD_ATTR; - prop.data = &wclass_->wmethods.back(); - js_props_.emplace_back(prop); - } - } - - void ProcessMethods() - { - auto klass = wclass_->ets_klass; - auto methods = klass->GetMethods(); - - std::unordered_set names; - - for (auto &method : methods) { - auto name = method.GetName().data; - auto it = names.insert(name); - NAPI_FATAL_IF(!it.second); // no overloading - AddMethod(&method); - if (strcmp(reinterpret_cast(name), INIT_NAME) == 0) { - wclass_->ets_init = WrapperMethodLink(&method); // lazy - } - } - ASSERT(wclass_->ets_init.GetUnresolved() != nullptr); - } - - void VisitU1() override - { - if (InStatic()) { - AddField(nullptr, WrapperStaticFieldGetU1, WrapperStaticFieldSetU1); - } else { - AddField(nullptr, WrapperFieldGetU1, WrapperFieldSetU1); - } - } - - void VisitI8() override - { - NAPI_FATAL_IF("not implemented"); - } - - void VisitI16() override - { - NAPI_FATAL_IF("not implemented"); - } - - void VisitU16() override - { - NAPI_FATAL_IF("not implemented"); - } - - void VisitI32() override - { - if (InStatic()) { - AddField(nullptr, WrapperStaticFieldGetI32, WrapperStaticFieldSetI32); - } else { - AddField(nullptr, WrapperFieldGetI32, WrapperFieldSetI32); - } - } - - void VisitI64() override - { - if (InStatic()) { - AddField(nullptr, WrapperStaticFieldGetI64, WrapperStaticFieldSetI64); - } else { - AddField(nullptr, WrapperFieldGetI64, WrapperFieldSetI64); - } - } - - void VisitF32() override - { - if (InStatic()) { - AddField(nullptr, WrapperStaticFieldGetF32, WrapperStaticFieldSetF32); - } else { - AddField(nullptr, WrapperFieldGetF32, WrapperFieldSetF32); - } - } - - void VisitF64() override - { - if (InStatic()) { - AddField(nullptr, WrapperStaticFieldGetF64, WrapperStaticFieldSetF64); - } else { - AddField(nullptr, WrapperFieldGetF64, WrapperFieldSetF64); - } - } - - void VisitString([[maybe_unused]] panda::Class *klass) override - { - if (InStatic()) { - AddField(nullptr, WrapperStaticFieldGetSTRING, WrapperStaticFieldSetSTRING); - } else { - AddField(nullptr, WrapperFieldGetSTRING, WrapperFieldSetSTRING); - } - } - - void VisitArray([[maybe_unused]] panda::Class *klass) override - { - NAPI_FATAL_IF("not implemented"); - } - - void VisitFieldReference([[maybe_unused]] panda::Field const *field, panda::Class *klass) override - { - if (klass->IsStringClass()) { - VisitString(klass); - return; - } - - if (InStatic()) { - NAPI_FATAL_IF("not implemented"); - } else { - AddField(klass, WrapperFieldGetOBJ, WrapperFieldSetOBJ); - } - } - - void VisitFieldPrimitive([[maybe_unused]] panda::Field const *field, panda::panda_file::Type type) override - { - VisitPrimitive(type); - } - - void VisitField(const panda::Field *field) override - { - ets_field_ = field; - Base::VisitField(field); - } - -private: - napi_env env_; - WrapperClass *wclass_ {}; - - std::vector js_props_; - const panda::Field *ets_field_ {}; // to pass to field visitor - - static EtsClass *shareable_klass_; -}; -EtsClass *WrapperClassCreator::shareable_klass_ {}; - -static WrapperClass *GetWrapperClass(napi_env env, panda::Class *klass) -{ - ASSERT(klass != nullptr); - auto wclass = WrapperClassCache::Lookup(klass); - if (LIKELY(wclass != nullptr)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("GetWrapperClass: cache hit: %p -> %p", (void *)klass, (void *)wclass); - return wclass; - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("GetWrapperClass: cache miss: %p", (void *)klass); - // call convertor - WrapperClassCreator creator(env, klass); - wclass = creator.Process(); - if (UNLIKELY(wclass == nullptr)) { - std::string msg("can't create proxy wrapper of class '" + klass->GetName() + - "' Error: " + creator.Error().value_or("empty")); - InteropCtx::ThrowJSError(env, msg); - return nullptr; - } - return wclass; -} - -static WrapperMethod *GetWrapperMethod(napi_env env, panda::Method *method); - -static inline WrapperMethod *WrapperMethodLinkResolve(napi_env env, WrapperMethodLink &link) -{ - if (LIKELY(link.IsResolved())) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_CLOG_A("WrapperMethodLinkResolve: hit: %p -> %p", (void *)link.GetResolved()->ets_method, - (void *)link.GetResolved()); - return link.GetResolved(); - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_CLOG_A("WrapperMethodLinkResolve: miss: %p", (void *)link.GetUnresolved()); - WrapperMethod *wmethod = GetWrapperMethod(env, link.GetUnresolved()); - if (UNLIKELY(wmethod == nullptr)) { - return nullptr; - } - link = WrapperMethodLink(wmethod); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("WrapperMethodLinkResolve: resolved: %p -> %p", (void *)link.GetResolved()->ets_method, - (void *)link.GetResolved()); - return wmethod; -} - -[[maybe_unused]] static void DumpInvoke(WrapperMethod *wmethod, std::vector &args) -{ - auto klass_name = wmethod->ets_method->GetClass()->GetName(); - auto method_name = reinterpret_cast(wmethod->ets_method->GetName().data); - std::string msg = "DumpInvoke: " + klass_name + "::" + method_name + " with args:"; - - for (size_t i = 0; i < args.size(); ++i) { - msg += "\n[" + std::to_string(i) + "] = " + std::to_string(args[i].GetAsLong()); - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("%s", msg.c_str()); -} - -// internal call on wrapped object -static bool WrapperCtorInit(napi_env env, napi_callback_info cinfo, size_t argc, WrapperClass *wclass, - mem::Reference *ets_ref) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("WrapperCtorInit"); - auto coro = EtsCoroutine::GetCurrent(); - auto ctx = InteropCtx::Current(coro); - [[maybe_unused]] EtsJSNapiEnvScope envscope(ctx, env); - auto &wmethod_link = wclass->ets_init; - auto wmethod = WrapperMethodLinkResolve(env, wmethod_link); - if (UNLIKELY(wmethod == nullptr)) { - return false; - } - - if (UNLIKELY(wmethod->wclass_args.size() != argc)) { - InteropCtx::ThrowJSError(env, "constructor called in wrong context"); - return false; - } - - auto &ets_args = ctx->GetTempArgs(argc + 1); // this - auto &js_args = ctx->GetTempArgs(argc); - - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, js_args.data(), nullptr, nullptr)); - - ets_args[0] = panda::Value(ctx->Refstor()->Get(ets_ref)); // this - - for (size_t i = 0; i < argc; ++i) { - auto &wtype = wmethod->wclass_args[i]; - auto res = wtype.conv.unwrap(env, &wtype, js_args[i], ets_args[i + 1]); - if (UNLIKELY(!res)) { - return false; - } - } - - wmethod->ets_method->Invoke(coro, ets_args.data()); - if (UNLIKELY(coro->HasPendingException())) { - NAPI_FATAL_IF("exception proxy not implemented"); - return false; - } - return true; -} - -template -napi_value CallWrapperMethod(napi_env env, napi_callback_info cinfo) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("CallWrapperMethod static=%d", static_cast(IS_STATIC)); - auto coro = EtsCoroutine::GetCurrent(); - auto ctx = InteropCtx::Current(coro); - [[maybe_unused]] EtsJSNapiEnvScope envscope(ctx, env); - static constexpr auto ARGS_START = static_cast(!IS_STATIC); - - size_t constexpr ARGS_OPT_SZ = 16U; - size_t argc = ARGS_OPT_SZ; - auto &ets_args = ctx->GetTempArgs(argc); - auto &js_args = ctx->GetTempArgs(argc); - napi_value athis; - void *data; - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, js_args.data(), &athis, &data)); - - WrapperMethod *wmethod; - - if constexpr (IS_FUNC) { - wmethod = reinterpret_cast(data); - } else { - auto wmethod_link = reinterpret_cast(data); - wmethod = WrapperMethodLinkResolve(env, *wmethod_link); - if (UNLIKELY(wmethod == nullptr)) { - return nullptr; - } - } - - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("CallWrapperMethod: %s", reinterpret_cast(wmethod->ets_method->GetName().data)); - - if (UNLIKELY(wmethod->wclass_args.size() != argc)) { - InteropCtx::ThrowJSError(env, "wrong argc: " + std::to_string(wmethod->wclass_args.size()) + " expected"); - return nullptr; - } - - if (UNLIKELY(argc > js_args.size())) { - ets_args.resize(argc + ARGS_START); - js_args.resize(argc); - NAPI_CHECK_FATAL(napi_get_cb_info(env, cinfo, &argc, js_args.data(), &athis, &data)); - } - - ScopedManagedCodeThreadSwitch managed_scope(coro); - - if constexpr (!IS_STATIC) { - auto &wtype = wmethod->wclass_this; - auto res = wtype.conv.unwrap(env, &wtype, athis, ets_args[0]); - if (UNLIKELY(!res)) { - return nullptr; - } - } - auto &wargs = wmethod->wclass_args; - for (size_t i = 0; i < argc; ++i) { - auto &wtype = wargs[i]; - auto res = wtype.conv.unwrap(env, &wtype, js_args[i], ets_args[i + ARGS_START]); - if (UNLIKELY(!res)) { - return nullptr; - } - } - - panda::Value ets_res = wmethod->ets_method->Invoke(coro, ets_args.data()); - if (UNLIKELY(coro->HasPendingException())) { - NAPI_FATAL_IF("exception proxy not implemented"); - return nullptr; - } - auto &wtype_ret = wmethod->wclass_ret; - - return wtype_ret.conv.wrap(env, &wtype_ret, ets_res); -} - -class WrapperMethodCreator final : public EtsMethodVisitor { -public: - WrapperMethodCreator(napi_env env, panda::Method *method) : EtsMethodVisitor(method), env_(env) - { - NAPI_FATAL_IF(WrapperMethodCache::Lookup(method)); - - wmethod_ = WrapperMethodCache::Insert(method); - wmethod_->ets_method = method; - } - - WrapperMethod *ProcessMethod() - { - Process(); - if (Error()) { - WrapperMethodCache::Remove(wmethod_->ets_method); - return nullptr; - } - return wmethod_; - } - - WrapperMethod *ProcessFunction() - { - Process(); - if (Error()) { - WrapperMethodCache::Remove(wmethod_->ets_method); - return nullptr; - } - auto method = wmethod_->ets_method; - auto const method_name = method->GetName(); - napi_value js_func; - NAPI_CHECK_FATAL(napi_create_function(env_, reinterpret_cast(method_name.data), NAPI_AUTO_LENGTH, - &CallWrapperMethod, wmethod_, &js_func)); - NAPI_CHECK_FATAL(napi_create_reference(env_, js_func, 1, &wmethod_->js_func)); - return wmethod_; - } - -private: - void Process() - { - auto method = wmethod_->ets_method; - auto const n_args = method->GetNumArgs(); - wmethod_->wclass_args.reserve(n_args); - VisitMethod(); - TYPEVIS_ABRUPT_ON_ERROR(); - - if (!method->IsStatic()) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("vis this"); - wmethod_->wclass_this = GetConvertors(method->GetClass()); - TYPEVIS_ABRUPT_ON_ERROR(); - } - } - - WrapperType GetConvertors(panda::panda_file::Type type) - { - WrapperValueConvertors conv {}; - - switch (type.GetId()) { - case panda::panda_file::Type::TypeId::VOID: - conv = WrapperValueConvertorsVOID; - break; - case panda::panda_file::Type::TypeId::U1: - conv = WrapperValueConvertorsU1; - break; - case panda::panda_file::Type::TypeId::I32: - conv = WrapperValueConvertorsI32; - break; - case panda::panda_file::Type::TypeId::I64: - conv = WrapperValueConvertorsI64; - break; - case panda::panda_file::Type::TypeId::F32: - conv = WrapperValueConvertorsF32; - break; - case panda::panda_file::Type::TypeId::F64: - conv = WrapperValueConvertorsF64; - break; - default: - NAPI_FATAL_IF("not implemented"); - } - return {conv, nullptr}; - } - - WrapperType GetConvertors(panda::Class *klass) - { - ASSERT(klass != nullptr); - - if (klass->IsStringClass()) { - return {WrapperValueConvertorsSTRING, nullptr}; - } - - auto wclass = GetWrapperClass(env_, klass); - if (UNLIKELY(wclass == nullptr)) { - Error() = "wrapper class creation failed"; - return {}; - } - return {WrapperValueConvertorsOBJ, wclass}; - } - - void VisitReturn(panda::panda_file::Type type) override - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("VisitReturn: primitive"); - wmethod_->wclass_ret = GetConvertors(type); - TYPEVIS_ABRUPT_ON_ERROR(); - } - void VisitReturn(panda::Class *klass) override - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("VisitReturn: klass"); - wmethod_->wclass_ret = GetConvertors(klass); - TYPEVIS_ABRUPT_ON_ERROR(); - } - void VisitArgument([[maybe_unused]] uint32_t idx, panda::panda_file::Type type) override - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("VisitArgument: %u primitive", idx); - wmethod_->wclass_args.push_back(GetConvertors(type)); - TYPEVIS_ABRUPT_ON_ERROR(); - } - void VisitArgument([[maybe_unused]] uint32_t idx, panda::Class *klass) override - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("VisitArgument: %u klass", idx); - wmethod_->wclass_args.push_back(GetConvertors(klass)); - TYPEVIS_ABRUPT_ON_ERROR(); - } - - napi_env env_; - WrapperMethod *wmethod_ {}; -}; - -static WrapperMethod *GetWrapperFunction(napi_env env, panda::Method *method) -{ - auto wmethod = WrapperMethodCache::Lookup(method); - if (LIKELY(wmethod != nullptr)) { - return wmethod; - } - // call convertor - WrapperMethodCreator creator(env, method); - wmethod = creator.ProcessFunction(); - if (UNLIKELY(wmethod == nullptr)) { - InteropCtx::ThrowJSError(env, std::string("can't create proxy wrapper of function ") + - reinterpret_cast(method->GetName().data)); - return nullptr; - } - return wmethod; -} - -static WrapperMethod *GetWrapperMethod(napi_env env, panda::Method *method) -{ - auto wmethod = WrapperMethodCache::Lookup(method); - if (LIKELY(wmethod != nullptr)) { - return wmethod; - } - // call convertor - WrapperMethodCreator creator(env, method); - wmethod = creator.ProcessMethod(); - if (UNLIKELY(wmethod == nullptr)) { - InteropCtx::ThrowJSError(env, std::string("can't create proxy wrapper of method ") + - reinterpret_cast(method->GetName().data)); - return nullptr; - } - return wmethod; -} - -/******************************************************************************/ - -napi_value RegisterETSClassImpl(napi_env env, napi_value *jsargv, uint32_t jsargc) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("RegisterETSClass: enter"); - - if (jsargc != 1) { - InteropCtx::ThrowJSError(env, "RegisterETSClass: bad args"); - return nullptr; - } - - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - if (GetValueType(env, jsargv[0]) != napi_string) { - InteropCtx::ThrowJSError(env, "RegisterETSClass: class name is not a string"); - return nullptr; - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - std::string klass_name = GetString(env, jsargv[0]); - - auto coro = EtsCoroutine::GetCurrent(); - auto ets_klass = coro->GetPandaVM()->GetClassLinker()->GetClass(klass_name.c_str()); - if (UNLIKELY(ets_klass == nullptr)) { - InteropCtx::ThrowJSError(env, "RegisterETSClass: unresolved klass " + klass_name); - return nullptr; - } - - ScopedManagedCodeThreadSwitch managed_scope(coro); - auto wclass = GetWrapperClass(env, ets_klass->GetRuntimeClass()); - if (UNLIKELY(wclass == nullptr)) { - return nullptr; - } - - napi_value js_ctor; - NAPI_CHECK_FATAL(napi_get_reference_value(env, wclass->js_ctor, &js_ctor)); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("RegisterETSClass: exit"); - return js_ctor; -} - -napi_value RegisterETSFunctionImpl(napi_env env, napi_value *jsargv, uint32_t jsargc) -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("RegisterETSFunction: enter"); - - if (jsargc != 2) { - InteropCtx::ThrowJSError(env, "RegisterETSFunction: bad args"); - return nullptr; - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - if (GetValueType(env, jsargv[0]) != napi_string) { - InteropCtx::ThrowJSError(env, "RegisterETSFunction: function name is not a string"); - return nullptr; - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - if (GetValueType(env, jsargv[1]) != napi_string) { - InteropCtx::ThrowJSError(env, "RegisterETSFunction: function name is not a string"); - return nullptr; - } - - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - std::string class_descriptor = GetString(env, jsargv[0]); - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - std::string method_name = GetString(env, jsargv[1]); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG_A("RegisterETSFunction: method name: %s", method_name.c_str()); - - auto coro = EtsCoroutine::GetCurrent(); - - ScopedManagedCodeThreadSwitch managed_scope(coro); - - EtsClass *ets_class = coro->GetPandaVM()->GetClassLinker()->GetClass(class_descriptor.data()); - if (UNLIKELY(ets_class == nullptr)) { - InteropCtx::ThrowJSError(env, "RegisterETSFunctionImpl: unresolved class " + std::string(class_descriptor)); - return nullptr; - } - - EtsMethod *ets_method = ets_class->GetDirectMethod(method_name.data()); - if (UNLIKELY(ets_method == nullptr)) { - InteropCtx::ThrowJSError(env, "RegisterETSFunctionImpl: class " + std::string(class_descriptor) + - " doesn't contain " + std::string(method_name) + " method"); - return nullptr; - } - - auto wmethod = GetWrapperFunction(env, ets_method->GetPandaMethod()); - if (UNLIKELY(wmethod == nullptr)) { - return nullptr; - } - - napi_value js_func; - NAPI_CHECK_FATAL(napi_get_reference_value(env, wmethod->js_func, &js_func)); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - PROXY_DLOG("RegisterETSFunction: exit"); - return js_func; -} - -} // namespace panda::ets::interop::js diff --git a/plugins/ets/runtime/interop_js/ts2ets_proxy.h b/plugins/ets/runtime/interop_js/ts2ets_proxy.h deleted file mode 100644 index b6268676f..000000000 --- a/plugins/ets/runtime/interop_js/ts2ets_proxy.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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_TS2ETS_TS2ETS_PROXY_H_ -#define PANDA_PLUGINS_ETS_RUNTIME_TS2ETS_TS2ETS_PROXY_H_ - -#include - -namespace panda::ets::interop::js { - -/* - * Resolve ETS class by "name" and return something that appears like js class - * constructor for its proxy class. Constructor arguments, fields, methods match - * those in mirrored TS class with some restrictions of current implementation. - * - * TS common type: (name: string) : new (...args : any) => any - */ -napi_value RegisterETSClassImpl(napi_env env, napi_value *jsargv, uint32_t jsargc); - -/* - * Resolve ETS function by "name" and return something that appears like its js - * proxy function. Arguments and return value types match those in mirrored TS - * function with some restrictions of current implementation. - * - * TS common type: (name: string) : (...args : any) => any - */ -napi_value RegisterETSFunctionImpl(napi_env env, napi_value *jsargv, uint32_t jsargc); - -} // namespace panda::ets::interop::js - -#endif // !PANDA_PLUGINS_ETS_RUNTIME_TS2ETS_TS2ETS_PROXY_H_ -- Gitee