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 6d982607d3bf2927545996689263080c50dcb9af..6a2d2cc358da7726780afe2e96fb877b8bb36126 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 0063ae61d146d9d6f8158869f7df6541033b1e1c..25c6d6664d946e2b727680eb43c9f57f734c35ff 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,8 @@ #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 #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 +30,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 +1404,64 @@ JSValue *JSRuntimeGetPropertyJSValueyByKey(JSValue *objectValue, JSValue *keyVal return res.value(); } +class CallbackHintProxyFromDynamicToStatic { +public: + 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(); @@ -1422,16 +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)); + bool detached = false; + napi_is_detached_arraybuffer(env, dynamicArrayBuffer, &detached); + if (detached) { + ctx->ThrowETSError(coro, "Failed to detach ArrayBuffer: it's already detached"); + } - [[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)); - return arrayBuffer; + return TransferArrayBufferToStaticImpl(coro, dynamicArrayBuffer); } EtsObject *TransferArrayBufferToDynamic(EtsEscompatArrayBuffer *staticArrayBuffer) diff --git a/static_core/plugins/ets/runtime/intrinsics/escompat_TypedArrays.cpp b/static_core/plugins/ets/runtime/intrinsics/escompat_TypedArrays.cpp index 9c63c9b4751d4bc49abd2870f4d420295b5d606f..0a015c9dd1d80d0c7adda491fd9ae002d4d7726d 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