Skip to content

Commit

Permalink
feat: JSI support for BigInt, Initial TypedArrays & ArrayBuffer creat…
Browse files Browse the repository at this point in the history
…ion (#204)

* feat: JSI support for BigInt, Initial TypedArrays & ArrayBuffer creation

* chore(release): 8.5.1-dev.0

---------

Co-authored-by: Igor Randjelovic <rigor789@gmail.com>
Co-authored-by: Eduardo Speroni <edusperoni@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 26, 2024
1 parent 5088f5f commit 4cd869d
Show file tree
Hide file tree
Showing 10 changed files with 498 additions and 1 deletion.
1 change: 1 addition & 0 deletions NativeScript/JSIRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2022 Progress. All rights reserved.
//

#pragma once
#import <Foundation/Foundation.h>
#import "v8runtime/V8Runtime.h"
#include "runtime/Runtime.h"
Expand Down
8 changes: 8 additions & 0 deletions NativeScript/jsi/decorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
Array createArray(size_t length) override {
return plain_.createArray(length);
};
ArrayBuffer createArrayBuffer(
std::shared_ptr<MutableBuffer> buffer) override {
return plain_.createArrayBuffer(std::move(buffer));
};
size_t size(const Array& a) override {
return plain_.size(a);
};
Expand Down Expand Up @@ -671,6 +675,10 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
Around around{with_};
return RD::createArray(length);
};
ArrayBuffer createArrayBuffer(
std::shared_ptr<MutableBuffer> buffer) override {
return RD::createArrayBuffer(std::move(buffer));
};
size_t size(const Array& a) override {
Around around{with_};
return RD::size(a);
Expand Down
16 changes: 16 additions & 0 deletions NativeScript/jsi/jsi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,22 @@ inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && {
return ArrayBuffer(value);
}


inline TypedArray Object::getTypedArray(Runtime &runtime) const &{
assert(runtime.isTypedArray(*this));
(void) runtime; // when assert is disabled we need to mark this as used
return TypedArray(runtime.cloneObject(ptr_));
}

inline TypedArray Object::getTypedArray(Runtime &runtime) &&{
assert(runtime.isTypedArray(*this));
(void) runtime; // when assert is disabled we need to mark this as used
Runtime::PointerValue *value = ptr_;
ptr_ = nullptr;
return TypedArray(value);
}


inline Function Object::getFunction(Runtime& runtime) const& {
assert(runtime.isFunction(*this));
return Function(runtime.cloneObject(ptr_));
Expand Down
2 changes: 2 additions & 0 deletions NativeScript/jsi/jsi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ Value callGlobalFunction(Runtime& runtime, const char* name, const Value& arg) {

Buffer::~Buffer() = default;

MutableBuffer::~MutableBuffer() = default;

PreparedJavaScript::~PreparedJavaScript() = default;

Value HostObject::get(Runtime&, const PropNameID&) {
Expand Down
184 changes: 184 additions & 0 deletions NativeScript/jsi/jsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ class JSI_EXPORT Buffer {
virtual const uint8_t* data() const = 0;
};

/// Base class for buffers of data that need to be passed to the runtime. The
/// result of size() and data() must not change after construction. However, the
/// region pointed to by data() may be modified by the user or the runtime. The
/// user must ensure that access to the contents of the buffer is properly
/// synchronised.
class JSI_EXPORT MutableBuffer {
public:
virtual ~MutableBuffer();
virtual size_t size() const = 0;
virtual uint8_t* data() = 0;
};

class JSI_EXPORT StringBuffer : public Buffer {
public:
StringBuffer(std::string s) : s_(std::move(s)) {}
Expand Down Expand Up @@ -80,6 +92,7 @@ class Instrumentation;
class Scope;
class JSIException;
class JSError;
class TypedArray;

/// A function which has this type can be registered as a function
/// callable from JavaScript using Function::createFromHostFunction().
Expand Down Expand Up @@ -251,6 +264,8 @@ class JSI_EXPORT Runtime {
friend class Value;
friend class Scope;
friend class JSError;
friend class TypedArray;


// Potential optimization: avoid the cloneFoo() virtual dispatch,
// and instead just fix the number of fields, and copy them, since
Expand Down Expand Up @@ -307,18 +322,40 @@ class JSI_EXPORT Runtime {

virtual bool isArray(const Object&) const = 0;
virtual bool isArrayBuffer(const Object&) const = 0;
virtual bool isArrayBufferView(const Object&) const = 0;
virtual bool isTypedArray(const Object&) const = 0;
virtual bool isInt8Array(const Object&) const = 0;
virtual bool isUint8Array(const Object&) const = 0;
virtual bool isUint8ClampedArray(const Object&) const = 0;
virtual bool isInt16Array(const Object&) const = 0;
virtual bool isUint16Array(const Object&) const = 0;
virtual bool isInt32Array(const Object&) const = 0;
virtual bool isUint32Array(const Object&) const = 0;
virtual bool isFloat32Array(const Object&) const = 0;
virtual bool isBigInt64Array(const Object&) const = 0;
virtual bool isBigUint64Array(const Object&) const = 0;
virtual bool isFloat64Array(const Object&) const = 0;
virtual bool isFunction(const Object&) const = 0;
virtual bool isHostObject(const jsi::Object&) const = 0;
virtual bool isHostFunction(const jsi::Function&) const = 0;
virtual Array getPropertyNames(const Object&) = 0;

virtual WeakObject createWeakObject(const Object&) = 0;
virtual Value lockWeakObject(WeakObject&) = 0;

virtual uint64_t uint64Value(const BigInt&, bool *lossless) const = 0;
virtual int64_t int64Value(const BigInt&, bool *lossless) const = 0;


virtual Array createArray(size_t length) = 0;
virtual ArrayBuffer createArrayBuffer(
std::shared_ptr<MutableBuffer> buffer) = 0;
virtual size_t size(const Array&) = 0;
virtual size_t size(const ArrayBuffer&) = 0;
virtual size_t size(const TypedArray&) = 0;
virtual uint8_t* data(const ArrayBuffer&) = 0;
virtual uint8_t* data(const TypedArray&) = 0;
virtual size_t offset(const TypedArray&) = 0;
virtual Value getValueAtIndex(const Array&, size_t i) = 0;
virtual void setValueAtIndexImpl(Array&, size_t i, const Value& value) = 0;

Expand Down Expand Up @@ -493,6 +530,26 @@ class JSI_EXPORT BigInt : public Pointer {

BigInt(BigInt&& other) = default;
BigInt& operator=(BigInt&& other) = default;


/**
* Returns the value of this BigInt as an unsigned 64-bit integer.
* If `lossless` is provided, it will reflect whether the return value was
* truncated or wrapped around. In particular, it is set to `false` if this
* BigInt is negative.
*/
uint64_t Uint64Value(Runtime& runtime, bool* lossless = nullptr) const {
return runtime.uint64Value(*this, lossless);
}

/**
* Returns the value of this BigInt as a signed 64-bit integer.
* If `lossless` is provided, it will reflect whether this BigInt was
* truncated or not.
*/
int64_t Int64Value(Runtime& runtime, bool* lossless = nullptr) const {
return runtime.int64Value(*this, lossless);
}

friend class Runtime;
friend class Value;
Expand Down Expand Up @@ -641,6 +698,86 @@ class JSI_EXPORT Object : public Pointer {
bool isArrayBuffer(Runtime& runtime) const {
return runtime.isArrayBuffer(*this);
}


/// \return true iff the Object is an isArrayBufferView. If so, then \c
/// getArrayBuffer() will succeed.
bool isArrayBufferView(Runtime& runtime) const {
return runtime.isArrayBufferView(*this);
}

/// \return true iff the Object is an isTypedArray. If so, then \c
/// getArrayBuffer() will succeed.
bool isTypedArray(Runtime& runtime) const {
return runtime.isTypedArray(*this);
}


/// \return true iff the Object is an isInt8Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isInt8Array(Runtime& runtime) const {
return runtime.isInt8Array(*this);
}

/// \return true iff the Object is an isUint8Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isUint8Array(Runtime& runtime) const {
return runtime.isUint8Array(*this);
}

/// \return true iff the Object is an isUint8ClampedArray. If so, then \c
/// getArrayBuffer() will succeed.
bool isUint8ClampedArray(Runtime& runtime) const {
return runtime.isUint8ClampedArray(*this);
}

/// \return true iff the Object is an isInt16Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isInt16Array(Runtime& runtime) const {
return runtime.isInt16Array(*this);
}

/// \return true iff the Object is an isUint16Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isUint16Array(Runtime& runtime) const {
return runtime.isUint16Array(*this);
}

/// \return true iff the Object is an isInt32Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isInt32Array(Runtime& runtime) const {
return runtime.isInt32Array(*this);
}

/// \return true iff the Object is an isUint32Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isUint32Array(Runtime& runtime) const {
return runtime.isUint32Array(*this);
}

/// \return true iff the Object is an isFloat32Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isFloat32Array(Runtime& runtime) const {
return runtime.isFloat32Array(*this);
}

/// \return true iff the Object is an isBigInt64Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isBigInt64Array(Runtime& runtime) const {
return runtime.isBigInt64Array(*this);
}

/// \return true iff the Object is an isBigUint64Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isBigUint64Array(Runtime& runtime) const {
return runtime.isBigUint64Array(*this);
}

/// \return true iff the Object is an isFloat64Array. If so, then \c
/// getArrayBuffer() will succeed.
bool isFloat64Array(Runtime& runtime) const {
return runtime.isFloat64Array(*this);
}

/// \return true iff the Object is callable. If so, then \c
/// getFunction will succeed.
Expand Down Expand Up @@ -679,6 +816,16 @@ class JSI_EXPORT Object : public Pointer {
/// \return an ArrayBuffer instance which refers to the same underlying
/// object. If \c isArrayBuffer() would return false, this will assert.
ArrayBuffer getArrayBuffer(Runtime& runtime) &&;


/// \return an TypedArray instance which refers to the same underlying
/// object. If \c isTypedArray() would return false, this will assert.
TypedArray getTypedArray(Runtime& runtime) const&;

/// \return an TypedArray instance which refers to the same underlying
/// object. If \c isTypedArray() would return false, this will assert.
TypedArray getTypedArray(Runtime& runtime) &&;


/// \return a Function instance which refers to the same underlying
/// object. If \c isFunction() would return false, this will assert.
Expand Down Expand Up @@ -828,6 +975,8 @@ class JSI_EXPORT ArrayBuffer : public Object {
public:
ArrayBuffer(ArrayBuffer&&) = default;
ArrayBuffer& operator=(ArrayBuffer&&) = default;
ArrayBuffer(Runtime& runtime, std::shared_ptr<MutableBuffer> buffer)
: ArrayBuffer(runtime.createArrayBuffer(std::move(buffer))) {}

/// \return the size of the ArrayBuffer, according to its byteLength property.
/// (C++ naming convention)
Expand All @@ -850,6 +999,41 @@ class JSI_EXPORT ArrayBuffer : public Object {
ArrayBuffer(Runtime::PointerValue* value) : Object(value) {}
};


/// Represents a TypedArray
class JSI_EXPORT TypedArray : public Object {
public:
TypedArray(TypedArray &&) = default;

TypedArray &operator=(TypedArray &&) = default;

/// \return the size of the TypedArray ArrayBuffer, according to its byteLength property.
/// (C++ naming convention)
size_t size(Runtime &runtime) {
return runtime.size(*this);
}

size_t offset(Runtime &runtime) const {
return runtime.offset(*this);
}

size_t length(Runtime &runtime) const {
return runtime.size(*this);
}

uint8_t *data(Runtime &runtime) {
return runtime.data(*this);
}

private:
friend class Object;

friend class Value;

TypedArray(Runtime::PointerValue *value) : Object(value) {}
};


/// Represents a JS Object which is guaranteed to be Callable.
class JSI_EXPORT Function : public Object {
public:
Expand Down
20 changes: 20 additions & 0 deletions NativeScript/v8runtime/JSIV8ValueConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ jsi::Value JSIV8ValueConverter::ToJSIValue(
if (value->IsObject()) {
return V8Runtime::make<jsi::Object>(new V8PointerValue(isolate, value));
}

if (value->IsBigInt()) {
return V8Runtime::make<jsi::BigInt>(new V8PointerValue(isolate, value));
}

return jsi::Value::undefined();
}
Expand All @@ -65,6 +69,9 @@ v8::Local<v8::Value> JSIV8ValueConverter::ToV8Value(
} else if (value.isObject()) {
return scopedHandle.Escape(ToV8Object(
runtime, std::move(value.getObject(const_cast<V8Runtime &>(runtime)))));
}else if(value.isBigInt()){
return scopedHandle.Escape(ToV8BigInt(
runtime, std::move(value.getBigInt(const_cast<V8Runtime &>(runtime)))));
} else {
// What are you?
std::abort();
Expand Down Expand Up @@ -132,6 +139,19 @@ v8::Local<v8::Object> JSIV8ValueConverter::ToV8Object(
v8::Local<v8::Object>::Cast(v8PointerValue->Get(runtime.isolate_)));
}

// static
v8::Local<v8::BigInt> JSIV8ValueConverter::ToV8BigInt(
const V8Runtime &runtime,
const jsi::BigInt &bigInt) {
v8::EscapableHandleScope scopedHandle(runtime.isolate_);
const auto *v8PointerValue =
static_cast<const V8PointerValue *>(runtime.getPointerValue(bigInt));
assert(v8PointerValue->Get(runtime.isolate_)->IsBigInt());
return scopedHandle.Escape(
v8::Local<v8::BigInt>::Cast(v8PointerValue->Get(runtime.isolate_)));
}


// static
v8::Local<v8::Array> JSIV8ValueConverter::ToV8Array(
const V8Runtime &runtime,
Expand Down
4 changes: 4 additions & 0 deletions NativeScript/v8runtime/JSIV8ValueConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ class JSIV8ValueConverter {
static v8::Local<v8::Object> ToV8Object(
const V8Runtime &runtime,
const facebook::jsi::Object &object);

static v8::Local<v8::BigInt> ToV8BigInt(
const V8Runtime &runtime,
const facebook::jsi::BigInt &bigInt);

static v8::Local<v8::Array> ToV8Array(
const V8Runtime &runtime,
Expand Down
Loading

0 comments on commit 4cd869d

Please sign in to comment.