Skip to content
5 changes: 5 additions & 0 deletions API/hermes/TracingRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,11 @@ jsi::ArrayBuffer TracingRuntime::createArrayBuffer(
throw std::logic_error("Cannot create external ArrayBuffers in trace mode.");
}

std::shared_ptr<jsi::MutableBuffer> 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.
Expand Down
2 changes: 2 additions & 0 deletions API/hermes/TracingRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ class TracingRuntime : public jsi::RuntimeDecorator<jsi::Runtime> {
jsi::Array createArray(size_t length) override;
jsi::ArrayBuffer createArrayBuffer(
std::shared_ptr<jsi::MutableBuffer> buffer) override;
std::shared_ptr<jsi::MutableBuffer> getMutableBuffer(
const jsi::ArrayBuffer &buffer) override;

size_t size(const jsi::Array &arr) override;
size_t size(const jsi::ArrayBuffer &buf) override;
Expand Down
21 changes: 21 additions & 0 deletions API/hermes/hermes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,8 @@ class HermesRuntimeImpl final : public HermesRuntime,
jsi::Array createArray(size_t length) override;
jsi::ArrayBuffer createArrayBuffer(
std::shared_ptr<jsi::MutableBuffer> buffer) override;
std::shared_ptr<jsi::MutableBuffer> 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;
Expand Down Expand Up @@ -2410,6 +2412,25 @@ uint8_t *HermesRuntimeImpl::data(const jsi::ArrayBuffer &arr) {
return ab->getDataBlock(runtime_);
}

std::shared_ptr<jsi::MutableBuffer> 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) {
// ArrayBuffer does not hold a MutableBuffer.
return nullptr;
}
checkStatus(res);

auto mutableBuffer =
reinterpret_cast<std::shared_ptr<jsi::MutableBuffer> *>(context);
return *mutableBuffer;
}

jsi::Value HermesRuntimeImpl::getValueAtIndex(const jsi::Array &arr, size_t i) {
vm::GCScope gcScope(runtime_);
if (LLVM_UNLIKELY(i >= size(arr))) {
Expand Down
4 changes: 4 additions & 0 deletions API/hermes_abi/HermesABIRuntimeWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,10 @@ class HermesABIRuntimeWrapper : public Runtime {
return intoJSIArrayBuffer(vtable_->create_arraybuffer_from_external_data(
abiRt_, new MutableBufferWrapper(std::move(buffer))));
}
std::shared_ptr<MutableBuffer> getMutableBuffer(
const ArrayBuffer &buffer) override {
THROW_UNIMPLEMENTED();
}
size_t size(const Array &arr) override {
return vtable_->get_array_length(abiRt_, toABIArray(arr));
}
Expand Down
4 changes: 4 additions & 0 deletions API/hermes_sandbox/HermesSandboxRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2051,6 +2051,10 @@ class HermesSandboxRuntimeImpl : public facebook::hermes::HermesSandboxRuntime,
std::shared_ptr<MutableBuffer> buffer) override {
THROW_UNIMPLEMENTED();
}
std::shared_ptr<MutableBuffer> getMutableBuffer(
const ArrayBuffer &buffer) override {
THROW_UNIMPLEMENTED();
}
size_t size(const Array &arr) override {
return vt_.get_array_length(this, srt_, toSandboxArray(arr).pointer);
}
Expand Down
8 changes: 8 additions & 0 deletions API/jsi/jsi/decorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
std::shared_ptr<MutableBuffer> buffer) override {
return plain_.createArrayBuffer(std::move(buffer));
};
std::shared_ptr<MutableBuffer> getMutableBuffer(
const ArrayBuffer &buffer) override {
return plain_.getMutableBuffer(buffer);
}
size_t size(const Array& a) override {
return plain_.size(a);
};
Expand Down Expand Up @@ -894,6 +898,10 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
std::shared_ptr<MutableBuffer> buffer) override {
return RD::createArrayBuffer(std::move(buffer));
};
std::shared_ptr<MutableBuffer> getMutableBuffer(
const ArrayBuffer &buffer) override {
return RD::getMutableBuffer(buffer);
}
size_t size(const Array& a) override {
Around around{with_};
return RD::size(a);
Expand Down
11 changes: 10 additions & 1 deletion API/jsi/jsi/jsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ class JSI_EXPORT Runtime : public ICast {
virtual Array createArray(size_t length) = 0;
virtual ArrayBuffer createArrayBuffer(
std::shared_ptr<MutableBuffer> buffer) = 0;
virtual std::shared_ptr<MutableBuffer> 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;
Expand Down Expand Up @@ -1216,7 +1218,7 @@ class JSI_EXPORT ArrayBuffer : public Object {
ArrayBuffer(ArrayBuffer&&) = default;
ArrayBuffer& operator=(ArrayBuffer&&) = default;

ArrayBuffer(Runtime& runtime, std::shared_ptr<MutableBuffer> buffer)
ArrayBuffer(Runtime &runtime, std::shared_ptr<MutableBuffer> buffer)
: ArrayBuffer(runtime.createArrayBuffer(std::move(buffer))) {}

/// \return the size of the ArrayBuffer storage. This is not affected by
Expand All @@ -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<MutableBuffer> getMutableBuffer(Runtime& runtime) const {
return runtime.getMutableBuffer(*this);
}

private:
friend class Object;
friend class Value;
Expand Down
8 changes: 8 additions & 0 deletions include/hermes/VM/JSArrayBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ 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<JSArrayBuffer> 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.
Expand Down
26 changes: 26 additions & 0 deletions lib/VM/JSArrayBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,5 +274,31 @@ ExecutionStatus JSArrayBuffer::setExternalDataBlock(
return ExecutionStatus::RETURNED;
}

ExecutionStatus JSArrayBuffer::getExternalDataBlock(
Runtime &runtime,
Handle<JSArrayBuffer> self,
void **context) {
assert(self->attached() && "Buffer must be attached");

NamedPropertyDescriptor desc;
bool exists = JSObject::getOwnNamedDescriptor(
self,
runtime,
Predefined::getSymbolID(
Predefined::InternalPropertyArrayBufferExternalFinalizer),
desc);
if (!exists) {
// JSArrayBuffer does not hold an external data block
return ExecutionStatus::EXCEPTION;
}
// Raw pointers below.
NoAllocScope scope(runtime);
NativeState *ns = vmcast<NativeState>(
JSObject::getNamedSlotValueUnsafe(*self, runtime, desc)
.getObject(runtime));
*context = ns->context();
return ExecutionStatus::RETURNED;
}

} // namespace vm
} // namespace hermes
20 changes: 20 additions & 0 deletions unittests/API/APITest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,26 @@ TEST_F(HermesRuntimeTestMethodsTest, ExternalArrayBufferTest) {
rt->instrumentation().collectGarbage("");
EXPECT_TRUE(weakBuf.expired());
}

{
auto buf = std::make_shared<FixedBuffer>();
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;
})
)#");
Value result =
roundtrip.asObject(*rt).asFunction(*rt).call(*rt, arrayBuffer);
ArrayBuffer arrayBufferAgain =
result.asObject(*rt).getArrayBuffer(*rt);
std::shared_ptr<MutableBuffer> mutableBuffer =
arrayBufferAgain.getMutableBuffer(*rt);
EXPECT_TRUE(mutableBuffer == buf);
}
}

TEST_F(HermesRuntimeTestMethodsTest, DetachedArrayBuffer) {
Expand Down
Loading