From 74daf11173d55846d4d339031e10c86c08bedb0a Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Mon, 9 Nov 2026 12:49:24 +0500 Subject: [PATCH 1/2] test(android): add ArrayBuffer.from cases to the marshalling benchmark --- .../app/src/main/assets/app/marshalling-benchmark.js | 2 ++ .../src/main/java/org/nativescript/Benchmarks.java | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/platforms/android/test-app/app/src/main/assets/app/marshalling-benchmark.js b/platforms/android/test-app/app/src/main/assets/app/marshalling-benchmark.js index 5799c149..bfbc3048 100644 --- a/platforms/android/test-app/app/src/main/assets/app/marshalling-benchmark.js +++ b/platforms/android/test-app/app/src/main/assets/app/marshalling-benchmark.js @@ -103,6 +103,8 @@ function buildSuite() { var cb = new org.nativescript.BenchmarkCallback({ onString: function (value) {} }); for (var i = 0; i < 1e4; i++) Benchmarks.invokeStringCallback(cb); } }, + { name: "ArrayBuffer.from direct ByteBuffer", iterations: 1e5, action: function () { var b = Benchmarks.returnDirectBuffer(); for (var i = 0; i < 1e5; i++) ArrayBuffer.from(b); } }, + { name: "ArrayBuffer.from heap ByteBuffer", iterations: 1e5, action: function () { var b = Benchmarks.returnHeapBuffer(); for (var i = 0; i < 1e5; i++) ArrayBuffer.from(b); } }, { name: "Invoke callback that throws", iterations: 5e4, action: function () { var cb = new org.nativescript.BenchmarkCallback({ onCallback: function () { throw new Error("benchmark"); } }); Benchmarks.invokeThrowingCallback(cb, 5e4); diff --git a/platforms/android/test-app/app/src/main/java/org/nativescript/Benchmarks.java b/platforms/android/test-app/app/src/main/java/org/nativescript/Benchmarks.java index d08537fa..71ceb39a 100644 --- a/platforms/android/test-app/app/src/main/java/org/nativescript/Benchmarks.java +++ b/platforms/android/test-app/app/src/main/java/org/nativescript/Benchmarks.java @@ -127,6 +127,18 @@ public static void invokeStringCallback(BenchmarkCallback callback) { callback.onString("hello"); } + private static final java.nio.ByteBuffer DIRECT_BUFFER = java.nio.ByteBuffer.allocateDirect(1024); + + private static final java.nio.ByteBuffer HEAP_BUFFER = java.nio.ByteBuffer.allocate(1024); + + public static java.nio.ByteBuffer returnDirectBuffer() { + return DIRECT_BUFFER; + } + + public static java.nio.ByteBuffer returnHeapBuffer() { + return HEAP_BUFFER; + } + public static int invokeThrowingCallback(BenchmarkCallback callback, int count) { int failures = 0; for (int i = 0; i < count; i++) { From 71b8c21a3bbb0ce1c18611b13cb0ec120167e8b1 Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Mon, 9 Nov 2026 12:49:24 +0500 Subject: [PATCH 2/2] perf(android): create ArrayBuffer.from without calling ByteBuffer.isDirect --- .../jni/jsi/conversion/ArrayBufferHelper.cpp | 16 +++--------- .../jni/jsi/conversion/ArrayBufferHelper.h | 1 - .../jni/napi/conversion/ArrayBufferHelper.cpp | 26 ++++++------------- .../jni/napi/conversion/ArrayBufferHelper.h | 1 - 4 files changed, 12 insertions(+), 32 deletions(-) diff --git a/NativeScript/ffi/jni/jsi/conversion/ArrayBufferHelper.cpp b/NativeScript/ffi/jni/jsi/conversion/ArrayBufferHelper.cpp index 7067854d..2879d14e 100644 --- a/NativeScript/ffi/jni/jsi/conversion/ArrayBufferHelper.cpp +++ b/NativeScript/ffi/jni/jsi/conversion/ArrayBufferHelper.cpp @@ -41,7 +41,7 @@ namespace { } ArrayBufferHelper::ArrayBufferHelper() - : m_objectManager(nullptr), m_ByteBufferClass(nullptr), m_isDirectMethodID(nullptr), + : m_objectManager(nullptr), m_ByteBufferClass(nullptr), m_remainingMethodID(nullptr), m_getMethodID(nullptr) { } @@ -103,19 +103,11 @@ JsValue ArrayBufferHelper::CreateFromCallbackImpl(JsRuntime &rt, const JsValue * throw NativeScriptException("Wrong type of argument (ByteBuffer expected)"); } - if (m_isDirectMethodID == nullptr) { - m_isDirectMethodID = jEnv.GetMethodID(m_ByteBufferClass, "isDirect", "()Z"); - assert(m_isDirectMethodID != nullptr); - } - - auto ret = jEnv.CallBooleanMethod(obj, m_isDirectMethodID); - - auto isDirectBuffer = ret == JNI_TRUE; - std::shared_ptr buffer; - if (isDirectBuffer) { - auto data = jEnv.GetDirectBufferAddress(obj); + auto data = jEnv.GetDirectBufferAddress(obj); + + if (data != nullptr) { auto size = jEnv.GetDirectBufferCapacity(obj); buffer = std::make_shared(static_cast(data), (size_t) size); diff --git a/NativeScript/ffi/jni/jsi/conversion/ArrayBufferHelper.h b/NativeScript/ffi/jni/jsi/conversion/ArrayBufferHelper.h index d55ba45e..2c8ec678 100644 --- a/NativeScript/ffi/jni/jsi/conversion/ArrayBufferHelper.h +++ b/NativeScript/ffi/jni/jsi/conversion/ArrayBufferHelper.h @@ -17,7 +17,6 @@ namespace tns { ObjectManager* m_objectManager; jclass m_ByteBufferClass; - jmethodID m_isDirectMethodID; jmethodID m_remainingMethodID; jmethodID m_getMethodID; }; diff --git a/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.cpp b/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.cpp index ac5a0a7f..e229a3e2 100644 --- a/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.cpp +++ b/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.cpp @@ -6,7 +6,7 @@ using namespace tns; ArrayBufferHelper::ArrayBufferHelper() - : m_objectManager(nullptr), m_ByteBufferClass(nullptr), m_isDirectMethodID(nullptr), + : m_objectManager(nullptr), m_ByteBufferClass(nullptr), m_remainingMethodID(nullptr), m_getMethodID(nullptr) { } @@ -80,19 +80,11 @@ napi_value ArrayBufferHelper::CreateFromCallbackImpl(napi_env env, size_t argc, throw NativeScriptException("Wrong type of argument (ByteBuffer expected)"); } - if (m_isDirectMethodID == nullptr) { - m_isDirectMethodID = jEnv.GetMethodID(m_ByteBufferClass, "isDirect", "()Z"); - assert(m_isDirectMethodID != nullptr); - } - - auto ret = jEnv.CallBooleanMethod(obj, m_isDirectMethodID); - - auto isDirectBuffer = ret == JNI_TRUE; - napi_value arrayBuffer; - if (isDirectBuffer) { - auto data = jEnv.GetDirectBufferAddress(obj); + auto data = jEnv.GetDirectBufferAddress(obj); + + if (data != nullptr) { auto size = jEnv.GetDirectBufferCapacity(obj); void* externalData = data; @@ -117,19 +109,17 @@ napi_value ArrayBufferHelper::CreateFromCallbackImpl(napi_env env, size_t argc, auto byteArrayElements = jEnv.GetByteArrayElements(byteArray, 0); - jbyte* data = new jbyte[bufferRemainingSize]; - memcpy(data, byteArrayElements, bufferRemainingSize); + jbyte* copy = new jbyte[bufferRemainingSize]; + memcpy(copy, byteArrayElements, bufferRemainingSize); - NAPI_GUARD(napi_create_external_arraybuffer(env, data, bufferRemainingSize, [](napi_env env, void* finalize_data, void* finalize_hint) { + NAPI_GUARD(napi_create_external_arraybuffer(env, copy, bufferRemainingSize, [](napi_env env, void* finalize_data, void* finalize_hint) { delete[] static_cast(finalize_data); }, nullptr, &arrayBuffer)) {} jEnv.ReleaseByteArrayElements(byteArray, byteArrayElements, 0); } - napi_value nativeObjectKey; - NAPI_GUARD(napi_create_string_utf8(env, "nativeObject", NAPI_AUTO_LENGTH, &nativeObjectKey)) {} - NAPI_GUARD(napi_set_property(env, arrayBuffer, nativeObjectKey, argObj)) {} + NAPI_GUARD(napi_set_named_property(env, arrayBuffer, "nativeObject", argObj)) {} return arrayBuffer; } \ No newline at end of file diff --git a/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.h b/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.h index cccd172c..9cb321d5 100644 --- a/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.h +++ b/NativeScript/ffi/jni/napi/conversion/ArrayBufferHelper.h @@ -19,7 +19,6 @@ namespace tns { ObjectManager* m_objectManager; jclass m_ByteBufferClass; - jmethodID m_isDirectMethodID; jmethodID m_remainingMethodID; jmethodID m_getMethodID; };