diff --git a/plugins/ets/runtime/ets_vm.h b/plugins/ets/runtime/ets_vm.h index ed500973fb92596c3aab42bdc3ec7ce657dd8fa5..12033beb684bec76597bbd36c207427b3f850c92 100644 --- a/plugins/ets/runtime/ets_vm.h +++ b/plugins/ets/runtime/ets_vm.h @@ -241,12 +241,11 @@ public: static PandaEtsVM *FromExternalData(void *external_data) { ASSERT(external_data != nullptr); - return reinterpret_cast(reinterpret_cast(external_data) - - MEMBER_OFFSET(PandaEtsVM, external_data_)); + return reinterpret_cast(ToUintPtr(external_data) - MEMBER_OFFSET(PandaEtsVM, external_data_)); } struct alignas(16) ExternalData { // NOLINT(readability-magic-numbers) - static constexpr size_t SIZE = 256U; + static constexpr size_t SIZE = 512U; uint8_t data[SIZE]; // NOLINT(modernize-avoid-c-arrays) }; diff --git a/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp b/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp index d717a2671c49c13faa725ccdeb1d2faf3a118c40..a95fa05d4246775be63d8e9b677082302faa3da7 100644 --- a/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp +++ b/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp @@ -18,6 +18,7 @@ #include "plugins/ets/runtime/ets_vm_api.h" #include "plugins/ets/runtime/types/ets_method.h" #include "plugins/ets/runtime/interop_js/js_value.h" +#include "plugins/ets/runtime/interop_js/js_convert.h" #include "plugins/ets/runtime/interop_js/ts2ets_common.h" #include "plugins/ets/runtime/interop_js/ets_vm_plugin.h" #include "plugins/ets/runtime/interop_js/ts2ets_copy.h" @@ -54,8 +55,10 @@ static napi_value Version(napi_env env, [[maybe_unused]] napi_callback_info info return result; } -static VMHandle CreateJSException(EtsCoroutine *coro, GlobalCtx *ctx, JSValue *jsvalue) +EtsObject *JsPlugin::CreateJSException(EtsCoroutine *coro, JSValue *jsvalue) { + auto ctx = GetContext(); + [[maybe_unused]] HandleScope scope(coro); VMHandle jsvalue_handle(coro, jsvalue->GetCoreType()); auto cls = ctx->JSExceptionClass(); @@ -71,10 +74,14 @@ static VMHandle CreateJSException(EtsCoroutine *coro, GlobalCtx *c std::array args {Value(exc_handle.GetPtr()), Value(jsvalue_handle.GetPtr())}; ctor->InvokeVoid(coro, args.data()); - return exc_handle; + auto res = EtsObject::FromCoreType(exc_handle.GetPtr()); + if (UNLIKELY(coro->HasPendingException())) { + return nullptr; + } + return res; } -void JsPlugin::ThrowETSException(JSValue *jsvalue) +void JsPlugin::ThrowETSException(napi_value val) { auto coro = EtsCoroutine::GetCurrent(); auto ctx = GlobalCtx::Current(coro); @@ -86,13 +93,11 @@ void JsPlugin::ThrowETSException(JSValue *jsvalue) } ASSERT(!coro->HasPendingException()); - [[maybe_unused]] HandleScope scope(coro); - auto exc_handle = CreateJSException(coro, ctx, jsvalue); - - if (LIKELY(!coro->HasPendingException())) { // Exception raised while CreateJSException - ASSERT(exc_handle.GetPtr()); - coro->SetException(exc_handle.GetPtr()); - } + auto exc = JSConvertJSException::Unwrap(ctx, ctx->GetJSEnv(), val); + if (LIKELY(exc.has_value())) { + ASSERT(exc != nullptr); + coro->SetException(exc.value()->GetCoreType()); + } // otherwise exception is already set } void JsPlugin::ThrowETSException(char const *msg) @@ -132,13 +137,8 @@ void JsPlugin::ForwardEtsException(EtsCoroutine *coro) coro->ClearException(); auto klass = exc->GetClass(); if (LIKELY(klass->GetRuntimeClass() == ctx->JSExceptionClass())) { - // TODO(vpukhov): remove call after adding a mirror-class for JSException - auto method = exc->GetClass()->GetMethod("getValue"); - ASSERT(method != nullptr); - std::array args = {Value(exc->GetCoreType())}; - auto val = JSValue::FromCoreType(method->GetPandaMethod()->Invoke(coro, args.data()).GetAs()); - ASSERT(val != nullptr); - JsPlugin::ThrowJSException(env, val->GetNapiValue(env)); + napi_value js_exc = JSConvertJSException::Wrap(env, exc); + JsPlugin::ThrowJSException(env, js_exc); return; } // TODO(vpukhov): put proxy-object instead @@ -151,7 +151,7 @@ void JsPlugin::ForwardJSException([[maybe_unused]] EtsCoroutine *coro) auto env = ctx->GetJSEnv(); napi_value excval; NAPI_CHECK_FATAL(napi_get_and_clear_last_exception(env, &excval)); - JsPlugin::ThrowETSException(JSValue::CreateNapiValue(ctx, excval)); + JsPlugin::ThrowETSException(excval); } void JSConvertTypeCheckFailed(char const *type_name) diff --git a/plugins/ets/runtime/interop_js/ets_vm_plugin.h b/plugins/ets/runtime/interop_js/ets_vm_plugin.h index 04bb333f696b04acd8cc5a4ee9716ed91758c387..1da35446178cf557e23491b753d6ad157eaabd85 100644 --- a/plugins/ets/runtime/interop_js/ets_vm_plugin.h +++ b/plugins/ets/runtime/interop_js/ets_vm_plugin.h @@ -43,7 +43,14 @@ public: return &ctx_; } - static void ThrowETSException(JSValue *jsvalue); + static JsPlugin *FromContext(GlobalCtx *ctx) + { + return reinterpret_cast(ToUintPtr(ctx) - MEMBER_OFFSET(JsPlugin, ctx_)); + } + + EtsObject *CreateJSException(EtsCoroutine *coro, JSValue *jsvalue); + + static void ThrowETSException(napi_value val); static void ThrowETSException(char const *msg); static void ThrowETSException(std::string const &msg) { diff --git a/plugins/ets/runtime/interop_js/global_context.cpp b/plugins/ets/runtime/interop_js/global_context.cpp index 052630621a3c353f166ae1bd83d4118d6444ee4c..6c597c2b45c2877cea14c038812116d94e298144 100644 --- a/plugins/ets/runtime/interop_js/global_context.cpp +++ b/plugins/ets/runtime/interop_js/global_context.cpp @@ -15,6 +15,7 @@ #include "plugins/ets/runtime/interop_js/global_context.h" +#include "interop_js/js_convert.h" #include "plugins/ets/runtime/ets_class_linker_extension.h" #include "plugins/ets/runtime/ets_vm.h" #include "plugins/ets/runtime/interop_js/ets_vm_plugin.h" @@ -24,6 +25,43 @@ namespace panda::ets::interop::js { +// JSRefConvert adapter for builtin reference types +template +class JSRefConvertBuiltin : public JSRefConvert { +public: + JSRefConvertBuiltin() : JSRefConvert(this) {} + + napi_value WrapImpl(JsPlugin *plugin, EtsObject *obj) + { + auto ctx = plugin->GetContext(); + using ObjType = std::remove_pointer_t; + return Conv::Wrap(ctx->GetJSEnv(), FromEtsObject(obj)); + } + + EtsObject *UnwrapImpl(JsPlugin *plugin, napi_value js_value) + { + auto ctx = plugin->GetContext(); + auto res = Conv::Unwrap(ctx, ctx->GetJSEnv(), js_value); + if (!res) { + return nullptr; + } + return AsEtsObject(res.value()); + } +}; + +template +static inline void RegisterBuiltinRefConvertor(JSRefConvertCache *cache, Class *klass) +{ + cache->Insert(klass, std::unique_ptr(new JSRefConvertBuiltin())); +} + +void GlobalCtx::CacheClass(Class **klass, const char *descriptor) +{ + EtsClassLinker *ets_class_linker = JsPlugin::FromContext(this)->GetPandaEtsVM()->GetClassLinker(); + *klass = ets_class_linker->GetClass(descriptor)->GetRuntimeClass(); + ASSERT(*klass != nullptr); +} + GlobalCtx::GlobalCtx(EtsCoroutine *coro) { PandaEtsVM *vm = coro->GetPandaVM(); @@ -31,15 +69,16 @@ GlobalCtx::GlobalCtx(EtsCoroutine *coro) refstor_ = vm->GetGlobalObjectStorage(); linker_ctx_ = ets_class_linker->GetEtsClassLinkerExtension()->GetBootContext(); - jsruntime_class_ = ets_class_linker->GetClass("Lstd/interop/js/JSRuntime;")->GetRuntimeClass(); - jsvalue_class_ = ets_class_linker->GetClass("Lstd/interop/js/JSValue;")->GetRuntimeClass(); - jsexception_class_ = ets_class_linker->GetClass("Lstd/interop/js/JSException;")->GetRuntimeClass(); - promise_class_ = ets_class_linker->GetPromiseClass()->GetRuntimeClass(); + CacheClass(&jsruntime_class_, "Lstd/interop/js/JSRuntime;"); + CacheClass(&jsvalue_class_, "Lstd/interop/js/JSValue;"); + CacheClass(&jsexception_class_, "Lstd/interop/js/JSException;"); + CacheClass(&string_class_, "Lstd/core/String;"); + CacheClass(&promise_class_, "Lstd/core/Promise;"); - ASSERT(refstor_ != nullptr); - ASSERT(jsvalue_class_ != nullptr); - ASSERT(jsexception_class_ != nullptr); - ASSERT(promise_class_ != nullptr); + RegisterBuiltinRefConvertor(&refconvert_cache_, jsvalue_class_); + RegisterBuiltinRefConvertor(&refconvert_cache_, jsexception_class_); + RegisterBuiltinRefConvertor(&refconvert_cache_, string_class_); + RegisterBuiltinRefConvertor(&refconvert_cache_, promise_class_); { auto method = EtsClass::FromRuntimeClass(jsruntime_class_)->GetMethod("createFinalizationQueue"); diff --git a/plugins/ets/runtime/interop_js/global_context.h b/plugins/ets/runtime/interop_js/global_context.h index 7039f0e5a64e71f8bf46b74e2dfaec8cc0480911..159e80cd496cd88657a481f93af06163b4d4838d 100644 --- a/plugins/ets/runtime/interop_js/global_context.h +++ b/plugins/ets/runtime/interop_js/global_context.h @@ -17,6 +17,7 @@ #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_GLOBAL_CONTEXT_H_ #include "plugins/ets/runtime/ets_coroutine.h" +#include "plugins/ets/runtime/interop_js/js_refconvert.h" #include "libpandabase/macros.h" #include "runtime/include/value.h" #include @@ -104,16 +105,6 @@ public: return refstor_; } - Class *JSValueClass() const - { - return jsvalue_class_; - } - - Class *JSExceptionClass() const - { - return jsexception_class_; - } - ClassLinkerContext *LinkerCtx() const { return linker_ctx_; @@ -145,6 +136,26 @@ public: return args; } + JSRefConvertCache *GetRefConvertCache() + { + return &refconvert_cache_; + } + + Class *JSValueClass() const + { + return jsvalue_class_; + } + + Class *JSExceptionClass() const + { + return jsexception_class_; + } + + Class *GetStringClass() const + { + return string_class_; + } + Class *GetPromiseClass() const { return promise_class_; @@ -163,9 +174,14 @@ private: // Temporary storages for arg convertors, std::tuple...> std::tuple, std::vector, std::vector> tmp_args_; + JSRefConvertCache refconvert_cache_; + + void CacheClass(Class **klass, const char *descriptor); + Class *jsruntime_class_ {}; Class *jsvalue_class_ {}; Class *jsexception_class_ {}; + Class *string_class_ {}; Class *promise_class_ {}; Method *jsvalue_fqueue_register_ {}; diff --git a/plugins/ets/runtime/interop_js/js_convert.h b/plugins/ets/runtime/interop_js/js_convert.h index b541ce24d5dc855af555f0da589c0d0a4574af86..bd0a962d2a9f66548806f60fa5320cfd85c38b9b 100644 --- a/plugins/ets/runtime/interop_js/js_convert.h +++ b/plugins/ets/runtime/interop_js/js_convert.h @@ -16,7 +16,8 @@ #ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_CONVERT_H_ #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_CONVERT_H_ -#include "plugins/ets/runtime/ets_vm_api.h" +#include "plugins/ets/runtime/ets_panda_file_items.h" +#include "plugins/ets/runtime/interop_js/ets_vm_plugin.h" #include "plugins/ets/runtime/interop_js/ts2ets_common.h" #include "plugins/ets/runtime/interop_js/js_value.h" #include "runtime/handle_scope-inl.h" @@ -24,22 +25,41 @@ #include "plugins/ets/runtime/types/ets_string.h" #include "plugins/ets/runtime/types/ets_promise.h" #include "plugins/ets/runtime/interop_js/pending_promise_listener.h" +#include "plugins/ets/runtime/types/ets_method.h" namespace panda::ets::interop::js { -void JSConvertTypeCheckFailed(char const *type_name); +template +inline EtsObject *AsEtsObject(T *obj) +{ + static_assert(std::is_base_of_v); + return reinterpret_cast(obj); +} + +template +inline T *FromEtsObject(EtsObject *obj) +{ + static_assert(std::is_base_of_v); + return reinterpret_cast(obj); +} + +void JSConvertTypeCheckFailed(const char *type_name); +// Conversion interface for primitive types and some built-in classes, has no state // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define JSCONVERT_DEFINE_TYPE(type, cpptype_) \ struct JSConvert##type { \ using cpptype = cpptype_; \ - static constexpr char const *type_name = #type; \ + static constexpr const char *type_name = #type; \ + JSConvert##type() = delete; \ [[maybe_unused]] static void TypeCheckFailed() \ { \ JSConvertTypeCheckFailed(type_name); \ } \ + /* Must not fail */ \ [[maybe_unused]] static inline napi_value Wrap([[maybe_unused]] napi_env env, \ [[maybe_unused]] cpptype ets_val); \ + /* May fail */ \ [[maybe_unused]] static inline std::optional Unwrap([[maybe_unused]] GlobalCtx *ctx, \ [[maybe_unused]] napi_env env, \ [[maybe_unused]] napi_value js_val); \ @@ -136,6 +156,37 @@ JSCONVERT_UNWRAP(JSValue) return JSValue::CreateNapiValue(ctx, js_val); } +JSCONVERT_DEFINE_TYPE(JSException, EtsObject *) // TODO(vpukhov): associate with js Error? +JSCONVERT_WRAP(JSException) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto ctx = JsPlugin::GetCurrent(coro)->GetContext(); + + auto klass = ets_val->GetClass(); + NAPI_FATAL_IF(klass->GetRuntimeClass() != ctx->JSExceptionClass()); + + // TODO(vpukhov): remove call after adding a mirror-class for JSException + auto method = klass->GetMethod("getValue"); + ASSERT(method != nullptr); + std::array args = {Value(ets_val->GetCoreType())}; + auto val = JSValue::FromCoreType(method->GetPandaMethod()->Invoke(coro, args.data()).GetAs()); + NAPI_FATAL_IF(val == nullptr); + return val->GetNapiValue(env); +} +JSCONVERT_UNWRAP(JSException) +{ + auto coro = EtsCoroutine::GetCurrent(); + auto value = JSValue::CreateNapiValue(ctx, js_val); + if (UNLIKELY(value == nullptr)) { + return {}; + } + auto res = JsPlugin::FromContext(ctx)->CreateJSException(coro, value); + if (UNLIKELY(coro->HasPendingException())) { + return {}; + } + return res; +} + JSCONVERT_DEFINE_TYPE(Promise, EtsPromise *) JSCONVERT_WRAP(Promise) diff --git a/plugins/ets/runtime/interop_js/js_refconvert.h b/plugins/ets/runtime/interop_js/js_refconvert.h new file mode 100644 index 0000000000000000000000000000000000000000..4337f2e3c4bd6b486af1122e690ad229d99b25e9 --- /dev/null +++ b/plugins/ets/runtime/interop_js/js_refconvert.h @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_REFCONVERT_H_ +#define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_REFCONVERT_H_ + +#include "plugins/ets/runtime/ets_coroutine.h" +#include "libpandabase/macros.h" +#include +#include + +namespace panda::ets::interop::js { + +class JsPlugin; + +// Conversion interface for some panda::Class objects +class JSRefConvert { +public: + // Must not fail + napi_value Wrap(JsPlugin *ctx, EtsObject *obj) + { + return (this->*(this->wrap_))(ctx, obj); + } + + // May fail + EtsObject *Unwrap(JsPlugin *ctx, napi_value js_value) + { + return (this->*(this->unwrap_))(ctx, js_value); + } + + JSRefConvert() = delete; + NO_COPY_SEMANTIC(JSRefConvert); + NO_MOVE_SEMANTIC(JSRefConvert); + virtual ~JSRefConvert() = default; + +protected: + template + explicit JSRefConvert(D * /*unused*/) + : wrap_(static_cast(&D::WrapImpl)), unwrap_(static_cast(&D::UnwrapImpl)) + { + } + +private: + using WrapT = decltype(&JSRefConvert::Wrap); + using UnwrapT = decltype(&JSRefConvert::Unwrap); + + WrapT wrap_; + UnwrapT unwrap_; +}; + +// Fast cache to find convertor for some panda::Class +class JSRefConvertCache { +public: + JSRefConvert *Lookup(Class *klass) + { + auto *entry = GetDirCacheEntry(klass); + if (LIKELY(entry->klass == klass)) { + return entry->value; + } + auto value = LookupFull(klass); + *entry = {klass, value}; + return value; + } + + void Insert(Class *klass, std::unique_ptr value) + { + auto owned_value = value.get(); + auto [it, inserted] = cache_.insert_or_assign(klass, std::move(value)); + ASSERT(inserted); + (void)inserted; + *GetDirCacheEntry(klass) = {klass, owned_value}; + } + + JSRefConvertCache() : dircache_(new DirCachePair[DIRCACHE_SZ]) {} + ~JSRefConvertCache() = default; + NO_COPY_SEMANTIC(JSRefConvertCache); + NO_MOVE_SEMANTIC(JSRefConvertCache); + +private: + __attribute__((noinline)) JSRefConvert *LookupFull(Class *klass) + { + auto it = cache_.find(klass); + if (UNLIKELY(it == cache_.end())) { + return nullptr; + } + return it->second.get(); + } + + struct DirCachePair { + Class *klass {}; + JSRefConvert *value {}; + }; + + static constexpr uint32_t DIRCACHE_SZ = 1024; + + DirCachePair *GetDirCacheEntry(Class *klass) + { + static_assert(helpers::math::IsPowerOfTwo(DIRCACHE_SZ)); + auto hash = helpers::math::PowerOfTwoTableSlot(ToUintPtr(klass), DIRCACHE_SZ, + GetLogAlignment(alignof(Class))); + return &dircache_[hash]; + } + + std::unique_ptr dircache_; // NOLINT(modernize-avoid-c-arrays) + std::unordered_map> cache_; +}; + +} // namespace panda::ets::interop::js + +#endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_REFCONVERT_H_ diff --git a/plugins/ets/runtime/interop_js/js_value.h b/plugins/ets/runtime/interop_js/js_value.h index 7da3c19ab6ec4a57fddfa6bde8657e516f64ccab..1abe809319c3cb887d4ca622d9096ebbc3e0a573 100644 --- a/plugins/ets/runtime/interop_js/js_value.h +++ b/plugins/ets/runtime/interop_js/js_value.h @@ -44,9 +44,14 @@ public: return FromEtsObject(EtsObject::FromCoreType(object)); } - EtsObject *GetEtsType() + EtsObject *AsObject() { - return this; + return reinterpret_cast(this); + } + + const EtsObject *AsObject() const + { + return reinterpret_cast(this); } ObjectHeader *GetCoreType() const diff --git a/plugins/ets/runtime/interop_js/js_value_call.cpp b/plugins/ets/runtime/interop_js/js_value_call.cpp index 3f73bbf585f444374a6f54fe603717d94bd5e5d0..d484d4bf1246daa8d7171962d4edb0dbf9913687 100644 --- a/plugins/ets/runtime/interop_js/js_value_call.cpp +++ b/plugins/ets/runtime/interop_js/js_value_call.cpp @@ -41,11 +41,7 @@ template return false; } if constexpr (std::is_pointer_v) { - if constexpr (std::is_convertible_v) { - store_ref(res.value()); - } else { - store_ref(res.value()->GetCoreType()); - } + store_ref(AsEtsObject(res.value())->GetCoreType()); } else { store_prim(Value(res.value()).GetAsLong()); } @@ -68,17 +64,28 @@ template } case panda_file::Type::TypeId::REFERENCE: { auto klass = resolve_ref_cls(); + // start fastpath if (klass == ctx->JSValueClass()) { return unwrap_val(helpers::TypeIdentity()); } - if (klass->IsStringClass()) { + if (klass == ctx->GetStringClass()) { return unwrap_val(helpers::TypeIdentity()); } - NapiFatal("ConvertNapiVal: unknown reftype class"); + // start slowpath + auto refconv = ctx->GetRefConvertCache()->Lookup(klass); + if (UNLIKELY(refconv == nullptr)) { + NapiFatal(std::string("ConvertNapiVal: unknown class: ") + utf::Mutf8AsCString(klass->GetDescriptor())); + } + ObjectHeader *res = refconv->Unwrap(JsPlugin::FromContext(ctx), js_val)->GetCoreType(); + if (UNLIKELY(res == nullptr)) { + return false; + } + store_ref(res); + return true; } default: { - NapiFatal("ConvertNapiVal: unsupported typeid " + - std::string(panda_file::Type::GetSignatureByTypeId(type))); + NapiFatal(std::string("ConvertNapiVal: unsupported typeid ") + + panda_file::Type::GetSignatureByTypeId(type)); } } UNREACHABLE(); @@ -99,12 +106,7 @@ static ALWAYS_INLINE inline void ConvertEtsVal(GlobalCtx *ctx, [[maybe_unused]] auto wrap_ref = [&](auto conv_tag, ObjectHeader *ref) -> void { using Convertor = typename decltype(conv_tag)::type; // conv_tag acts as lambda template parameter using cpptype = typename Convertor::cpptype; // NOLINT(readability-identifier-naming) - cpptype value; - if constexpr (std::is_convertible_v) { - value = static_cast(ref); - } else { - value = std::remove_pointer_t::FromEtsObject(EtsObject::FromCoreType(ref)); - } + cpptype value = std::remove_pointer_t::FromEtsObject(EtsObject::FromCoreType(ref)); store_res(Convertor::Wrap(env, value)); }; @@ -123,22 +125,26 @@ static ALWAYS_INLINE inline void ConvertEtsVal(GlobalCtx *ctx, [[maybe_unused]] return wrap_prim(helpers::TypeIdentity()); } case panda_file::Type::TypeId::REFERENCE: { - auto ref = read_val(helpers::TypeIdentity()); + ObjectHeader *ref = read_val(helpers::TypeIdentity()); auto klass = ref->template ClassAddr(); ASSERT(klass == resolve_ref_cls()); + // start fastpath if (klass == ctx->JSValueClass()) { return wrap_ref(helpers::TypeIdentity(), ref); } - if (klass->IsStringClass()) { + if (klass == ctx->GetStringClass()) { return wrap_ref(helpers::TypeIdentity(), ref); } - if (klass == ctx->GetPromiseClass()) { - return wrap_ref(helpers::TypeIdentity(), ref); + // start slowpath + auto refconv = ctx->GetRefConvertCache()->Lookup(klass); + if (UNLIKELY(refconv == nullptr)) { + NapiFatal(std::string("ConvertEtsVal: unknown class: ") + utf::Mutf8AsCString(klass->GetDescriptor())); } - NapiFatal("ConvertEtsVal: unknown reftype class"); + store_res(refconv->Wrap(JsPlugin::FromContext(ctx), EtsObject::FromCoreType(ref))); + return; } default: { - NapiFatal("ConvertEtsVal: unsupported typeid " + std::string(panda_file::Type::GetSignatureByTypeId(type))); + NapiFatal(std::string("ConvertEtsVal: unsupported typeid ") + panda_file::Type::GetSignatureByTypeId(type)); } } UNREACHABLE(); @@ -173,7 +179,7 @@ static ALWAYS_INLINE inline napi_value EtsCallImpl(EtsCoroutine *coro, GlobalCtx auto const num_args = method->GetNumArgs() - ETS_ARGS_DISP; auto &ets_args = ctx->GetTempArgs(method->GetNumArgs()); { - panda::HandleScope ets_handle_scope(coro); + HandleScope ets_handle_scope(coro); auto &ets_boxed_args = ctx->GetTempArgs(num_args); @@ -183,8 +189,8 @@ static ALWAYS_INLINE inline napi_value EtsCallImpl(EtsCoroutine *coro, GlobalCtx auto js_val = jsargv[arg_idx]; 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 = [&](panda::ObjectHeader *obj) { - uintptr_t addr = panda::VMHandle(coro, obj).GetAddress(); + auto store_ref = [&](ObjectHeader *obj) { + uintptr_t addr = VMHandle(coro, obj).GetAddress(); ets_boxed_args[arg_idx] = reinterpret_cast(addr); }; if (UNLIKELY(!ConvertNapiVal(ctx, cls_resolver, store_prim, store_ref, type, js_val))) { @@ -417,7 +423,7 @@ static ALWAYS_INLINE inline uint64_t JSValueJSCallImpl(Method *method, uint8_t * return 0; } - panda::Value ets_ret; + Value ets_ret; if constexpr (IS_NEWCALL) { NAPI_FATAL_IF(resolve_ref_cls(0) != ctx->JSValueClass()); @@ -425,12 +431,12 @@ static ALWAYS_INLINE inline uint64_t JSValueJSCallImpl(Method *method, uint8_t * if (!res.has_value()) { NapiFatal("newcall result unwrap failed, but shouldnt"); } - ets_ret = panda::Value(res.value()->GetEtsType()->GetCoreType()); + ets_ret = Value(res.value()->GetCoreType()); } else { panda_file::Type type = method->GetReturnType(); auto cls_resolver = [&]() { return resolve_ref_cls(0); }; auto store_prim = [&](uint64_t val) { ets_ret = Value(val); }; - auto store_ref = [&](panda::ObjectHeader *obj) { ets_ret = Value(obj); }; + auto store_ref = [&](ObjectHeader *obj) { ets_ret = Value(obj); }; if (UNLIKELY(!ConvertNapiVal(ctx, cls_resolver, store_prim, store_ref, type, js_ret))) { JsPlugin::ForwardJSException(coro); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) @@ -459,12 +465,12 @@ extern "C" uint64_t JSValueJSNew(Method *method, uint8_t *args, uint8_t *in_stac return JSValueJSCallImpl(method, args, in_stack_args); } -extern "C" void JSValueJSCallBridge(panda::Method *method, ...); -extern "C" void JSValueJSCallByValueBridge(panda::Method *method, ...); -extern "C" void JSValueJSNewBridge(panda::Method *method, ...); +extern "C" void JSValueJSCallBridge(Method *method, ...); +extern "C" void JSValueJSCallByValueBridge(Method *method, ...); +extern "C" void JSValueJSNewBridge(Method *method, ...); template -static void InitJSCallSignatures(panda::coretypes::String *cls_str) +static void InitJSCallSignatures(coretypes::String *cls_str) { auto coro = EtsCoroutine::GetCurrent(); auto ctx = GlobalCtx::Current(coro); diff --git a/plugins/ets/runtime/interop_js/ts2ets_tstype.cpp b/plugins/ets/runtime/interop_js/ts2ets_tstype.cpp index ebb2af712679bdec791b3f1f84cf541ddf117bde..2b400dbd1c35777b2e3b64c266ca3ee462797db3 100644 --- a/plugins/ets/runtime/interop_js/ts2ets_tstype.cpp +++ b/plugins/ets/runtime/interop_js/ts2ets_tstype.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "plugins/ets/runtime/ets_vm_api.h" #include "plugins/ets/runtime/interop_js/napi_env_scope.h" #include "plugins/ets/runtime/interop_js/js_convert.h" #include "runtime/include/class_linker-inl.h" @@ -355,7 +356,7 @@ ALWAYS_INLINE inline uint64_t TSTypeCall(Method *method, uint8_t *args, uint8_t class_linker->InitializeClass(coro, klass); } JSValue *value = JSValue::CreateTSTypeDerived(klass, env, js_ret); - ets_ret = panda::Value(value->GetEtsType()->GetCoreType()); + ets_ret = panda::Value(value->GetCoreType()); break; } NapiFatal("unsupported reftype"); diff --git a/plugins/ets/runtime/types/ets_promise.h b/plugins/ets/runtime/types/ets_promise.h index 86853732d6153a41ebbc162338b3b37a4cb1e84d..32b80825bdfabf6f8f344e54a9ce01a93935bc1b 100644 --- a/plugins/ets/runtime/types/ets_promise.h +++ b/plugins/ets/runtime/types/ets_promise.h @@ -49,6 +49,11 @@ public: return reinterpret_cast(promise); } + ObjectHeader *GetCoreType() + { + return reinterpret_cast(this); + } + EtsObject *AsObject() { return EtsObject::FromCoreType(this); @@ -59,6 +64,11 @@ public: return EtsObject::FromCoreType(this); } + static EtsPromise *FromEtsObject(EtsObject *promise) + { + return reinterpret_cast(promise); + } + EtsObjectArray *GetThenQueue(EtsCoroutine *coro) { return EtsObjectArray::FromCoreType(