From adf024d063138f6e98fe20dbc9246623c62f0d8e Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:23:20 +0200 Subject: [PATCH 01/13] feat: Add `jsi::ArrayBuffer::getMutableBuffer()` --- API/hermes/TracingRuntime.cpp | 5 ++++ API/hermes/TracingRuntime.h | 2 ++ API/hermes/hermes.cpp | 19 +++++++++++++++ API/hermes_abi/HermesABIRuntimeWrapper.cpp | 2 ++ API/hermes_sandbox/HermesSandboxRuntime.cpp | 4 ++++ API/jsi/jsi/decorator.h | 8 +++++++ API/jsi/jsi/jsi.h | 9 +++++++ include/hermes/VM/JSArrayBuffer.h | 7 ++++++ lib/VM/JSArrayBuffer.cpp | 26 +++++++++++++++++++++ 9 files changed, 82 insertions(+) diff --git a/API/hermes/TracingRuntime.cpp b/API/hermes/TracingRuntime.cpp index 36dc1cc0c41..9920f3be4b4 100644 --- a/API/hermes/TracingRuntime.cpp +++ b/API/hermes/TracingRuntime.cpp @@ -879,6 +879,11 @@ jsi::ArrayBuffer TracingRuntime::createArrayBuffer( throw std::logic_error("Cannot create external ArrayBuffers in trace mode."); } +std::shared_ptr TracingRuntime::getMutableBuffer( + const jsi::ArrayBuffer& buffer) { + throw std::logic_error("Cannot get external ArrayBuffers in trace mode."); +} + size_t TracingRuntime::size(const jsi::Array &arr) { // Array size inquiries read from the length property, which is // non-configurable and thus cannot have side effects. diff --git a/API/hermes/TracingRuntime.h b/API/hermes/TracingRuntime.h index dc26fcb0c4e..f7f4d4c7fd5 100644 --- a/API/hermes/TracingRuntime.h +++ b/API/hermes/TracingRuntime.h @@ -121,6 +121,8 @@ class TracingRuntime : public jsi::RuntimeDecorator { jsi::Array createArray(size_t length) override; jsi::ArrayBuffer createArrayBuffer( std::shared_ptr buffer) override; + std::shared_ptr getMutableBuffer( + const jsi::ArrayBuffer& buffer) override; size_t size(const jsi::Array &arr) override; size_t size(const jsi::ArrayBuffer &buf) override; diff --git a/API/hermes/hermes.cpp b/API/hermes/hermes.cpp index f74e2340f4f..7c7a9dfd180 100644 --- a/API/hermes/hermes.cpp +++ b/API/hermes/hermes.cpp @@ -725,6 +725,8 @@ class HermesRuntimeImpl final : public HermesRuntime, jsi::Array createArray(size_t length) override; jsi::ArrayBuffer createArrayBuffer( std::shared_ptr buffer) override; + std::shared_ptr getMutableBuffer( + const jsi::ArrayBuffer& buffer) override; size_t size(const jsi::Array &) override; size_t size(const jsi::ArrayBuffer &) override; uint8_t *data(const jsi::ArrayBuffer &) override; @@ -2410,6 +2412,23 @@ uint8_t *HermesRuntimeImpl::data(const jsi::ArrayBuffer &arr) { return ab->getDataBlock(runtime_); } +std::shared_ptr HermesRuntimeImpl::getMutableBuffer( + const jsi::ArrayBuffer& arr) { + auto buf = arrayBufferHandle(arr); + if (LLVM_UNLIKELY(!buf->attached())) + throw jsi::JSINativeException("ArrayBuffer is detached."); + + void* context = nullptr; + auto res = vm::JSArrayBuffer::getExternalDataBlock( + runtime_, buf, &context); + if (context == nullptr) + return nullptr; + auto mutableBuffer = dynamic_cast *>(context); + if (LLVM_UNLIKELY(mutableBuffer == nullptr)) + throw jsi::JSINativeException("ArrayBuffer's external data block is not a jsi::MutableBuffer!"); + return mutableBuffer; +} + jsi::Value HermesRuntimeImpl::getValueAtIndex(const jsi::Array &arr, size_t i) { vm::GCScope gcScope(runtime_); if (LLVM_UNLIKELY(i >= size(arr))) { diff --git a/API/hermes_abi/HermesABIRuntimeWrapper.cpp b/API/hermes_abi/HermesABIRuntimeWrapper.cpp index 405d1af5672..446cb7f7233 100644 --- a/API/hermes_abi/HermesABIRuntimeWrapper.cpp +++ b/API/hermes_abi/HermesABIRuntimeWrapper.cpp @@ -944,6 +944,8 @@ class HermesABIRuntimeWrapper : public Runtime { return intoJSIArrayBuffer(vtable_->create_arraybuffer_from_external_data( abiRt_, new MutableBufferWrapper(std::move(buffer)))); } + std::shared_ptr getMutableBuffer( + const ArrayBuffer& buffer) override; size_t size(const Array &arr) override { return vtable_->get_array_length(abiRt_, toABIArray(arr)); } diff --git a/API/hermes_sandbox/HermesSandboxRuntime.cpp b/API/hermes_sandbox/HermesSandboxRuntime.cpp index ded57ed162b..6d171bc4e0f 100644 --- a/API/hermes_sandbox/HermesSandboxRuntime.cpp +++ b/API/hermes_sandbox/HermesSandboxRuntime.cpp @@ -2051,6 +2051,10 @@ class HermesSandboxRuntimeImpl : public facebook::hermes::HermesSandboxRuntime, std::shared_ptr buffer) override { THROW_UNIMPLEMENTED(); } + std::shared_ptr getMutableBuffer( + const ArrayBuffer& buffer) override { + THROW_UNIMPLEMENTED(); + } size_t size(const Array &arr) override { return vt_.get_array_length(this, srt_, toSandboxArray(arr).pointer); } diff --git a/API/jsi/jsi/decorator.h b/API/jsi/jsi/decorator.h index 6410257b250..3b68776850b 100644 --- a/API/jsi/jsi/decorator.h +++ b/API/jsi/jsi/decorator.h @@ -361,6 +361,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { std::shared_ptr buffer) override { return plain_.createArrayBuffer(std::move(buffer)); }; + std::shared_ptr getMutableBuffer( + const ArrayBuffer& buffer) override { + return plain_.getMutableBuffer(buffer); + } size_t size(const Array& a) override { return plain_.size(a); }; @@ -894,6 +898,10 @@ class WithRuntimeDecorator : public RuntimeDecorator { std::shared_ptr buffer) override { return RD::createArrayBuffer(std::move(buffer)); }; + std::shared_ptr getMutableBuffer( + const ArrayBuffer& buffer) override { + return RD::getMutableBuffer(buffer); + } size_t size(const Array& a) override { Around around{with_}; return RD::size(a); diff --git a/API/jsi/jsi/jsi.h b/API/jsi/jsi/jsi.h index ed8d37f265b..f2c4cae0949 100644 --- a/API/jsi/jsi/jsi.h +++ b/API/jsi/jsi/jsi.h @@ -499,6 +499,8 @@ class JSI_EXPORT Runtime : public ICast { virtual Array createArray(size_t length) = 0; virtual ArrayBuffer createArrayBuffer( std::shared_ptr buffer) = 0; + virtual std::shared_ptr getMutableBuffer( + const ArrayBuffer& buffer) = 0; virtual size_t size(const Array&) = 0; virtual size_t size(const ArrayBuffer&) = 0; virtual uint8_t* data(const ArrayBuffer&) = 0; @@ -1234,6 +1236,13 @@ class JSI_EXPORT ArrayBuffer : public Object { return runtime.data(*this); } + /// \return the underlying MutableBuffer if this ArrayBuffer + /// was created with one. + /// This returns nullptr if it does not carry a MutableBuffer. + std::shared_ptr getMutableBuffer() const { + return runtime.getMutableBuffer(*this); + } + private: friend class Object; friend class Value; diff --git a/include/hermes/VM/JSArrayBuffer.h b/include/hermes/VM/JSArrayBuffer.h index 566d939d650..44d1a8df5ac 100644 --- a/include/hermes/VM/JSArrayBuffer.h +++ b/include/hermes/VM/JSArrayBuffer.h @@ -79,6 +79,13 @@ class JSArrayBuffer final : public JSObject { void *context, FinalizeNativeStatePtr finalizePtr); + /// Gets the external data block that this JSArrayBuffer holds if there is any. + /// If this JSArrayBuffer does not hold external data, this returns an error. + static ExecutionStatus getExternalDataBlock( + Runtime &runtime, + Handle self, + void **context); + /// Retrieves a pointer to the held buffer. /// \return A pointer to the buffer owned by this object. This can be null /// if the ArrayBuffer is empty. diff --git a/lib/VM/JSArrayBuffer.cpp b/lib/VM/JSArrayBuffer.cpp index 043b902cf13..1ebf4249616 100644 --- a/lib/VM/JSArrayBuffer.cpp +++ b/lib/VM/JSArrayBuffer.cpp @@ -274,5 +274,31 @@ ExecutionStatus JSArrayBuffer::setExternalDataBlock( return ExecutionStatus::RETURNED; } +ExecutionStatus JSArrayBuffer::setExternalDataBlock( + Runtime &runtime, + Handle self, + void **context +) { + NamedPropertyDescriptor desc; + bool exists = JSObject::getOwnNamedDescriptor( + self, + runtime, + Predefined::getSymbolID(Predefined::InternalPropertyArrayBufferExternalFinalizer), + desc); + (void)exists; + if (!exists) { + // JSArrayBuffer does not hold an external data block + return ExecutionStatus::ExecutionFailed; + } + assert(exists && "JSArrayBuffer"); + // Raw pointers below. + NoAllocScope scope(runtime_); + NativeState *ns = vmcast( + JSObject::getNamedSlotValueUnsafe(*h, runtime_, desc) + .getObject(runtime_)); + context = ns->context(); + return ExecutionStatus::RETURNED; +} + } // namespace vm } // namespace hermes From 7e0e8fcf4680ea0498a62ff72d8ea40cf967fefd Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:26:38 +0200 Subject: [PATCH 02/13] whoops --- API/hermes/hermes.cpp | 2 +- lib/VM/JSArrayBuffer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/API/hermes/hermes.cpp b/API/hermes/hermes.cpp index 7c7a9dfd180..7ed8940ef97 100644 --- a/API/hermes/hermes.cpp +++ b/API/hermes/hermes.cpp @@ -2426,7 +2426,7 @@ std::shared_ptr HermesRuntimeImpl::getMutableBuffer( auto mutableBuffer = dynamic_cast *>(context); if (LLVM_UNLIKELY(mutableBuffer == nullptr)) throw jsi::JSINativeException("ArrayBuffer's external data block is not a jsi::MutableBuffer!"); - return mutableBuffer; + return *mutableBuffer; } jsi::Value HermesRuntimeImpl::getValueAtIndex(const jsi::Array &arr, size_t i) { diff --git a/lib/VM/JSArrayBuffer.cpp b/lib/VM/JSArrayBuffer.cpp index 1ebf4249616..9545d996864 100644 --- a/lib/VM/JSArrayBuffer.cpp +++ b/lib/VM/JSArrayBuffer.cpp @@ -274,7 +274,7 @@ ExecutionStatus JSArrayBuffer::setExternalDataBlock( return ExecutionStatus::RETURNED; } -ExecutionStatus JSArrayBuffer::setExternalDataBlock( +ExecutionStatus JSArrayBuffer::getExternalDataBlock( Runtime &runtime, Handle self, void **context From 42af3520f3756c5fc1614124e4bc9b52d82d30d7 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:28:29 +0200 Subject: [PATCH 03/13] Update jsi.h --- API/jsi/jsi/jsi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API/jsi/jsi/jsi.h b/API/jsi/jsi/jsi.h index f2c4cae0949..439d478f920 100644 --- a/API/jsi/jsi/jsi.h +++ b/API/jsi/jsi/jsi.h @@ -1239,7 +1239,7 @@ class JSI_EXPORT ArrayBuffer : public Object { /// \return the underlying MutableBuffer if this ArrayBuffer /// was created with one. /// This returns nullptr if it does not carry a MutableBuffer. - std::shared_ptr getMutableBuffer() const { + std::shared_ptr getMutableBuffer(Runtime& runtime) const { return runtime.getMutableBuffer(*this); } From a355b8894529c2a69649a5e5529bea47dc3b0415 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:35:12 +0200 Subject: [PATCH 04/13] Update JSArrayBuffer.cpp --- lib/VM/JSArrayBuffer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/VM/JSArrayBuffer.cpp b/lib/VM/JSArrayBuffer.cpp index 9545d996864..016def7a31a 100644 --- a/lib/VM/JSArrayBuffer.cpp +++ b/lib/VM/JSArrayBuffer.cpp @@ -279,18 +279,18 @@ ExecutionStatus JSArrayBuffer::getExternalDataBlock( Handle self, void **context ) { + assert(attached() && "Buffer must be attached"); + NamedPropertyDescriptor desc; bool exists = JSObject::getOwnNamedDescriptor( self, runtime, Predefined::getSymbolID(Predefined::InternalPropertyArrayBufferExternalFinalizer), desc); - (void)exists; if (!exists) { // JSArrayBuffer does not hold an external data block - return ExecutionStatus::ExecutionFailed; + return ExecutionStatus::EXCEPTION; } - assert(exists && "JSArrayBuffer"); // Raw pointers below. NoAllocScope scope(runtime_); NativeState *ns = vmcast( From 9bda2477ffd8bb8529d387342f41240c8a1335d1 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:35:38 +0200 Subject: [PATCH 05/13] Update JSArrayBuffer.cpp --- lib/VM/JSArrayBuffer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/VM/JSArrayBuffer.cpp b/lib/VM/JSArrayBuffer.cpp index 016def7a31a..10d6d295d6b 100644 --- a/lib/VM/JSArrayBuffer.cpp +++ b/lib/VM/JSArrayBuffer.cpp @@ -292,10 +292,10 @@ ExecutionStatus JSArrayBuffer::getExternalDataBlock( return ExecutionStatus::EXCEPTION; } // Raw pointers below. - NoAllocScope scope(runtime_); + NoAllocScope scope(runtime); NativeState *ns = vmcast( - JSObject::getNamedSlotValueUnsafe(*h, runtime_, desc) - .getObject(runtime_)); + JSObject::getNamedSlotValueUnsafe(*h, runtime, desc) + .getObject(runtime)); context = ns->context(); return ExecutionStatus::RETURNED; } From a1d74fd1ce1dd2bba65009a50602001da1d5f138 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:36:45 +0200 Subject: [PATCH 06/13] reinterpret cast --- API/hermes/hermes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API/hermes/hermes.cpp b/API/hermes/hermes.cpp index 7ed8940ef97..183dd50f4ab 100644 --- a/API/hermes/hermes.cpp +++ b/API/hermes/hermes.cpp @@ -2423,7 +2423,7 @@ std::shared_ptr HermesRuntimeImpl::getMutableBuffer( runtime_, buf, &context); if (context == nullptr) return nullptr; - auto mutableBuffer = dynamic_cast *>(context); + auto mutableBuffer = reinterpret_cast *>(context); if (LLVM_UNLIKELY(mutableBuffer == nullptr)) throw jsi::JSINativeException("ArrayBuffer's external data block is not a jsi::MutableBuffer!"); return *mutableBuffer; From 08e002a194a08adb657c069952b35ffba71b098e Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:46:14 +0200 Subject: [PATCH 07/13] fix: Assignment and add tests --- lib/VM/JSArrayBuffer.cpp | 2 +- unittests/API/APITest.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/VM/JSArrayBuffer.cpp b/lib/VM/JSArrayBuffer.cpp index 10d6d295d6b..11982376357 100644 --- a/lib/VM/JSArrayBuffer.cpp +++ b/lib/VM/JSArrayBuffer.cpp @@ -296,7 +296,7 @@ ExecutionStatus JSArrayBuffer::getExternalDataBlock( NativeState *ns = vmcast( JSObject::getNamedSlotValueUnsafe(*h, runtime, desc) .getObject(runtime)); - context = ns->context(); + *context = ns->context(); return ExecutionStatus::RETURNED; } diff --git a/unittests/API/APITest.cpp b/unittests/API/APITest.cpp index 1f2160a0e62..97d27b01afc 100644 --- a/unittests/API/APITest.cpp +++ b/unittests/API/APITest.cpp @@ -199,6 +199,23 @@ TEST_F(HermesRuntimeTestMethodsTest, ExternalArrayBufferTest) { rt->instrumentation().collectGarbage(""); EXPECT_TRUE(weakBuf.expired()); } + + { + auto buf = std::make_shared(); + for (uint32_t i = 0; i < buf->arr.size(); i++) + buf->arr[i] = i; + auto arrayBuffer = ArrayBuffer(*rt, buf); + auto roundtrip = eval( + R"#( +(function (buf) { + return buf; +}) +)#"); + jsi::Value result = roundtrip.asObject(*rt).asFunction(*rt).call(*rt, arrayBuffer); + jsi::ArrayBuffer arrayBufferAgain = result.asObject(*rt).getArrayBuffer(*rt); + std::shared_ptr mutableBuffer = arrayBufferAgain.getMutableBuffer(*rt); + EXPECT_TRUE(mutableBuffer == buf); + } } TEST_F(HermesRuntimeTestMethodsTest, DetachedArrayBuffer) { From 39e8e0ecce83e007322dcca9cb61a323b777ddd3 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:47:01 +0200 Subject: [PATCH 08/13] fix: Handle deref --- lib/VM/JSArrayBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/VM/JSArrayBuffer.cpp b/lib/VM/JSArrayBuffer.cpp index 11982376357..8156b0e5e83 100644 --- a/lib/VM/JSArrayBuffer.cpp +++ b/lib/VM/JSArrayBuffer.cpp @@ -294,7 +294,7 @@ ExecutionStatus JSArrayBuffer::getExternalDataBlock( // Raw pointers below. NoAllocScope scope(runtime); NativeState *ns = vmcast( - JSObject::getNamedSlotValueUnsafe(*h, runtime, desc) + JSObject::getNamedSlotValueUnsafe(*self, runtime, desc) .getObject(runtime)); *context = ns->context(); return ExecutionStatus::RETURNED; From f1d20722fb674562cac3e33ead618fb66a70a838 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:49:31 +0200 Subject: [PATCH 09/13] fix: Fix namespace MutableBuffer --- API/hermes_sandbox/HermesSandboxRuntime.cpp | 2 +- API/jsi/jsi/decorator.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/API/hermes_sandbox/HermesSandboxRuntime.cpp b/API/hermes_sandbox/HermesSandboxRuntime.cpp index 6d171bc4e0f..a474b472b74 100644 --- a/API/hermes_sandbox/HermesSandboxRuntime.cpp +++ b/API/hermes_sandbox/HermesSandboxRuntime.cpp @@ -2051,7 +2051,7 @@ class HermesSandboxRuntimeImpl : public facebook::hermes::HermesSandboxRuntime, std::shared_ptr buffer) override { THROW_UNIMPLEMENTED(); } - std::shared_ptr getMutableBuffer( + std::shared_ptr getMutableBuffer( const ArrayBuffer& buffer) override { THROW_UNIMPLEMENTED(); } diff --git a/API/jsi/jsi/decorator.h b/API/jsi/jsi/decorator.h index 3b68776850b..0e97a32be55 100644 --- a/API/jsi/jsi/decorator.h +++ b/API/jsi/jsi/decorator.h @@ -361,7 +361,7 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { std::shared_ptr buffer) override { return plain_.createArrayBuffer(std::move(buffer)); }; - std::shared_ptr getMutableBuffer( + std::shared_ptr getMutableBuffer( const ArrayBuffer& buffer) override { return plain_.getMutableBuffer(buffer); } @@ -898,7 +898,7 @@ class WithRuntimeDecorator : public RuntimeDecorator { std::shared_ptr buffer) override { return RD::createArrayBuffer(std::move(buffer)); }; - std::shared_ptr getMutableBuffer( + std::shared_ptr getMutableBuffer( const ArrayBuffer& buffer) override { return RD::getMutableBuffer(buffer); } From 46fc3b64ff801b6511886a0b0db41f21cd5f8d16 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 17:54:54 +0200 Subject: [PATCH 10/13] Update JSArrayBuffer.cpp --- lib/VM/JSArrayBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/VM/JSArrayBuffer.cpp b/lib/VM/JSArrayBuffer.cpp index 8156b0e5e83..1b7b30aa085 100644 --- a/lib/VM/JSArrayBuffer.cpp +++ b/lib/VM/JSArrayBuffer.cpp @@ -279,7 +279,7 @@ ExecutionStatus JSArrayBuffer::getExternalDataBlock( Handle self, void **context ) { - assert(attached() && "Buffer must be attached"); + assert(self->attached() && "Buffer must be attached"); NamedPropertyDescriptor desc; bool exists = JSObject::getOwnNamedDescriptor( From e0a1dcbe2eee001a4131371bb039877356e17ce9 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 18:11:39 +0200 Subject: [PATCH 11/13] chore: Format --- API/hermes/TracingRuntime.cpp | 2 +- API/hermes/TracingRuntime.h | 2 +- API/hermes/hermes.cpp | 20 +++++++++++--------- API/hermes_abi/HermesABIRuntimeWrapper.cpp | 2 +- API/hermes_sandbox/HermesSandboxRuntime.cpp | 2 +- API/jsi/jsi/decorator.h | 4 ++-- API/jsi/jsi/jsi.h | 4 ++-- include/hermes/VM/JSArrayBuffer.h | 11 ++++++----- lib/VM/JSArrayBuffer.cpp | 6 +++--- unittests/API/APITest.cpp | 9 ++++++--- 10 files changed, 34 insertions(+), 28 deletions(-) diff --git a/API/hermes/TracingRuntime.cpp b/API/hermes/TracingRuntime.cpp index 9920f3be4b4..986a9698bf8 100644 --- a/API/hermes/TracingRuntime.cpp +++ b/API/hermes/TracingRuntime.cpp @@ -880,7 +880,7 @@ jsi::ArrayBuffer TracingRuntime::createArrayBuffer( } std::shared_ptr TracingRuntime::getMutableBuffer( - const jsi::ArrayBuffer& buffer) { + const jsi::ArrayBuffer &buffer) { throw std::logic_error("Cannot get external ArrayBuffers in trace mode."); } diff --git a/API/hermes/TracingRuntime.h b/API/hermes/TracingRuntime.h index f7f4d4c7fd5..8936c1b25c2 100644 --- a/API/hermes/TracingRuntime.h +++ b/API/hermes/TracingRuntime.h @@ -122,7 +122,7 @@ class TracingRuntime : public jsi::RuntimeDecorator { jsi::ArrayBuffer createArrayBuffer( std::shared_ptr buffer) override; std::shared_ptr getMutableBuffer( - const jsi::ArrayBuffer& buffer) override; + const jsi::ArrayBuffer &buffer) override; size_t size(const jsi::Array &arr) override; size_t size(const jsi::ArrayBuffer &buf) override; diff --git a/API/hermes/hermes.cpp b/API/hermes/hermes.cpp index 183dd50f4ab..498ebbf9dc9 100644 --- a/API/hermes/hermes.cpp +++ b/API/hermes/hermes.cpp @@ -726,7 +726,7 @@ class HermesRuntimeImpl final : public HermesRuntime, jsi::ArrayBuffer createArrayBuffer( std::shared_ptr buffer) override; std::shared_ptr getMutableBuffer( - const jsi::ArrayBuffer& buffer) override; + const jsi::ArrayBuffer &buffer) override; size_t size(const jsi::Array &) override; size_t size(const jsi::ArrayBuffer &) override; uint8_t *data(const jsi::ArrayBuffer &) override; @@ -2413,19 +2413,21 @@ uint8_t *HermesRuntimeImpl::data(const jsi::ArrayBuffer &arr) { } std::shared_ptr HermesRuntimeImpl::getMutableBuffer( - const jsi::ArrayBuffer& arr) { + const jsi::ArrayBuffer &arr) { auto buf = arrayBufferHandle(arr); if (LLVM_UNLIKELY(!buf->attached())) throw jsi::JSINativeException("ArrayBuffer is detached."); - void* context = nullptr; - auto res = vm::JSArrayBuffer::getExternalDataBlock( - runtime_, buf, &context); - if (context == nullptr) + void *context = nullptr; + auto res = vm::JSArrayBuffer::getExternalDataBlock(runtime_, buf, &context); + if (context == nullptr) { + // ArrayBuffer does not hold a MutableBuffer. return nullptr; - auto mutableBuffer = reinterpret_cast *>(context); - if (LLVM_UNLIKELY(mutableBuffer == nullptr)) - throw jsi::JSINativeException("ArrayBuffer's external data block is not a jsi::MutableBuffer!"); + } + checkStatus(res); + + auto mutableBuffer = + reinterpret_cast *>(context); return *mutableBuffer; } diff --git a/API/hermes_abi/HermesABIRuntimeWrapper.cpp b/API/hermes_abi/HermesABIRuntimeWrapper.cpp index 446cb7f7233..bf02c5d6ba5 100644 --- a/API/hermes_abi/HermesABIRuntimeWrapper.cpp +++ b/API/hermes_abi/HermesABIRuntimeWrapper.cpp @@ -945,7 +945,7 @@ class HermesABIRuntimeWrapper : public Runtime { abiRt_, new MutableBufferWrapper(std::move(buffer)))); } std::shared_ptr getMutableBuffer( - const ArrayBuffer& buffer) override; + const ArrayBuffer &buffer) override; size_t size(const Array &arr) override { return vtable_->get_array_length(abiRt_, toABIArray(arr)); } diff --git a/API/hermes_sandbox/HermesSandboxRuntime.cpp b/API/hermes_sandbox/HermesSandboxRuntime.cpp index a474b472b74..ca828152b78 100644 --- a/API/hermes_sandbox/HermesSandboxRuntime.cpp +++ b/API/hermes_sandbox/HermesSandboxRuntime.cpp @@ -2052,7 +2052,7 @@ class HermesSandboxRuntimeImpl : public facebook::hermes::HermesSandboxRuntime, THROW_UNIMPLEMENTED(); } std::shared_ptr getMutableBuffer( - const ArrayBuffer& buffer) override { + const ArrayBuffer &buffer) override { THROW_UNIMPLEMENTED(); } size_t size(const Array &arr) override { diff --git a/API/jsi/jsi/decorator.h b/API/jsi/jsi/decorator.h index 0e97a32be55..8e9b16eb5fe 100644 --- a/API/jsi/jsi/decorator.h +++ b/API/jsi/jsi/decorator.h @@ -362,7 +362,7 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { return plain_.createArrayBuffer(std::move(buffer)); }; std::shared_ptr getMutableBuffer( - const ArrayBuffer& buffer) override { + const ArrayBuffer &buffer) override { return plain_.getMutableBuffer(buffer); } size_t size(const Array& a) override { @@ -899,7 +899,7 @@ class WithRuntimeDecorator : public RuntimeDecorator { return RD::createArrayBuffer(std::move(buffer)); }; std::shared_ptr getMutableBuffer( - const ArrayBuffer& buffer) override { + const ArrayBuffer &buffer) override { return RD::getMutableBuffer(buffer); } size_t size(const Array& a) override { diff --git a/API/jsi/jsi/jsi.h b/API/jsi/jsi/jsi.h index 439d478f920..af63eedb6d6 100644 --- a/API/jsi/jsi/jsi.h +++ b/API/jsi/jsi/jsi.h @@ -500,11 +500,11 @@ class JSI_EXPORT Runtime : public ICast { virtual ArrayBuffer createArrayBuffer( std::shared_ptr buffer) = 0; virtual std::shared_ptr getMutableBuffer( - const ArrayBuffer& buffer) = 0; virtual size_t size(const Array&) = 0; virtual size_t size(const ArrayBuffer&) = 0; virtual uint8_t* data(const ArrayBuffer&) = 0; virtual Value getValueAtIndex(const Array&, size_t i) = 0; + const ArrayBuffer &buffer) = 0; virtual void setValueAtIndexImpl(const Array&, size_t i, const Value& value) = 0; @@ -1218,7 +1218,7 @@ class JSI_EXPORT ArrayBuffer : public Object { ArrayBuffer(ArrayBuffer&&) = default; ArrayBuffer& operator=(ArrayBuffer&&) = default; - ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) + ArrayBuffer(Runtime &runtime, std::shared_ptr buffer) : ArrayBuffer(runtime.createArrayBuffer(std::move(buffer))) {} /// \return the size of the ArrayBuffer storage. This is not affected by diff --git a/include/hermes/VM/JSArrayBuffer.h b/include/hermes/VM/JSArrayBuffer.h index 44d1a8df5ac..5bbe9e8b397 100644 --- a/include/hermes/VM/JSArrayBuffer.h +++ b/include/hermes/VM/JSArrayBuffer.h @@ -79,12 +79,13 @@ class JSArrayBuffer final : public JSObject { void *context, FinalizeNativeStatePtr finalizePtr); - /// Gets the external data block that this JSArrayBuffer holds if there is any. - /// If this JSArrayBuffer does not hold external data, this returns an error. + /// Gets the external data block that this JSArrayBuffer holds if there is + /// any. If this JSArrayBuffer does not hold external data, this returns an + /// error. static ExecutionStatus getExternalDataBlock( - Runtime &runtime, - Handle self, - void **context); + Runtime &runtime, + Handle self, + void **context); /// Retrieves a pointer to the held buffer. /// \return A pointer to the buffer owned by this object. This can be null diff --git a/lib/VM/JSArrayBuffer.cpp b/lib/VM/JSArrayBuffer.cpp index 1b7b30aa085..ff31e0bd938 100644 --- a/lib/VM/JSArrayBuffer.cpp +++ b/lib/VM/JSArrayBuffer.cpp @@ -277,15 +277,15 @@ ExecutionStatus JSArrayBuffer::setExternalDataBlock( ExecutionStatus JSArrayBuffer::getExternalDataBlock( Runtime &runtime, Handle self, - void **context -) { + void **context) { assert(self->attached() && "Buffer must be attached"); NamedPropertyDescriptor desc; bool exists = JSObject::getOwnNamedDescriptor( self, runtime, - Predefined::getSymbolID(Predefined::InternalPropertyArrayBufferExternalFinalizer), + Predefined::getSymbolID( + Predefined::InternalPropertyArrayBufferExternalFinalizer), desc); if (!exists) { // JSArrayBuffer does not hold an external data block diff --git a/unittests/API/APITest.cpp b/unittests/API/APITest.cpp index 97d27b01afc..f6793b3c1bd 100644 --- a/unittests/API/APITest.cpp +++ b/unittests/API/APITest.cpp @@ -211,9 +211,12 @@ TEST_F(HermesRuntimeTestMethodsTest, ExternalArrayBufferTest) { return buf; }) )#"); - jsi::Value result = roundtrip.asObject(*rt).asFunction(*rt).call(*rt, arrayBuffer); - jsi::ArrayBuffer arrayBufferAgain = result.asObject(*rt).getArrayBuffer(*rt); - std::shared_ptr mutableBuffer = arrayBufferAgain.getMutableBuffer(*rt); + Value result = + roundtrip.asObject(*rt).asFunction(*rt).call(*rt, arrayBuffer); + ArrayBuffer arrayBufferAgain = + result.asObject(*rt).getArrayBuffer(*rt); + std::shared_ptr mutableBuffer = + arrayBufferAgain.getMutableBuffer(*rt); EXPECT_TRUE(mutableBuffer == buf); } } From 745a535eb6d7dd80e89e4503fc72df76b10f24ee Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 18:13:59 +0200 Subject: [PATCH 12/13] Update jsi.h --- API/jsi/jsi/jsi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API/jsi/jsi/jsi.h b/API/jsi/jsi/jsi.h index af63eedb6d6..14e7e3e4306 100644 --- a/API/jsi/jsi/jsi.h +++ b/API/jsi/jsi/jsi.h @@ -500,11 +500,11 @@ class JSI_EXPORT Runtime : public ICast { virtual ArrayBuffer createArrayBuffer( std::shared_ptr buffer) = 0; virtual std::shared_ptr getMutableBuffer( + const ArrayBuffer &buffer) = 0; virtual size_t size(const Array&) = 0; virtual size_t size(const ArrayBuffer&) = 0; virtual uint8_t* data(const ArrayBuffer&) = 0; virtual Value getValueAtIndex(const Array&, size_t i) = 0; - const ArrayBuffer &buffer) = 0; virtual void setValueAtIndexImpl(const Array&, size_t i, const Value& value) = 0; From 661a78ed6a23e14d48b827e461a35000feaf40b8 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 14 Jul 2025 18:49:11 +0200 Subject: [PATCH 13/13] fix: Throw in `HermesABIRuntimeWrapper::getMutableBuffer()` --- API/hermes_abi/HermesABIRuntimeWrapper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/API/hermes_abi/HermesABIRuntimeWrapper.cpp b/API/hermes_abi/HermesABIRuntimeWrapper.cpp index bf02c5d6ba5..86cbfbc3ba8 100644 --- a/API/hermes_abi/HermesABIRuntimeWrapper.cpp +++ b/API/hermes_abi/HermesABIRuntimeWrapper.cpp @@ -945,7 +945,9 @@ class HermesABIRuntimeWrapper : public Runtime { abiRt_, new MutableBufferWrapper(std::move(buffer)))); } std::shared_ptr getMutableBuffer( - const ArrayBuffer &buffer) override; + const ArrayBuffer &buffer) override { + THROW_UNIMPLEMENTED(); + } size_t size(const Array &arr) override { return vtable_->get_array_length(abiRt_, toABIArray(arr)); }