From 51b40444c16a716937c5dc5ef497d2679cc78d99 Mon Sep 17 00:00:00 2001 From: MockMockBlack Date: Thu, 15 May 2025 15:27:06 +0800 Subject: [PATCH 1/3] [Builtin] TypedArray external buffer support Modified EtsEscompatTypedArrayGet and EtsEscompatTypedArraySet to support using ArrayBuffer created from external buffer(not in object buffer). Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/IC81RE Signed-off-by: MockMockBlack Change-Id: I4384a4c2d3d2325352200479a26ae49ba0cea442 --- .../intrinsics/escompat_TypedArrays.cpp | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/static_core/plugins/ets/runtime/intrinsics/escompat_TypedArrays.cpp b/static_core/plugins/ets/runtime/intrinsics/escompat_TypedArrays.cpp index 9c63c9b475..0a015c9dd1 100644 --- a/static_core/plugins/ets/runtime/intrinsics/escompat_TypedArrays.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/escompat_TypedArrays.cpp @@ -16,8 +16,10 @@ #include #include #include "ets_handle.h" +#include "libpandabase/mem/mem.h" #include "libpandabase/utils/utf.h" #include "libpandabase/utils/utils.h" +#include "plugins/ets/runtime/types/ets_primitives.h" #include "plugins/ets/runtime/types/ets_typed_arrays.h" #include "plugins/ets/runtime/types/ets_typed_unsigned_arrays.h" #include "plugins/ets/runtime/intrinsics/helpers/ets_intrinsics_helpers.h" @@ -69,15 +71,15 @@ static void EtsEscompatTypedArraySet(T *thisArray, EtsInt pos, typename T::Eleme * us from proceeding */ // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + // Boundary Check if (UNLIKELY(pos < 0 || pos >= thisArray->GetLengthInt())) { EtsCoroutine *coro = EtsCoroutine::GetCurrent(); ThrowEtsException(coro, panda_file_items::class_descriptors::RANGE_ERROR, "invalid index"); return; } - // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) - ObjectAccessor::SetPrimitive( - // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) - data, pos * sizeof(typename T::ElementType) + static_cast(thisArray->GetByteOffset()), val); + + auto *dataPtr = ToVoidPtr(ToUintPtr(data) + static_cast(thisArray->GetByteOffset())); + reinterpret_cast(dataPtr)[pos] = val; } template @@ -95,21 +97,14 @@ typename T::ElementType EtsEscompatTypedArrayGet(T *thisArray, EtsInt pos) * us from proceeding */ // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + // Boundary Check if (UNLIKELY(pos < 0 || pos >= thisArray->GetLengthInt())) { EtsCoroutine *coro = EtsCoroutine::GetCurrent(); ThrowEtsException(coro, panda_file_items::class_descriptors::RANGE_ERROR, "invalid index"); return 0; } - /** - * False-positive static-analyzer report: - * GC can happen only on ThrowException in GetNativeData. - * But such case meaning data to be nullptr and retun prevents - * us from proceeding - */ - // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) - return ObjectAccessor::GetPrimitive( - // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) - data, pos * sizeof(typename T::ElementType) + static_cast(thisArray->GetByteOffset())); + auto *dataPtr = ToVoidPtr(ToUintPtr(data) + static_cast(thisArray->GetByteOffset())); + return reinterpret_cast(dataPtr)[pos]; } template -- Gitee From e01c44677348817a374fda278c7f87b41c6b8dbd Mon Sep 17 00:00:00 2001 From: MockMockBlack Date: Mon, 26 May 2025 11:14:02 +0800 Subject: [PATCH 2/3] [Interop] transfer dynamic arraybuffer Support transfer dynamic arraybuffer with zero copy impl. Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/ICAGV5 Change-Id: I31df7234ac06219e14f64999b49282db05b8b80c Signed-off-by: MockMockBlack --- .../ets/runtime/interop_js/ets_vm_plugin.cpp | 36 ++++++++++++++++++ .../interop_js/intrinsics_api_impl.cpp | 38 ++++++++++++++++--- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/static_core/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp b/static_core/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp index 6d982607d3..6a2d2cc358 100644 --- a/static_core/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp +++ b/static_core/plugins/ets/runtime/interop_js/ets_vm_plugin.cpp @@ -14,6 +14,10 @@ */ #include +#include +#include +#include +#include #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" @@ -140,6 +144,36 @@ static napi_value GetEtsModule(napi_env env, napi_callback_info info) return ets_proxy::GetETSModule(env, moduleName); } +napi_value GetExternalArrayBuffer([[maybe_unused]] napi_env env, [[maybe_unused]] napi_callback_info info) +{ + EtsCoroutine *coro = EtsCoroutine::GetCurrent(); + // InteropCtx *ctx = InteropCtx::Current(coro); + INTEROP_CODE_SCOPE_JS_TO_ETS(coro); + ScopedManagedCodeThread managedScope(coro); + + napi_value arrayBuffer; + size_t length = 8; + void *data = malloc(sizeof(uint8_t) * length); + void *hint = malloc(sizeof(uint32_t)); + *(uint32_t *)hint = 0xCAFEBEEF; + + napi_create_external_buffer( + env, length, data, + []([[maybe_unused]] napi_env env, void *data, void *hint) { + free(data); + printf("this is a free callback the developer push to napi\n"); + printf("freeing data %p\n", data); + printf("the hint is %u\n", *(uint32_t *)hint); + // if (hint != nullptr) { + // free(hint); + // } + }, + hint, &arrayBuffer); + printf("I'm in GetExternalArrayBuffer\n"); + + return arrayBuffer; +} + static std::optional> GetArgStrings(napi_env env, napi_value options, bool needFakeArgv0 = true) { @@ -335,6 +369,8 @@ static napi_value Init(napi_env env, napi_value exports) napi_property_descriptor {"getClass", 0, GetEtsClass, 0, 0, 0, napi_enumerable, 0}, napi_property_descriptor {"getInstance", 0, GetEtsInstance, 0, 0, 0, napi_enumerable, 0}, napi_property_descriptor {"getModule", 0, GetEtsModule, 0, 0, 0, napi_enumerable, 0}, + napi_property_descriptor {"getExternalArrayBuffer", 0, GetExternalArrayBuffer, 0, 0, 0, napi_enumerable, 0}, + }; NAPI_CHECK_FATAL(napi_define_properties(env, exports, desc.size(), desc.data())); diff --git a/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp b/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp index 0063ae61d1..cc7ae2c05d 100644 --- a/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp +++ b/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp @@ -19,6 +19,7 @@ #include "plugins/ets/runtime/interop_js/js_value.h" #include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h" +#include #include "plugins/ets/runtime/interop_js/code_scopes.h" #include "plugins/ets/runtime/interop_js/logger.h" #include "plugins/ets/runtime/types/ets_string.h" @@ -28,8 +29,8 @@ // NOLINTBEGIN(readability-identifier-naming) // CC-OFFNXT(G.FMT.10-CPP) project code style -napi_status __attribute__((weak)) -napi_load_module_with_module_request(napi_env env, const char *request_name, napi_value *result); +napi_status __attribute__((weak)) napi_load_module_with_module_request(napi_env env, const char *request_name, + napi_value *result); // NOLINTEND(readability-identifier-naming) static bool StringStartWith(std::string_view str, std::string_view startStr) @@ -1402,6 +1403,13 @@ JSValue *JSRuntimeGetPropertyJSValueyByKey(JSValue *objectValue, JSValue *keyVal return res.value(); } +class TransferStaticArrayBufferCallbackHint { +public: + napi_env env = nullptr; + napi_finalize finalizeCb = nullptr; + void *finalizeHint = nullptr; +}; + EtsEscompatArrayBuffer *TransferArrayBufferToStatic(ESValue *object) { auto coro = EtsCoroutine::GetCurrent(); @@ -1427,10 +1435,30 @@ EtsEscompatArrayBuffer *TransferArrayBufferToStatic(ESValue *object) // NOTE(dslynko, #23919): finalize semantics of resizable ArrayBuffers NAPI_CHECK_FATAL(napi_get_arraybuffer_info(env, dynamicArrayBuffer, &data, &byteLength)); + TransferStaticArrayBufferCallbackHint *finalizeHint = new TransferStaticArrayBufferCallbackHint(); + finalizeHint->env = env; + NAPI_CHECK_FATAL(napi_get_arraybuffer_finalizer(env, dynamicArrayBuffer, &finalizeHint->finalizeCb, + &finalizeHint->finalizeHint)); + + // detach the ArrayBuffer from the JS engine to prevent it from being finalized + NAPI_CHECK_FATAL(napi_detach_arraybuffer(env, dynamicArrayBuffer)); + // reset the finalizer to prevent the hint being freed by the JS engine + NAPI_CHECK_FATAL(napi_set_arraybuffer_finalizer(env, dynamicArrayBuffer, nullptr, nullptr)); + + bool detached = false; + napi_is_detached_arraybuffer(env, dynamicArrayBuffer, &detached); + printf("ArrayBuffer detached: %s\n", detached ? "true" : "false"); + + // TODO: only handle the external ArrayBuffer for now [[maybe_unused]] EtsHandleScope s(coro); - void *etsData = nullptr; - auto *arrayBuffer = EtsEscompatArrayBuffer::Create(coro, byteLength, &etsData); - std::copy_n(reinterpret_cast(data), byteLength, reinterpret_cast(etsData)); + auto *arrayBuffer = EtsEscompatArrayBuffer::Create( + coro, data, byteLength, + [](void *finalizeData, void *finalizeHint) { + auto *hint = reinterpret_cast(finalizeHint); + hint->finalizeCb(hint->env, finalizeData, hint->finalizeHint); + // delete hint; + }, + finalizeHint); return arrayBuffer; } -- Gitee From ad40fb133bad5bce83d83fd73ef33ef394983c4c Mon Sep 17 00:00:00 2001 From: MockMockBlack Date: Wed, 2 Jul 2025 11:42:25 +0800 Subject: [PATCH 3/3] [Interop] Support transfer with zero copy Change-Id: I3f4607f6c00b389971c005d5b4b35e02a56e763e Signed-off-by: MockMockBlack --- .../interop_js/intrinsics_api_impl.cpp | 89 ++++++++++++------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp b/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp index cc7ae2c05d..25c6d6664d 100644 --- a/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp +++ b/static_core/plugins/ets/runtime/interop_js/intrinsics_api_impl.cpp @@ -20,6 +20,7 @@ #include "plugins/ets/runtime/interop_js/interop_common.h" #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h" #include +#include #include "plugins/ets/runtime/interop_js/code_scopes.h" #include "plugins/ets/runtime/interop_js/logger.h" #include "plugins/ets/runtime/types/ets_string.h" @@ -1403,13 +1404,64 @@ JSValue *JSRuntimeGetPropertyJSValueyByKey(JSValue *objectValue, JSValue *keyVal return res.value(); } -class TransferStaticArrayBufferCallbackHint { +class CallbackHintProxyFromDynamicToStatic { public: - napi_env env = nullptr; napi_finalize finalizeCb = nullptr; void *finalizeHint = nullptr; }; +static EtsEscompatArrayBuffer *TransferArrayBufferToStaticImpl(EtsCoroutine *coro, napi_value dynamicArrayBuffer) +{ + auto ctx = InteropCtx::Current(coro); + auto env = ctx->GetJSEnv(); + + void *data = nullptr; + size_t byteLength = 0; + napi_finalize finalizeCb = nullptr; + void *finalizeHint = nullptr; + bool isExternalArrayBuffer = false; + + NAPI_CHECK_FATAL(napi_release_buffer_arraybuffer(env, dynamicArrayBuffer, &data, &byteLength, &finalizeCb, + &finalizeHint, &isExternalArrayBuffer)); + + if (!isExternalArrayBuffer) { + // internal ArrayBuffer + // the data is already detached from the JS engine + // just create a new finilizer for the buffer + [[maybe_unused]] EtsHandleScope s(coro); + auto *arrayBuffer = EtsEscompatArrayBuffer::Create( + coro, data, byteLength, + [](void *finalizeData, void *_) { + ASSERT(finalizeData != nullptr); + free(finalizeData); + + printf("MOCK: this is a mock finalizer for internal ArrayBuffer\n"); + }, + nullptr); + return arrayBuffer; + + } else { + // external ArrayBuffer + // we need to proxy the finalizer that user provided + CallbackHintProxyFromDynamicToStatic *finalizeHint = new CallbackHintProxyFromDynamicToStatic(); + finalizeHint->finalizeCb = finalizeCb; + finalizeHint->finalizeHint = finalizeHint; + + [[maybe_unused]] EtsHandleScope s(coro); + auto *arrayBuffer = EtsEscompatArrayBuffer::Create( + coro, data, byteLength, + [](void *finalizeData, void *finalizeHint) { + auto *hint = reinterpret_cast(finalizeHint); + hint->finalizeCb(nullptr, finalizeData, hint->finalizeHint); + // delete hint; + }, + finalizeHint); + return arrayBuffer; + } +} + +// static EtsEscompatArrayBuffer *TransferExternalArrayBufferToStatic(ESValue *object) {} + EtsEscompatArrayBuffer *TransferArrayBufferToStatic(ESValue *object) { auto coro = EtsCoroutine::GetCurrent(); @@ -1430,36 +1482,13 @@ EtsEscompatArrayBuffer *TransferArrayBufferToStatic(ESValue *object) ctx->ThrowETSError(coro, "Dynamic object is not arraybuffer"); } - void *data = nullptr; - size_t byteLength = 0; - // NOTE(dslynko, #23919): finalize semantics of resizable ArrayBuffers - NAPI_CHECK_FATAL(napi_get_arraybuffer_info(env, dynamicArrayBuffer, &data, &byteLength)); - - TransferStaticArrayBufferCallbackHint *finalizeHint = new TransferStaticArrayBufferCallbackHint(); - finalizeHint->env = env; - NAPI_CHECK_FATAL(napi_get_arraybuffer_finalizer(env, dynamicArrayBuffer, &finalizeHint->finalizeCb, - &finalizeHint->finalizeHint)); - - // detach the ArrayBuffer from the JS engine to prevent it from being finalized - NAPI_CHECK_FATAL(napi_detach_arraybuffer(env, dynamicArrayBuffer)); - // reset the finalizer to prevent the hint being freed by the JS engine - NAPI_CHECK_FATAL(napi_set_arraybuffer_finalizer(env, dynamicArrayBuffer, nullptr, nullptr)); - bool detached = false; napi_is_detached_arraybuffer(env, dynamicArrayBuffer, &detached); - printf("ArrayBuffer detached: %s\n", detached ? "true" : "false"); - - // TODO: only handle the external ArrayBuffer for now - [[maybe_unused]] EtsHandleScope s(coro); - auto *arrayBuffer = EtsEscompatArrayBuffer::Create( - coro, data, byteLength, - [](void *finalizeData, void *finalizeHint) { - auto *hint = reinterpret_cast(finalizeHint); - hint->finalizeCb(hint->env, finalizeData, hint->finalizeHint); - // delete hint; - }, - finalizeHint); - return arrayBuffer; + if (detached) { + ctx->ThrowETSError(coro, "Failed to detach ArrayBuffer: it's already detached"); + } + + return TransferArrayBufferToStaticImpl(coro, dynamicArrayBuffer); } EtsObject *TransferArrayBufferToDynamic(EtsEscompatArrayBuffer *staticArrayBuffer) -- Gitee