diff --git a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp index e04e999aca9f..7d6e3c4e8dbe 100644 --- a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp +++ b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp @@ -146,6 +146,10 @@ class JSCRuntime : public jsi::Runtime { friend class JSCRuntime; }; + using IRuntime::getProperty; + using IRuntime::hasProperty; + using IRuntime::setPropertyValue; + PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override; PointerValue* cloneBigInt(const Runtime::PointerValue* pv) override; PointerValue* cloneString(const Runtime::PointerValue* pv) override; diff --git a/packages/react-native/ReactCommon/jsi/jsi/decorator.h b/packages/react-native/ReactCommon/jsi/jsi/decorator.h index 7e46db66be91..295baf32d256 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/decorator.h +++ b/packages/react-native/ReactCommon/jsi/jsi/decorator.h @@ -400,6 +400,10 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { plain_.setValueAtIndexImpl(a, i, value); } + size_t push(const Array& a, const Value* elements, size_t count) override { + return plain_.push(a, elements, count); + } + Function createFunctionFromHostFunction( const PropNameID& name, unsigned int paramCount, @@ -969,6 +973,10 @@ class WithRuntimeDecorator : public RuntimeDecorator { Around around{with_}; RD::setValueAtIndexImpl(a, i, value); } + size_t push(const Array& a, const Value* elements, size_t count) override { + Around around{with_}; + return RD::push(a, elements, count); + } Function createFunctionFromHostFunction( const PropNameID& name, diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h b/packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h index 2f70a59483c9..b8c0c1bb6aee 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h @@ -11,48 +11,48 @@ namespace facebook { namespace jsi { namespace detail { -inline Value toValue(Runtime&, std::nullptr_t) { +inline Value toValue(IRuntime&, std::nullptr_t) { return Value::null(); } -inline Value toValue(Runtime&, bool b) { +inline Value toValue(IRuntime&, bool b) { return Value(b); } -inline Value toValue(Runtime&, double d) { +inline Value toValue(IRuntime&, double d) { return Value(d); } -inline Value toValue(Runtime&, float f) { +inline Value toValue(IRuntime&, float f) { return Value(static_cast(f)); } -inline Value toValue(Runtime&, int i) { +inline Value toValue(IRuntime&, int i) { return Value(i); } -inline Value toValue(Runtime& runtime, const char* str) { +inline Value toValue(IRuntime& runtime, const char* str) { return String::createFromAscii(runtime, str); } -inline Value toValue(Runtime& runtime, const std::string& str) { +inline Value toValue(IRuntime& runtime, const std::string& str) { return String::createFromUtf8(runtime, str); } template -inline Value toValue(Runtime& runtime, const T& other) { +inline Value toValue(IRuntime& runtime, const T& other) { static_assert( std::is_base_of::value, "This type cannot be converted to Value"); return Value(runtime, other); } -inline Value toValue(Runtime& runtime, const Value& value) { +inline Value toValue(IRuntime& runtime, const Value& value) { return Value(runtime, value); } -inline Value&& toValue(Runtime&, Value&& value) { +inline Value&& toValue(IRuntime&, Value&& value) { return std::move(value); } -inline PropNameID toPropNameID(Runtime& runtime, const char* name) { +inline PropNameID toPropNameID(IRuntime& runtime, const char* name) { return PropNameID::forAscii(runtime, name); } -inline PropNameID toPropNameID(Runtime& runtime, const std::string& name) { +inline PropNameID toPropNameID(IRuntime& runtime, const std::string& name) { return PropNameID::forUtf8(runtime, name); } -inline PropNameID&& toPropNameID(Runtime&, PropNameID&& name) { +inline PropNameID&& toPropNameID(IRuntime&, PropNameID&& name) { return std::move(name); } @@ -85,107 +85,109 @@ inline const Runtime::PointerValue* Runtime::getPointerValue( } inline void Runtime::setRuntimeData( - const UUID& uuid, + const UUID& dataUUID, const std::shared_ptr& data) { auto* dataPtr = new std::shared_ptr(data); - setRuntimeDataImpl(uuid, dataPtr, [](const void* data) { + setRuntimeDataImpl(dataUUID, dataPtr, [](const void* data) { delete (const std::shared_ptr*)data; }); } -inline std::shared_ptr Runtime::getRuntimeData(const UUID& uuid) { - auto* data = (const std::shared_ptr*)getRuntimeDataImpl(uuid); +inline std::shared_ptr Runtime::getRuntimeData(const UUID& dataUUID) { + auto* data = (const std::shared_ptr*)getRuntimeDataImpl(dataUUID); return data ? *data : nullptr; } -Value Object::getPrototype(Runtime& runtime) const { +Value Object::getPrototype(IRuntime& runtime) const { return runtime.getPrototypeOf(*this); } -inline Value Object::getProperty(Runtime& runtime, const char* name) const { +inline Value Object::getProperty(IRuntime& runtime, const char* name) const { return getProperty(runtime, String::createFromAscii(runtime, name)); } -inline Value Object::getProperty(Runtime& runtime, const String& name) const { +inline Value Object::getProperty(IRuntime& runtime, const String& name) const { return runtime.getProperty(*this, name); } -inline Value Object::getProperty(Runtime& runtime, const PropNameID& name) +inline Value Object::getProperty(IRuntime& runtime, const PropNameID& name) const { return runtime.getProperty(*this, name); } -inline Value Object::getProperty(Runtime& runtime, const Value& name) const { +inline Value Object::getProperty(IRuntime& runtime, const Value& name) const { return runtime.getProperty(*this, name); } -inline bool Object::hasProperty(Runtime& runtime, const char* name) const { +inline bool Object::hasProperty(IRuntime& runtime, const char* name) const { return hasProperty(runtime, String::createFromAscii(runtime, name)); } -inline bool Object::hasProperty(Runtime& runtime, const String& name) const { +inline bool Object::hasProperty(IRuntime& runtime, const String& name) const { return runtime.hasProperty(*this, name); } -inline bool Object::hasProperty(Runtime& runtime, const PropNameID& name) +inline bool Object::hasProperty(IRuntime& runtime, const PropNameID& name) const { return runtime.hasProperty(*this, name); } -inline bool Object::hasProperty(Runtime& runtime, const Value& name) const { +inline bool Object::hasProperty(IRuntime& runtime, const Value& name) const { return runtime.hasProperty(*this, name); } template -void Object::setProperty(Runtime& runtime, const char* name, T&& value) const { +void Object::setProperty(IRuntime& runtime, const char* name, T&& value) const { setProperty( runtime, String::createFromAscii(runtime, name), std::forward(value)); } template -void Object::setProperty(Runtime& runtime, const String& name, T&& value) +void Object::setProperty(IRuntime& runtime, const String& name, T&& value) const { setPropertyValue( runtime, name, detail::toValue(runtime, std::forward(value))); } template -void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value) +void Object::setProperty(IRuntime& runtime, const PropNameID& name, T&& value) const { setPropertyValue( runtime, name, detail::toValue(runtime, std::forward(value))); } template -void Object::setProperty(Runtime& runtime, const Value& name, T&& value) const { +void Object::setProperty(IRuntime& runtime, const Value& name, T&& value) + const { setPropertyValue( runtime, name, detail::toValue(runtime, std::forward(value))); } -inline void Object::deleteProperty(Runtime& runtime, const char* name) const { +inline void Object::deleteProperty(IRuntime& runtime, const char* name) const { deleteProperty(runtime, String::createFromAscii(runtime, name)); } -inline void Object::deleteProperty(Runtime& runtime, const String& name) const { +inline void Object::deleteProperty(IRuntime& runtime, const String& name) + const { runtime.deleteProperty(*this, name); } -inline void Object::deleteProperty(Runtime& runtime, const PropNameID& name) +inline void Object::deleteProperty(IRuntime& runtime, const PropNameID& name) const { runtime.deleteProperty(*this, name); } -inline void Object::deleteProperty(Runtime& runtime, const Value& name) const { +inline void Object::deleteProperty(IRuntime& runtime, const Value& name) const { runtime.deleteProperty(*this, name); } -inline Array Object::getArray(Runtime& runtime) const& { +inline Array Object::getArray(IRuntime& runtime) const& { assert(runtime.isArray(*this)); (void)runtime; // when assert is disabled we need to mark this as used return Array(runtime.cloneObject(ptr_)); } -inline Array Object::getArray(Runtime& runtime) && { +inline Array Object::getArray(IRuntime& runtime) && { assert(runtime.isArray(*this)); (void)runtime; // when assert is disabled we need to mark this as used Runtime::PointerValue* value = ptr_; @@ -193,13 +195,13 @@ inline Array Object::getArray(Runtime& runtime) && { return Array(value); } -inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) const& { +inline ArrayBuffer Object::getArrayBuffer(IRuntime& runtime) const& { assert(runtime.isArrayBuffer(*this)); (void)runtime; // when assert is disabled we need to mark this as used return ArrayBuffer(runtime.cloneObject(ptr_)); } -inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && { +inline ArrayBuffer Object::getArrayBuffer(IRuntime& runtime) && { assert(runtime.isArrayBuffer(*this)); (void)runtime; // when assert is disabled we need to mark this as used Runtime::PointerValue* value = ptr_; @@ -207,12 +209,12 @@ inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && { return ArrayBuffer(value); } -inline Function Object::getFunction(Runtime& runtime) const& { +inline Function Object::getFunction(IRuntime& runtime) const& { assert(runtime.isFunction(*this)); return Function(runtime.cloneObject(ptr_)); } -inline Function Object::getFunction(Runtime& runtime) && { +inline Function Object::getFunction(IRuntime& runtime) && { assert(runtime.isFunction(*this)); (void)runtime; // when assert is disabled we need to mark this as used Runtime::PointerValue* value = ptr_; @@ -221,24 +223,24 @@ inline Function Object::getFunction(Runtime& runtime) && { } template -inline bool Object::isHostObject(Runtime& runtime) const { +inline bool Object::isHostObject(IRuntime& runtime) const { return runtime.isHostObject(*this) && std::dynamic_pointer_cast(runtime.getHostObject(*this)); } template <> -inline bool Object::isHostObject(Runtime& runtime) const { +inline bool Object::isHostObject(IRuntime& runtime) const { return runtime.isHostObject(*this); } template -inline std::shared_ptr Object::getHostObject(Runtime& runtime) const { +inline std::shared_ptr Object::getHostObject(IRuntime& runtime) const { assert(isHostObject(runtime)); return std::static_pointer_cast(runtime.getHostObject(*this)); } template -inline std::shared_ptr Object::asHostObject(Runtime& runtime) const { +inline std::shared_ptr Object::asHostObject(IRuntime& runtime) const { if (!isHostObject(runtime)) { detail::throwOrDie( "Object is not a HostObject of desired type"); @@ -248,59 +250,59 @@ inline std::shared_ptr Object::asHostObject(Runtime& runtime) const { template <> inline std::shared_ptr Object::getHostObject( - Runtime& runtime) const { + IRuntime& runtime) const { assert(runtime.isHostObject(*this)); return runtime.getHostObject(*this); } template -inline bool Object::hasNativeState(Runtime& runtime) const { +inline bool Object::hasNativeState(IRuntime& runtime) const { return runtime.hasNativeState(*this) && std::dynamic_pointer_cast(runtime.getNativeState(*this)); } template <> -inline bool Object::hasNativeState(Runtime& runtime) const { +inline bool Object::hasNativeState(IRuntime& runtime) const { return runtime.hasNativeState(*this); } template -inline std::shared_ptr Object::getNativeState(Runtime& runtime) const { +inline std::shared_ptr Object::getNativeState(IRuntime& runtime) const { assert(hasNativeState(runtime)); return std::static_pointer_cast(runtime.getNativeState(*this)); } inline void Object::setNativeState( - Runtime& runtime, + IRuntime& runtime, std::shared_ptr state) const { runtime.setNativeState(*this, state); } -inline void Object::setExternalMemoryPressure(Runtime& runtime, size_t amt) +inline void Object::setExternalMemoryPressure(IRuntime& runtime, size_t amt) const { runtime.setExternalMemoryPressure(*this, amt); } -inline Array Object::getPropertyNames(Runtime& runtime) const { +inline Array Object::getPropertyNames(IRuntime& runtime) const { return runtime.getPropertyNames(*this); } -inline Value WeakObject::lock(Runtime& runtime) const { +inline Value WeakObject::lock(IRuntime& runtime) const { return runtime.lockWeakObject(*this); } template -void Array::setValueAtIndex(Runtime& runtime, size_t i, T&& value) const { +void Array::setValueAtIndex(IRuntime& runtime, size_t i, T&& value) const { setValueAtIndexImpl( runtime, i, detail::toValue(runtime, std::forward(value))); } -inline Value Array::getValueAtIndex(Runtime& runtime, size_t i) const { +inline Value Array::getValueAtIndex(IRuntime& runtime, size_t i) const { return runtime.getValueAtIndex(*this, i); } inline Function Function::createFromHostFunction( - Runtime& runtime, + IRuntime& runtime, const jsi::PropNameID& name, unsigned int paramCount, jsi::HostFunctionType func) { @@ -308,18 +310,19 @@ inline Function Function::createFromHostFunction( name, paramCount, std::move(func)); } -inline Value Function::call(Runtime& runtime, const Value* args, size_t count) +inline Value Function::call(IRuntime& runtime, const Value* args, size_t count) const { return runtime.call(*this, Value::undefined(), args, count); } -inline Value Function::call(Runtime& runtime, std::initializer_list args) - const { +inline Value Function::call( + IRuntime& runtime, + std::initializer_list args) const { return call(runtime, args.begin(), args.size()); } template -inline Value Function::call(Runtime& runtime, Args&&... args) const { +inline Value Function::call(IRuntime& runtime, Args&&... args) const { // A more awesome version of this would be able to create raw values // which can be used directly without wrapping and unwrapping, but // this will do for now. @@ -327,7 +330,7 @@ inline Value Function::call(Runtime& runtime, Args&&... args) const { } inline Value Function::callWithThis( - Runtime& runtime, + IRuntime& runtime, const Object& jsThis, const Value* args, size_t count) const { @@ -335,7 +338,7 @@ inline Value Function::callWithThis( } inline Value Function::callWithThis( - Runtime& runtime, + IRuntime& runtime, const Object& jsThis, std::initializer_list args) const { return callWithThis(runtime, jsThis, args.begin(), args.size()); @@ -343,7 +346,7 @@ inline Value Function::callWithThis( template inline Value Function::callWithThis( - Runtime& runtime, + IRuntime& runtime, const Object& jsThis, Args&&... args) const { // A more awesome version of this would be able to create raw values @@ -354,14 +357,30 @@ inline Value Function::callWithThis( } template -inline Array Array::createWithElements(Runtime& runtime, Args&&... args) { +inline Array Array::createWithElements(IRuntime& runtime, Args&&... args) { return createWithElements( runtime, {detail::toValue(runtime, std::forward(args))...}); } +template +inline size_t Array::push(IRuntime& runtime, Args&&... args) { + return push(runtime, {detail::toValue(runtime, std::forward(args))...}); +} + +inline size_t Array::push( + IRuntime& runtime, + std::initializer_list elements) { + return push(runtime, elements.begin(), elements.size()); +} + +inline size_t +Array::push(IRuntime& runtime, const Value* elements, size_t count) { + return runtime.push(*this, elements, count); +} + template inline std::vector PropNameID::names( - Runtime& runtime, + IRuntime& runtime, Args&&... args) { return names({detail::toPropNameID(runtime, std::forward(args))...}); } @@ -378,26 +397,26 @@ inline std::vector PropNameID::names( } inline Value Function::callAsConstructor( - Runtime& runtime, + IRuntime& runtime, const Value* args, size_t count) const { return runtime.callAsConstructor(*this, args, count); } inline Value Function::callAsConstructor( - Runtime& runtime, + IRuntime& runtime, std::initializer_list args) const { return callAsConstructor(runtime, args.begin(), args.size()); } template -inline Value Function::callAsConstructor(Runtime& runtime, Args&&... args) +inline Value Function::callAsConstructor(IRuntime& runtime, Args&&... args) const { return callAsConstructor( runtime, {detail::toValue(runtime, std::forward(args))...}); } -String BigInt::toString(Runtime& runtime, int radix) const { +String BigInt::toString(IRuntime& runtime, int radix) const { return runtime.bigintToString(*this, radix); } diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp b/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp index c362edf55d28..1a4e169d37b7 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp @@ -78,7 +78,7 @@ class RemoveRuntimeDataHostObject : public jsi::HostObject { }; // This is used for generating short exception strings. -std::string kindToString(const Value& v, Runtime* rt = nullptr) { +std::string kindToString(const Value& v, IRuntime* rt = nullptr) { if (v.isUndefined()) { return "undefined"; } else if (v.isNull()) { @@ -104,7 +104,10 @@ std::string kindToString(const Value& v, Runtime* rt = nullptr) { // failure is in building a JSError, this will lead to infinite // recursion. This function is used in place of getPropertyAsFunction // when building JSError, to avoid that infinite recursion. -Value callGlobalFunction(Runtime& runtime, const char* name, const Value& arg) { +Value callGlobalFunction( + IRuntime& runtime, + const char* name, + const Value& arg) { Value v = runtime.global().getProperty(runtime, name); if (!v.isObject()) { throw JSINativeException( @@ -456,6 +459,16 @@ void Runtime::deleteProperty(const Object& object, const Value& name) { } } +size_t Runtime::push(const Array& arr, const Value* elements, size_t count) { + size_t newSize = size(arr); + for (size_t i = 0; i < count; i++) { + arr.setProperty(*this, Value((int)newSize), elements[i]); + ++newSize; + } + arr.setProperty(*this, "length", Value((int)newSize)); + return newSize; +} + void Runtime::setRuntimeDataImpl( const UUID& uuid, const void* data, @@ -548,7 +561,7 @@ Pointer& Pointer::operator=(Pointer&& other) noexcept { return *this; } -Object Object::getPropertyAsObject(Runtime& runtime, const char* name) const { +Object Object::getPropertyAsObject(IRuntime& runtime, const char* name) const { Value v = getProperty(runtime, name); if (!v.isObject()) { @@ -561,7 +574,7 @@ Object Object::getPropertyAsObject(Runtime& runtime, const char* name) const { return v.getObject(runtime); } -Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) +Function Object::getPropertyAsFunction(IRuntime& runtime, const char* name) const { Object obj = getPropertyAsObject(runtime, name); if (!obj.isFunction(runtime)) { @@ -574,7 +587,7 @@ Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) return std::move(obj).getFunction(runtime); } -Array Object::asArray(Runtime& runtime) const& { +Array Object::asArray(IRuntime& runtime) const& { if (!isArray(runtime)) { throw JSError( runtime, @@ -584,7 +597,7 @@ Array Object::asArray(Runtime& runtime) const& { return getArray(runtime); } -Array Object::asArray(Runtime& runtime) && { +Array Object::asArray(IRuntime& runtime) && { if (!isArray(runtime)) { throw JSError( runtime, @@ -594,7 +607,7 @@ Array Object::asArray(Runtime& runtime) && { return std::move(*this).getArray(runtime); } -Function Object::asFunction(Runtime& runtime) const& { +Function Object::asFunction(IRuntime& runtime) const& { if (!isFunction(runtime)) { throw JSError( runtime, @@ -604,7 +617,7 @@ Function Object::asFunction(Runtime& runtime) const& { return getFunction(runtime); } -Function Object::asFunction(Runtime& runtime) && { +Function Object::asFunction(IRuntime& runtime) && { if (!isFunction(runtime)) { throw JSError( runtime, @@ -626,7 +639,7 @@ Value::Value(Value&& other) noexcept : Value(other.kind_) { other.kind_ = UndefinedKind; } -Value::Value(Runtime& runtime, const Value& other) : Value(other.kind_) { +Value::Value(IRuntime& runtime, const Value& other) : Value(other.kind_) { // data_ is uninitialized, so use placement new to create non-POD // types in it. Any other kind of initialization will call a dtor // first, which is incorrect. @@ -651,7 +664,7 @@ Value::~Value() { } } -bool Value::strictEquals(Runtime& runtime, const Value& a, const Value& b) { +bool Value::strictEquals(IRuntime& runtime, const Value& a, const Value& b) { if (a.kind_ != b.kind_) { return false; } @@ -701,7 +714,7 @@ double Value::asNumber() const { return getNumber(); } -Object Value::asObject(Runtime& rt) const& { +Object Value::asObject(IRuntime& rt) const& { if (!isObject()) { throw JSError( rt, "Value is " + kindToString(*this, &rt) + ", expected an Object"); @@ -710,7 +723,7 @@ Object Value::asObject(Runtime& rt) const& { return getObject(rt); } -Object Value::asObject(Runtime& rt) && { +Object Value::asObject(IRuntime& rt) && { if (!isObject()) { throw JSError( rt, "Value is " + kindToString(*this, &rt) + ", expected an Object"); @@ -720,7 +733,7 @@ Object Value::asObject(Runtime& rt) && { return static_cast(ptr); } -Symbol Value::asSymbol(Runtime& rt) const& { +Symbol Value::asSymbol(IRuntime& rt) const& { if (!isSymbol()) { throw JSError( rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol"); @@ -729,7 +742,7 @@ Symbol Value::asSymbol(Runtime& rt) const& { return getSymbol(rt); } -Symbol Value::asSymbol(Runtime& rt) && { +Symbol Value::asSymbol(IRuntime& rt) && { if (!isSymbol()) { throw JSError( rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol"); @@ -738,7 +751,7 @@ Symbol Value::asSymbol(Runtime& rt) && { return std::move(*this).getSymbol(rt); } -BigInt Value::asBigInt(Runtime& rt) const& { +BigInt Value::asBigInt(IRuntime& rt) const& { if (!isBigInt()) { throw JSError( rt, "Value is " + kindToString(*this, &rt) + ", expected a BigInt"); @@ -747,7 +760,7 @@ BigInt Value::asBigInt(Runtime& rt) const& { return getBigInt(rt); } -BigInt Value::asBigInt(Runtime& rt) && { +BigInt Value::asBigInt(IRuntime& rt) && { if (!isBigInt()) { throw JSError( rt, "Value is " + kindToString(*this, &rt) + ", expected a BigInt"); @@ -756,7 +769,7 @@ BigInt Value::asBigInt(Runtime& rt) && { return std::move(*this).getBigInt(rt); } -String Value::asString(Runtime& rt) const& { +String Value::asString(IRuntime& rt) const& { if (!isString()) { throw JSError( rt, "Value is " + kindToString(*this, &rt) + ", expected a String"); @@ -765,7 +778,7 @@ String Value::asString(Runtime& rt) const& { return getString(rt); } -String Value::asString(Runtime& rt) && { +String Value::asString(IRuntime& rt) && { if (!isString()) { throw JSError( rt, "Value is " + kindToString(*this, &rt) + ", expected a String"); @@ -774,19 +787,19 @@ String Value::asString(Runtime& rt) && { return std::move(*this).getString(rt); } -String Value::toString(Runtime& runtime) const { +String Value::toString(IRuntime& runtime) const { Function toString = runtime.global().getPropertyAsFunction(runtime, "String"); return toString.call(runtime, *this).getString(runtime); } -uint64_t BigInt::asUint64(Runtime& runtime) const { +uint64_t BigInt::asUint64(IRuntime& runtime) const { if (!isUint64(runtime)) { throw JSError(runtime, "Lossy truncation in BigInt64::asUint64"); } return getUint64(runtime); } -int64_t BigInt::asInt64(Runtime& runtime) const { +int64_t BigInt::asInt64(IRuntime& runtime) const { if (!isInt64(runtime)) { throw JSError(runtime, "Lossy truncation in BigInt64::asInt64"); } @@ -794,7 +807,7 @@ int64_t BigInt::asInt64(Runtime& runtime) const { } Array Array::createWithElements( - Runtime& rt, + IRuntime& rt, std::initializer_list elements) { Array result(rt, elements.size()); size_t index = 0; @@ -814,11 +827,11 @@ Runtime::ScopeState* Runtime::pushScope() { void Runtime::popScope(ScopeState*) {} -JSError::JSError(Runtime& rt, Value&& value) { +JSError::JSError(IRuntime& rt, Value&& value) { setValue(rt, std::move(value)); } -JSError::JSError(Runtime& rt, std::string msg) : message_(std::move(msg)) { +JSError::JSError(IRuntime& rt, std::string msg) : message_(std::move(msg)) { try { setValue( rt, @@ -829,7 +842,7 @@ JSError::JSError(Runtime& rt, std::string msg) : message_(std::move(msg)) { } } -JSError::JSError(Runtime& rt, std::string msg, std::string stack) +JSError::JSError(IRuntime& rt, std::string msg, std::string stack) : message_(std::move(msg)), stack_(std::move(stack)) { try { Object e(rt); @@ -841,7 +854,7 @@ JSError::JSError(Runtime& rt, std::string msg, std::string stack) } } -JSError::JSError(std::string what, Runtime& rt, Value&& value) +JSError::JSError(std::string what, IRuntime& rt, Value&& value) : JSIException(std::move(what)) { setValue(rt, std::move(value)); } @@ -852,7 +865,7 @@ JSError::JSError(Value&& value, std::string message, std::string stack) message_(std::move(message)), stack_(std::move(stack)) {} -void JSError::setValue(Runtime& rt, Value&& value) { +void JSError::setValue(IRuntime& rt, Value&& value) { value_ = std::make_shared(std::move(value)); if ((message_.empty() || stack_.empty()) && value_->isObject()) { diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi.h b/packages/react-native/ReactCommon/jsi/jsi/jsi.h index cbef7e252d21..772b235d4daf 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi.h +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi.h @@ -182,6 +182,7 @@ class JSI_EXPORT PreparedJavaScript { virtual ~PreparedJavaScript() = 0; }; +class IRuntime; class Runtime; class Pointer; class PropNameID; @@ -221,7 +222,7 @@ class JSI_EXPORT HostObject { // object. (This may be as late as when the Runtime is shut down.) // You have no control over which thread it is called on. This will // be called from inside the GC, so it is unsafe to do any VM - // operations which require a Runtime&. Derived classes' dtors + // operations which require a IRuntime&. Derived classes' dtors // should also avoid doing anything expensive. Calling the dtor on // a jsi object is explicitly ok. If you want to do JS operations, // or any nontrivial work, you should add it to a work queue, and @@ -325,29 +326,20 @@ class JSI_EXPORT ISerialization : public ICast { #endif // JSI_UNSTABLE -/// Represents a JS runtime. Movable, but not copyable. Note that -/// this object may not be thread-aware, but cannot be used safely from -/// multiple threads at once. The application is responsible for -/// ensuring that it is used safely. This could mean using the -/// Runtime from a single thread, using a mutex, doing all work on a -/// serial queue, etc. This restriction applies to the methods of -/// this class, and any method in the API which take a Runtime& as an -/// argument. Destructors (all but ~Scope), operators, or other methods -/// which do not take Runtime& as an argument are safe to call from any -/// thread, but it is still forbidden to make write operations on a single -/// instance of any class from more than one thread. In addition, to -/// make shutdown safe, destruction of objects associated with the Runtime -/// must be destroyed before the Runtime is destroyed, or from the -/// destructor of a managed HostObject or HostFunction. Informally, this -/// means that the main source of unsafe behavior is to hold a jsi object -/// in a non-Runtime-managed object, and not clean it up before the Runtime -/// is shut down. If your lifecycle is such that avoiding this is hard, -/// you will probably need to do use your own locks. -class JSI_EXPORT Runtime : public ICast { +/// An interface that provides various functionalities of the JS runtime. +/// The APIs must not be called from multiple threads concurrently. It is the +/// user's responsibility ensure thread safety when using IRuntime. +/// Users should cast their runtime to IRuntime to access these APIs. However, +/// for backward compatibility, these APIs are also accessible via the Runtime +/// object directly. +class JSI_EXPORT IRuntime : public ICast { public: - virtual ~Runtime(); - - ICast* castInterface(const UUID& interfaceUUID) override; + static constexpr jsi::UUID uuid{ + 0xc2e8e22e, + 0xd7a6, + 0x11f0, + 0x8de9, + 0x0242ac120002}; /// Evaluates the given JavaScript \c buffer. \c sourceURL is used /// to annotate the stack trace if there is an exception. The @@ -441,45 +433,31 @@ class JSI_EXPORT Runtime : public ICast { /// \return an interface to extract metrics from this \c Runtime. The default /// implementation of this function returns an \c Instrumentation instance /// which returns no metrics. - virtual Instrumentation& instrumentation(); + virtual Instrumentation& instrumentation() = 0; - /// Stores the pointer \p data with the \p uuid in the runtime. This can be - /// used to store some custom data within the runtime. When the runtime is + /// Stores the pointer \p data with the \p dataUUID in the runtime. This can + /// be used to store some custom data within the runtime. When the runtime is /// destroyed, or if an entry at an existing key is overwritten, the runtime /// will release its ownership of the held object. - void setRuntimeData(const UUID& uuid, const std::shared_ptr& data); - + virtual void setRuntimeData( + const UUID& dataUUID, + const std::shared_ptr& data) = 0; /// Returns the data associated with the \p uuid in the runtime. If there's no /// data associated with the uuid, return a null pointer. - std::shared_ptr getRuntimeData(const UUID& uuid); - - protected: - friend class Pointer; - friend class PropNameID; - friend class Symbol; - friend class BigInt; - friend class String; - friend class Object; - friend class WeakObject; - friend class Array; - friend class ArrayBuffer; - friend class Function; - friend class Value; - friend class Scope; - friend class JSError; + virtual std::shared_ptr getRuntimeData(const UUID& dataUUID) = 0; /// Stores the pointer \p data with the \p uuid in the runtime. This can be /// used to store some custom data within the runtime. When the runtime is /// destroyed, or if an entry at an existing key is overwritten, the runtime /// will release its ownership by calling \p deleter. virtual void setRuntimeDataImpl( - const UUID& uuid, + const UUID& dataUUID, const void* data, - void (*deleter)(const void* data)); + void (*deleter)(const void* data)) = 0; /// Returns the data associated with the \p uuid in the runtime. If there's no /// data associated with the uuid, return a null pointer. - virtual const void* getRuntimeDataImpl(const UUID& uuid); + virtual const void* getRuntimeDataImpl(const UUID& dataUUID) = 0; // Potential optimization: avoid the cloneFoo() virtual dispatch, // and instead just fix the number of fields, and copy them, since @@ -493,11 +471,11 @@ class JSI_EXPORT Runtime : public ICast { virtual ~PointerValue() = default; }; - virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneBigInt(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0; - virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0; + virtual PointerValue* cloneSymbol(const IRuntime::PointerValue* pv) = 0; + virtual PointerValue* cloneBigInt(const IRuntime::PointerValue* pv) = 0; + virtual PointerValue* cloneString(const IRuntime::PointerValue* pv) = 0; + virtual PointerValue* cloneObject(const IRuntime::PointerValue* pv) = 0; + virtual PointerValue* clonePropNameID(const IRuntime::PointerValue* pv) = 0; virtual PropNameID createPropNameIDFromAscii( const char* str, @@ -507,7 +485,7 @@ class JSI_EXPORT Runtime : public ICast { size_t length) = 0; virtual PropNameID createPropNameIDFromUtf16( const char16_t* utf16, - size_t length); + size_t length) = 0; virtual PropNameID createPropNameIDFromString(const String& str) = 0; virtual PropNameID createPropNameIDFromSymbol(const Symbol& sym) = 0; virtual std::string utf8(const PropNameID&) = 0; @@ -524,12 +502,14 @@ class JSI_EXPORT Runtime : public ICast { virtual String createStringFromAscii(const char* str, size_t length) = 0; virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0; - virtual String createStringFromUtf16(const char16_t* utf16, size_t length); + virtual String createStringFromUtf16( + const char16_t* utf16, + size_t length) = 0; virtual std::string utf8(const String&) = 0; // \return a \c Value created from a utf8-encoded JSON string. The default // implementation creates a \c String and invokes JSON.parse. - virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length); + virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length) = 0; virtual Object createObject() = 0; virtual Object createObject(std::shared_ptr ho) = 0; @@ -537,7 +517,7 @@ class JSI_EXPORT Runtime : public ICast { virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0; // Creates a new Object with the custom prototype - virtual Object createObjectWithPrototype(const Value& prototype); + virtual Object createObjectWithPrototype(const Value& prototype) = 0; virtual bool hasNativeState(const jsi::Object&) = 0; virtual std::shared_ptr getNativeState(const jsi::Object&) = 0; @@ -545,15 +525,15 @@ class JSI_EXPORT Runtime : public ICast { const jsi::Object&, std::shared_ptr state) = 0; - virtual void setPrototypeOf(const Object& object, const Value& prototype); - virtual Value getPrototypeOf(const Object& object); + virtual void setPrototypeOf(const Object& object, const Value& prototype) = 0; + virtual Value getPrototypeOf(const Object& object) = 0; virtual Value getProperty(const Object&, const PropNameID& name) = 0; virtual Value getProperty(const Object&, const String& name) = 0; - virtual Value getProperty(const Object&, const Value& name); + virtual Value getProperty(const Object&, const Value& name) = 0; virtual bool hasProperty(const Object&, const PropNameID& name) = 0; virtual bool hasProperty(const Object&, const String& name) = 0; - virtual bool hasProperty(const Object&, const Value& name); + virtual bool hasProperty(const Object&, const Value& name) = 0; virtual void setPropertyValue( const Object&, const PropNameID& name, @@ -561,11 +541,11 @@ class JSI_EXPORT Runtime : public ICast { virtual void setPropertyValue(const Object&, const String& name, const Value& value) = 0; virtual void - setPropertyValue(const Object&, const Value& name, const Value& value); + setPropertyValue(const Object&, const Value& name, const Value& value) = 0; - virtual void deleteProperty(const Object&, const PropNameID& name); - virtual void deleteProperty(const Object&, const String& name); - virtual void deleteProperty(const Object&, const Value& name); + virtual void deleteProperty(const Object&, const PropNameID& name) = 0; + virtual void deleteProperty(const Object&, const String& name) = 0; + virtual void deleteProperty(const Object&, const Value& name) = 0; virtual bool isArray(const Object&) const = 0; virtual bool isArrayBuffer(const Object&) const = 0; @@ -586,6 +566,7 @@ class JSI_EXPORT Runtime : public ICast { virtual Value getValueAtIndex(const Array&, size_t i) = 0; virtual void setValueAtIndexImpl(const Array&, size_t i, const Value& value) = 0; + virtual size_t push(const Array&, const Value*, size_t) = 0; virtual Function createFunctionFromHostFunction( const PropNameID& name, @@ -601,8 +582,8 @@ class JSI_EXPORT Runtime : public ICast { // Private data for managing scopes. struct ScopeState; - virtual ScopeState* pushScope(); - virtual void popScope(ScopeState*); + virtual ScopeState* pushScope() = 0; + virtual void popScope(ScopeState*) = 0; virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0; virtual bool strictEquals(const BigInt& a, const BigInt& b) const = 0; @@ -616,8 +597,8 @@ class JSI_EXPORT Runtime : public ICast { const jsi::Object& obj, size_t amount) = 0; - virtual std::u16string utf16(const String& str); - virtual std::u16string utf16(const PropNameID& sym); + virtual std::u16string utf16(const String& str) = 0; + virtual std::u16string utf16(const PropNameID& sym) = 0; /// Invokes the provided callback \p cb with the String content in \p str. /// The callback must take in three arguments: bool ascii, const void* data, @@ -631,7 +612,7 @@ class JSI_EXPORT Runtime : public ICast { virtual void getStringData( const jsi::String& str, void* ctx, - void (*cb)(void* ctx, bool ascii, const void* data, size_t num)); + void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) = 0; /// Invokes the provided callback \p cb with the PropNameID content in \p sym. /// The callback must take in three arguments: bool ascii, const void* data, @@ -645,7 +626,107 @@ class JSI_EXPORT Runtime : public ICast { virtual void getPropNameIdData( const jsi::PropNameID& sym, void* ctx, - void (*cb)(void* ctx, bool ascii, const void* data, size_t num)); + void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) = 0; + + protected: + virtual ~IRuntime() = default; +}; + +/// Represents a JS runtime. Movable, but not copyable. Note that +/// this object may not be thread-aware, but cannot be used safely from +/// multiple threads at once. The application is responsible for +/// ensuring that it is used safely. This could mean using the +/// Runtime from a single thread, using a mutex, doing all work on a +/// serial queue, etc. This restriction applies to the methods of +/// this class, and any method in the API which take a Runtime& as an +/// argument. Destructors (all but ~Scope), operators, or other methods +/// which do not take Runtime& as an argument are safe to call from any +/// thread, but it is still forbidden to make write operations on a single +/// instance of any class from more than one thread. In addition, to +/// make shutdown safe, destruction of objects associated with the Runtime +/// must be destroyed before the Runtime is destroyed, or from the +/// destructor of a managed HostObject or HostFunction. Informally, this +/// means that the main source of unsafe behavior is to hold a jsi object +/// in a non-Runtime-managed object, and not clean it up before the Runtime +/// is shut down. If your lifecycle is such that avoiding this is hard, +/// you will probably need to do use your own locks. +class JSI_EXPORT Runtime : public IRuntime { + public: + virtual ~Runtime() override; + + using IRuntime::getProperty; + using IRuntime::hasProperty; + using IRuntime::setPropertyValue; + ICast* castInterface(const UUID& uuid) override; + + Instrumentation& instrumentation() override; + + /// Stores the pointer \p data with the \p uuid in the runtime. This can be + /// used to store some custom data within the runtime. When the runtime is + /// destroyed, or if an entry at an existing key is overwritten, the runtime + /// will release its ownership of the held object. + void setRuntimeData(const UUID& uuid, const std::shared_ptr& data) + override; + + /// Returns the data associated with the \p uuid in the runtime. If there's no + /// data associated with the uuid, return a null pointer. + std::shared_ptr getRuntimeData(const UUID& uuid) override; + + Value getProperty(const Object&, const Value& name) override; + bool hasProperty(const Object&, const Value& name) override; + void setPropertyValue(const Object&, const Value& name, const Value& value) + override; + void deleteProperty(const Object&, const PropNameID& name) override; + void deleteProperty(const Object&, const String& name) override; + void deleteProperty(const Object&, const Value& name) override; + + void setRuntimeDataImpl( + const UUID& uuid, + const void* data, + void (*deleter)(const void* data)) override; + const void* getRuntimeDataImpl(const UUID& uuid) override; + + PropNameID createPropNameIDFromUtf16(const char16_t* utf16, size_t length) + override; + String createStringFromUtf16(const char16_t* utf16, size_t length) override; + + Value createValueFromJsonUtf8(const uint8_t* json, size_t length) override; + + Object createObjectWithPrototype(const Value& prototype) override; + void setPrototypeOf(const Object& object, const Value& prototype) override; + Value getPrototypeOf(const Object& object) override; + + ScopeState* pushScope() override; + void popScope(ScopeState*) override; + + std::u16string utf16(const String& str) override; + std::u16string utf16(const PropNameID& sym) override; + + void getStringData( + const jsi::String& str, + void* ctx, + void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) override; + void getPropNameIdData( + const jsi::PropNameID& sym, + void* ctx, + void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) override; + + size_t push(const Array&, const Value*, size_t) override; + + protected: + friend class Pointer; + friend class PropNameID; + friend class Symbol; + friend class BigInt; + friend class String; + friend class Object; + friend class WeakObject; + friend class Array; + friend class ArrayBuffer; + friend class Function; + friend class Value; + friend class Scope; + friend class JSError; // These exist so derived classes can access the private parts of // Value, Symbol, String, and Object, which are all friends of Runtime. @@ -688,7 +769,7 @@ class JSI_EXPORT PropNameID : public Pointer { public: using Pointer::Pointer; - PropNameID(Runtime& runtime, const PropNameID& other) + PropNameID(IRuntime& runtime, const PropNameID& other) : Pointer(runtime.clonePropNameID(other.ptr_)) {} PropNameID(PropNameID&& other) = default; @@ -696,32 +777,33 @@ class JSI_EXPORT PropNameID : public Pointer { /// Create a JS property name id from ascii values. The data is /// copied. - static PropNameID forAscii(Runtime& runtime, const char* str, size_t length) { + static PropNameID + forAscii(IRuntime& runtime, const char* str, size_t length) { return runtime.createPropNameIDFromAscii(str, length); } /// Create a property name id from a nul-terminated C ascii name. The data is /// copied. - static PropNameID forAscii(Runtime& runtime, const char* str) { + static PropNameID forAscii(IRuntime& runtime, const char* str) { return forAscii(runtime, str, strlen(str)); } /// Create a PropNameID from a C++ string. The string is copied. - static PropNameID forAscii(Runtime& runtime, const std::string& str) { + static PropNameID forAscii(IRuntime& runtime, const std::string& str) { return forAscii(runtime, str.c_str(), str.size()); } /// Create a PropNameID from utf8 values. The data is copied. /// Results are undefined if \p utf8 contains invalid code points. static PropNameID - forUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { + forUtf8(IRuntime& runtime, const uint8_t* utf8, size_t length) { return runtime.createPropNameIDFromUtf8(utf8, length); } /// Create a PropNameID from utf8-encoded octets stored in a /// std::string. The string data is transformed and copied. /// Results are undefined if \p utf8 contains invalid code points. - static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) { + static PropNameID forUtf8(IRuntime& runtime, const std::string& utf8) { return runtime.createPropNameIDFromUtf8( reinterpret_cast(utf8.data()), utf8.size()); } @@ -730,42 +812,42 @@ class JSI_EXPORT PropNameID : public Pointer { /// input may contain unpaired surrogates, which will be interpreted as a code /// point of the same value. static PropNameID - forUtf16(Runtime& runtime, const char16_t* utf16, size_t length) { + forUtf16(IRuntime& runtime, const char16_t* utf16, size_t length) { return runtime.createPropNameIDFromUtf16(utf16, length); } /// Given a series of UTF-16 encoded code units stored inside std::u16string, /// create a PropNameId. The input may contain unpaired surrogates, which /// will be interpreted as a code point of the same value. - static PropNameID forUtf16(Runtime& runtime, const std::u16string& str) { + static PropNameID forUtf16(IRuntime& runtime, const std::u16string& str) { return runtime.createPropNameIDFromUtf16(str.data(), str.size()); } /// Create a PropNameID from a JS string. - static PropNameID forString(Runtime& runtime, const jsi::String& str) { + static PropNameID forString(IRuntime& runtime, const jsi::String& str) { return runtime.createPropNameIDFromString(str); } /// Create a PropNameID from a JS symbol. - static PropNameID forSymbol(Runtime& runtime, const jsi::Symbol& sym) { + static PropNameID forSymbol(IRuntime& runtime, const jsi::Symbol& sym) { return runtime.createPropNameIDFromSymbol(sym); } // Creates a vector of PropNameIDs constructed from given arguments. template - static std::vector names(Runtime& runtime, Args&&... args); + static std::vector names(IRuntime& runtime, Args&&... args); // Creates a vector of given PropNameIDs. template static std::vector names(PropNameID (&&propertyNames)[N]); /// Copies the data in a PropNameID as utf8 into a C++ string. - std::string utf8(Runtime& runtime) const { + std::string utf8(IRuntime& runtime) const { return runtime.utf8(*this); } /// Copies the data in a PropNameID as utf16 into a C++ string. - std::u16string utf16(Runtime& runtime) const { + std::u16string utf16(IRuntime& runtime) const { return runtime.utf16(*this); } @@ -778,7 +860,7 @@ class JSI_EXPORT PropNameID : public Pointer { /// callback must not access runtime functionality, as any operation on the /// runtime may invalidate the data pointers. template - void getPropNameIdData(Runtime& runtime, CB& cb) const { + void getPropNameIdData(IRuntime& runtime, CB& cb) const { runtime.getPropNameIdData( *this, &cb, [](void* ctx, bool ascii, const void* data, size_t num) { (*((CB*)ctx))(ascii, data, num); @@ -786,7 +868,7 @@ class JSI_EXPORT PropNameID : public Pointer { } static bool compare( - Runtime& runtime, + IRuntime& runtime, const jsi::PropNameID& a, const jsi::PropNameID& b) { return runtime.compare(a, b); @@ -809,13 +891,14 @@ class JSI_EXPORT Symbol : public Pointer { Symbol& operator=(Symbol&& other) = default; /// \return whether a and b refer to the same symbol. - static bool strictEquals(Runtime& runtime, const Symbol& a, const Symbol& b) { + static bool + strictEquals(IRuntime& runtime, const Symbol& a, const Symbol& b) { return runtime.strictEquals(a, b); } /// Converts a Symbol into a C++ string as JS .toString would. The output /// will look like \c Symbol(description) . - std::string toString(Runtime& runtime) const { + std::string toString(IRuntime& runtime) const { return runtime.symbolToString(*this); } @@ -832,51 +915,52 @@ class JSI_EXPORT BigInt : public Pointer { BigInt& operator=(BigInt&& other) = default; /// Create a BigInt representing the signed 64-bit \p value. - static BigInt fromInt64(Runtime& runtime, int64_t value) { + static BigInt fromInt64(IRuntime& runtime, int64_t value) { return runtime.createBigIntFromInt64(value); } /// Create a BigInt representing the unsigned 64-bit \p value. - static BigInt fromUint64(Runtime& runtime, uint64_t value) { + static BigInt fromUint64(IRuntime& runtime, uint64_t value) { return runtime.createBigIntFromUint64(value); } /// \return whether a === b. - static bool strictEquals(Runtime& runtime, const BigInt& a, const BigInt& b) { + static bool + strictEquals(IRuntime& runtime, const BigInt& a, const BigInt& b) { return runtime.strictEquals(a, b); } /// \returns This bigint truncated to a signed 64-bit integer. - int64_t getInt64(Runtime& runtime) const { + int64_t getInt64(IRuntime& runtime) const { return runtime.truncate(*this); } /// \returns Whether this bigint can be losslessly converted to int64_t. - bool isInt64(Runtime& runtime) const { + bool isInt64(IRuntime& runtime) const { return runtime.bigintIsInt64(*this); } /// \returns This bigint truncated to a signed 64-bit integer. Throws a /// JSIException if the truncation is lossy. - int64_t asInt64(Runtime& runtime) const; + int64_t asInt64(IRuntime& runtime) const; /// \returns This bigint truncated to an unsigned 64-bit integer. - uint64_t getUint64(Runtime& runtime) const { + uint64_t getUint64(IRuntime& runtime) const { return runtime.truncate(*this); } /// \returns Whether this bigint can be losslessly converted to uint64_t. - bool isUint64(Runtime& runtime) const { + bool isUint64(IRuntime& runtime) const { return runtime.bigintIsUint64(*this); } /// \returns This bigint truncated to an unsigned 64-bit integer. Throws a /// JSIException if the truncation is lossy. - uint64_t asUint64(Runtime& runtime) const; + uint64_t asUint64(IRuntime& runtime) const; /// \returns this BigInt converted to a String in base \p radix. Throws a /// JSIException if radix is not in the [2, 36] range. - inline String toString(Runtime& runtime, int radix = 10) const; + inline String toString(IRuntime& runtime, int radix = 10) const; friend class Runtime; friend class Value; @@ -893,19 +977,19 @@ class JSI_EXPORT String : public Pointer { /// Create a JS string from ascii values. The string data is /// copied. static String - createFromAscii(Runtime& runtime, const char* str, size_t length) { + createFromAscii(IRuntime& runtime, const char* str, size_t length) { return runtime.createStringFromAscii(str, length); } /// Create a JS string from a nul-terminated C ascii string. The /// string data is copied. - static String createFromAscii(Runtime& runtime, const char* str) { + static String createFromAscii(IRuntime& runtime, const char* str) { return createFromAscii(runtime, str, strlen(str)); } /// Create a JS string from a C++ string. The string data is /// copied. - static String createFromAscii(Runtime& runtime, const std::string& str) { + static String createFromAscii(IRuntime& runtime, const std::string& str) { return createFromAscii(runtime, str.c_str(), str.size()); } @@ -913,14 +997,14 @@ class JSI_EXPORT String : public Pointer { /// transformed and copied. Results are undefined if \p utf8 contains invalid /// code points. static String - createFromUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) { + createFromUtf8(IRuntime& runtime, const uint8_t* utf8, size_t length) { return runtime.createStringFromUtf8(utf8, length); } /// Create a JS string from utf8-encoded octets stored in a /// std::string. The string data is transformed and copied. Results are /// undefined if \p utf8 contains invalid code points. - static String createFromUtf8(Runtime& runtime, const std::string& utf8) { + static String createFromUtf8(IRuntime& runtime, const std::string& utf8) { return runtime.createStringFromUtf8( reinterpret_cast(utf8.data()), utf8.length()); } @@ -929,29 +1013,32 @@ class JSI_EXPORT String : public Pointer { /// may contain unpaired surrogates, which will be interpreted as a code point /// of the same value. static String - createFromUtf16(Runtime& runtime, const char16_t* utf16, size_t length) { + createFromUtf16(IRuntime& runtime, const char16_t* utf16, size_t length) { return runtime.createStringFromUtf16(utf16, length); } /// Given a series of UTF-16 encoded code units stored inside std::u16string, /// create a JS String. The input may contain unpaired surrogates, which will /// be interpreted as a code point of the same value. - static String createFromUtf16(Runtime& runtime, const std::u16string& utf16) { + static String createFromUtf16( + IRuntime& runtime, + const std::u16string& utf16) { return runtime.createStringFromUtf16(utf16.data(), utf16.length()); } /// \return whether a and b contain the same characters. - static bool strictEquals(Runtime& runtime, const String& a, const String& b) { + static bool + strictEquals(IRuntime& runtime, const String& a, const String& b) { return runtime.strictEquals(a, b); } /// Copies the data in a JS string as utf8 into a C++ string. - std::string utf8(Runtime& runtime) const { + std::string utf8(IRuntime& runtime) const { return runtime.utf8(*this); } /// Copies the data in a JS string as utf16 into a C++ string. - std::u16string utf16(Runtime& runtime) const { + std::u16string utf16(IRuntime& runtime) const { return runtime.utf16(*this); } @@ -964,7 +1051,7 @@ class JSI_EXPORT String : public Pointer { /// must not access runtime functionality, as any operation on the runtime may /// invalidate the data pointers. template - void getStringData(Runtime& runtime, CB& cb) const { + void getStringData(IRuntime& runtime, CB& cb) const { runtime.getStringData( *this, &cb, [](void* ctx, bool ascii, const void* data, size_t num) { (*((CB*)ctx))(ascii, data, num); @@ -987,94 +1074,95 @@ class JSI_EXPORT Object : public Pointer { Object& operator=(Object&& other) = default; /// Creates a new Object instance, like '{}' in JS. - explicit Object(Runtime& runtime) : Object(runtime.createObject()) {} + explicit Object(IRuntime& runtime) : Object(runtime.createObject()) {} static Object createFromHostObject( - Runtime& runtime, + IRuntime& runtime, std::shared_ptr ho) { return runtime.createObject(ho); } /// Creates a new Object with the custom prototype - static Object create(Runtime& runtime, const Value& prototype) { + static Object create(IRuntime& runtime, const Value& prototype) { return runtime.createObjectWithPrototype(prototype); } /// \return whether this and \c obj are the same JSObject or not. - static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) { + static bool + strictEquals(IRuntime& runtime, const Object& a, const Object& b) { return runtime.strictEquals(a, b); } /// \return the result of `this instanceOf ctor` in JS. - bool instanceOf(Runtime& rt, const Function& ctor) const { + bool instanceOf(IRuntime& rt, const Function& ctor) const { return rt.instanceOf(*this, ctor); } /// Sets \p prototype as the prototype of the object. The prototype must be /// either an Object or null. If the prototype was not set successfully, this /// method will throw. - void setPrototype(Runtime& runtime, const Value& prototype) const { + void setPrototype(IRuntime& runtime, const Value& prototype) const { return runtime.setPrototypeOf(*this, prototype); } /// \return the prototype of the object - inline Value getPrototype(Runtime& runtime) const; + inline Value getPrototype(IRuntime& runtime) const; /// \return the property of the object with the given ascii name. /// If the name isn't a property on the object, returns the /// undefined value. - Value getProperty(Runtime& runtime, const char* name) const; + Value getProperty(IRuntime& runtime, const char* name) const; /// \return the property of the object with the String name. /// If the name isn't a property on the object, returns the /// undefined value. - Value getProperty(Runtime& runtime, const String& name) const; + Value getProperty(IRuntime& runtime, const String& name) const; /// \return the property of the object with the given JS PropNameID /// name. If the name isn't a property on the object, returns the /// undefined value. - Value getProperty(Runtime& runtime, const PropNameID& name) const; + Value getProperty(IRuntime& runtime, const PropNameID& name) const; /// \return the Property of the object with the given JS Value name. If the /// name isn't a property on the object, returns the undefined value.This /// attempts to convert the JS Value to convert to a property key. If the /// conversion fails, this method may throw. - Value getProperty(Runtime& runtime, const Value& name) const; + Value getProperty(IRuntime& runtime, const Value& name) const; /// \return true if and only if the object has a property with the /// given ascii name. - bool hasProperty(Runtime& runtime, const char* name) const; + bool hasProperty(IRuntime& runtime, const char* name) const; /// \return true if and only if the object has a property with the /// given String name. - bool hasProperty(Runtime& runtime, const String& name) const; + bool hasProperty(IRuntime& runtime, const String& name) const; /// \return true if and only if the object has a property with the /// given PropNameID name. - bool hasProperty(Runtime& runtime, const PropNameID& name) const; + bool hasProperty(IRuntime& runtime, const PropNameID& name) const; /// \return true if and only if the object has a property with the given /// JS Value name. This attempts to convert the JS Value to convert to a /// property key. If the conversion fails, this method may throw. - bool hasProperty(Runtime& runtime, const Value& name) const; + bool hasProperty(IRuntime& runtime, const Value& name) const; /// Sets the property value from a Value or anything which can be /// used to make one: nullptr_t, bool, double, int, const char*, /// String, or Object. template - void setProperty(Runtime& runtime, const char* name, T&& value) const; + void setProperty(IRuntime& runtime, const char* name, T&& value) const; /// Sets the property value from a Value or anything which can be /// used to make one: nullptr_t, bool, double, int, const char*, /// String, or Object. template - void setProperty(Runtime& runtime, const String& name, T&& value) const; + void setProperty(IRuntime& runtime, const String& name, T&& value) const; /// Sets the property value from a Value or anything which can be /// used to make one: nullptr_t, bool, double, int, const char*, /// String, or Object. template - void setProperty(Runtime& runtime, const PropNameID& name, T&& value) const; + void setProperty(IRuntime& runtime, const PropNameID& name, T&& value) const; /// Sets the property value from a Value or anything which can be /// used to make one: nullptr_t, bool, double, int, const char*, @@ -1082,39 +1170,39 @@ class JSI_EXPORT Object : public Pointer { /// attempts to convert to a property key. If the conversion fails, this /// method may throw. template - void setProperty(Runtime& runtime, const Value& name, T&& value) const; + void setProperty(IRuntime& runtime, const Value& name, T&& value) const; /// Delete the property with the given ascii name. Throws if the deletion /// failed. - void deleteProperty(Runtime& runtime, const char* name) const; + void deleteProperty(IRuntime& runtime, const char* name) const; /// Delete the property with the given String name. Throws if the deletion /// failed. - void deleteProperty(Runtime& runtime, const String& name) const; + void deleteProperty(IRuntime& runtime, const String& name) const; /// Delete the property with the given PropNameID name. Throws if the deletion /// failed. - void deleteProperty(Runtime& runtime, const PropNameID& name) const; + void deleteProperty(IRuntime& runtime, const PropNameID& name) const; /// Delete the property with the given Value name. Throws if the deletion /// failed. - void deleteProperty(Runtime& runtime, const Value& name) const; + void deleteProperty(IRuntime& runtime, const Value& name) const; /// \return true iff JS \c Array.isArray() would return \c true. If /// so, then \c getArray() will succeed. - bool isArray(Runtime& runtime) const { + bool isArray(IRuntime& runtime) const { return runtime.isArray(*this); } /// \return true iff the Object is an ArrayBuffer. If so, then \c /// getArrayBuffer() will succeed. - bool isArrayBuffer(Runtime& runtime) const { + bool isArrayBuffer(IRuntime& runtime) const { return runtime.isArrayBuffer(*this); } /// \return true iff the Object is callable. If so, then \c /// getFunction will succeed. - bool isFunction(Runtime& runtime) const { + bool isFunction(IRuntime& runtime) const { return runtime.isFunction(*this); } @@ -1122,99 +1210,99 @@ class JSI_EXPORT Object : public Pointer { /// and the HostObject passed is of type \c T. If returns \c true then /// \c getHostObject will succeed. template - bool isHostObject(Runtime& runtime) const; + bool isHostObject(IRuntime& runtime) const; /// \return an Array instance which refers to the same underlying /// object. If \c isArray() would return false, this will assert. - Array getArray(Runtime& runtime) const&; + Array getArray(IRuntime& runtime) const&; /// \return an Array instance which refers to the same underlying /// object. If \c isArray() would return false, this will assert. - Array getArray(Runtime& runtime) &&; + Array getArray(IRuntime& runtime) &&; /// \return an Array instance which refers to the same underlying /// object. If \c isArray() would return false, this will throw /// JSIException. - Array asArray(Runtime& runtime) const&; + Array asArray(IRuntime& runtime) const&; /// \return an Array instance which refers to the same underlying /// object. If \c isArray() would return false, this will throw /// JSIException. - Array asArray(Runtime& runtime) &&; + Array asArray(IRuntime& runtime) &&; /// \return an ArrayBuffer instance which refers to the same underlying /// object. If \c isArrayBuffer() would return false, this will assert. - ArrayBuffer getArrayBuffer(Runtime& runtime) const&; + ArrayBuffer getArrayBuffer(IRuntime& runtime) const&; /// \return an ArrayBuffer instance which refers to the same underlying /// object. If \c isArrayBuffer() would return false, this will assert. - ArrayBuffer getArrayBuffer(Runtime& runtime) &&; + ArrayBuffer getArrayBuffer(IRuntime& runtime) &&; /// \return a Function instance which refers to the same underlying /// object. If \c isFunction() would return false, this will assert. - Function getFunction(Runtime& runtime) const&; + Function getFunction(IRuntime& runtime) const&; /// \return a Function instance which refers to the same underlying /// object. If \c isFunction() would return false, this will assert. - Function getFunction(Runtime& runtime) &&; + Function getFunction(IRuntime& runtime) &&; /// \return a Function instance which refers to the same underlying /// object. If \c isFunction() would return false, this will throw /// JSIException. - Function asFunction(Runtime& runtime) const&; + Function asFunction(IRuntime& runtime) const&; /// \return a Function instance which refers to the same underlying /// object. If \c isFunction() would return false, this will throw /// JSIException. - Function asFunction(Runtime& runtime) &&; + Function asFunction(IRuntime& runtime) &&; /// \return a shared_ptr which refers to the same underlying /// \c HostObject that was used to create this object. If \c isHostObject /// is false, this will assert. Note that this does a type check and will /// assert if the underlying HostObject isn't of type \c T template - std::shared_ptr getHostObject(Runtime& runtime) const; + std::shared_ptr getHostObject(IRuntime& runtime) const; /// \return a shared_ptr which refers to the same underlying /// \c HostObject that was used to create this object. If \c isHostObject /// is false, this will throw. template - std::shared_ptr asHostObject(Runtime& runtime) const; + std::shared_ptr asHostObject(IRuntime& runtime) const; /// \return whether this object has native state of type T previously set by /// \c setNativeState. template - bool hasNativeState(Runtime& runtime) const; + bool hasNativeState(IRuntime& runtime) const; /// \return a shared_ptr to the state previously set by \c setNativeState. /// If \c hasNativeState is false, this will assert. Note that this does a /// type check and will assert if the native state isn't of type \c T template - std::shared_ptr getNativeState(Runtime& runtime) const; + std::shared_ptr getNativeState(IRuntime& runtime) const; /// Set the internal native state property of this object, overwriting any old /// value. Creates a new shared_ptr to the object managed by \p state, which /// will live until the value at this property becomes unreachable. /// /// Throws a type error if this object is a proxy or host object. - void setNativeState(Runtime& runtime, std::shared_ptr state) + void setNativeState(IRuntime& runtime, std::shared_ptr state) const; /// \return same as \c getProperty(name).asObject(), except with /// a better exception message. - Object getPropertyAsObject(Runtime& runtime, const char* name) const; + Object getPropertyAsObject(IRuntime& runtime, const char* name) const; /// \return similar to \c /// getProperty(name).getObject().getFunction(), except it will /// throw JSIException instead of asserting if the property is /// not an object, or the object is not callable. - Function getPropertyAsFunction(Runtime& runtime, const char* name) const; + Function getPropertyAsFunction(IRuntime& runtime, const char* name) const; /// \return an Array consisting of all enumerable property names in /// the object and its prototype chain. All values in the return /// will be isString(). (This is probably not optimal, but it /// works. I only need it in one place.) - Array getPropertyNames(Runtime& runtime) const; + Array getPropertyNames(IRuntime& runtime) const; /// Inform the runtime that there is additional memory associated with a given /// JavaScript object that is not visible to the GC. This can be used if an @@ -1224,25 +1312,27 @@ class JSI_EXPORT Object : public Pointer { /// calls will overwrite any previously set value. Once the object is garbage /// collected, the associated external memory will be considered freed and may /// no longer factor into GC decisions. - void setExternalMemoryPressure(Runtime& runtime, size_t amt) const; + void setExternalMemoryPressure(IRuntime& runtime, size_t amt) const; protected: void setPropertyValue( - Runtime& runtime, + IRuntime& runtime, const String& name, const Value& value) const { return runtime.setPropertyValue(*this, name, value); } void setPropertyValue( - Runtime& runtime, + IRuntime& runtime, const PropNameID& name, const Value& value) const { return runtime.setPropertyValue(*this, name, value); } - void setPropertyValue(Runtime& runtime, const Value& name, const Value& value) - const { + void setPropertyValue( + IRuntime& runtime, + const Value& name, + const Value& value) const { return runtime.setPropertyValue(*this, name, value); } @@ -1261,14 +1351,14 @@ class JSI_EXPORT WeakObject : public Pointer { WeakObject& operator=(WeakObject&& other) = default; /// Create a WeakObject from an Object. - WeakObject(Runtime& runtime, const Object& o) + WeakObject(IRuntime& runtime, const Object& o) : WeakObject(runtime.createWeakObject(o)) {} /// \return a Value representing the underlying Object if it is still valid; /// otherwise returns \c undefined. Note that this method has nothing to do /// with threads or concurrency. The name is based on std::weak_ptr::lock() /// which serves a similar purpose. - Value lock(Runtime& runtime) const; + Value lock(IRuntime& runtime) const; friend class Runtime; }; @@ -1279,43 +1369,54 @@ class JSI_EXPORT Array : public Object { public: Array(Array&&) = default; /// Creates a new Array instance, with \c length undefined elements. - Array(Runtime& runtime, size_t length) : Array(runtime.createArray(length)) {} + Array(IRuntime& runtime, size_t length) + : Array(runtime.createArray(length)) {} Array& operator=(Array&&) = default; /// \return the size of the Array, according to its length property. /// (C++ naming convention) - size_t size(Runtime& runtime) const { + size_t size(IRuntime& runtime) const { return runtime.size(*this); } /// \return the size of the Array, according to its length property. /// (JS naming convention) - size_t length(Runtime& runtime) const { + size_t length(IRuntime& runtime) const { return size(runtime); } /// \return the property of the array at index \c i. If there is no /// such property, returns the undefined value. If \c i is out of /// range [ 0..\c length ] throws a JSIException. - Value getValueAtIndex(Runtime& runtime, size_t i) const; + Value getValueAtIndex(IRuntime& runtime, size_t i) const; /// Sets the property of the array at index \c i. The argument /// value behaves as with Object::setProperty(). If \c i is out of /// range [ 0..\c length ] throws a JSIException. template - void setValueAtIndex(Runtime& runtime, size_t i, T&& value) const; + void setValueAtIndex(IRuntime& runtime, size_t i, T&& value) const; + + /// Appends provides values to the end of the Array in the order they appear. + /// Returns the new length of the array. + template + size_t push(IRuntime& runtime, Args&&... args); + + /// Appends everything in \p elements to the end of the Array in the order + /// they appear. Returns the new length of the array. + size_t push(IRuntime& runtime, std::initializer_list elements); - /// There is no current API for changing the size of an array once - /// created. We'll probably need that eventually. + /// Appends \p count elements at \p elements to the end of the Array in the + /// order they appear. + size_t push(IRuntime& runtime, const Value* elements, size_t count); /// Creates a new Array instance from provided values template - static Array createWithElements(Runtime&, Args&&... args); + static Array createWithElements(IRuntime&, Args&&... args); /// Creates a new Array instance from initializer list. static Array createWithElements( - Runtime& runtime, + IRuntime& runtime, std::initializer_list elements); private: @@ -1323,7 +1424,7 @@ class JSI_EXPORT Array : public Object { friend class Value; friend class Runtime; - void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value) + void setValueAtIndexImpl(IRuntime& runtime, size_t i, const Value& value) const { return runtime.setValueAtIndexImpl(*this, i, value); } @@ -1337,21 +1438,21 @@ class JSI_EXPORT ArrayBuffer : public Object { ArrayBuffer(ArrayBuffer&&) = default; ArrayBuffer& operator=(ArrayBuffer&&) = default; - ArrayBuffer(Runtime& runtime, std::shared_ptr buffer) + ArrayBuffer(IRuntime& runtime, std::shared_ptr buffer) : ArrayBuffer(runtime.createArrayBuffer(std::move(buffer))) {} /// \return the size of the ArrayBuffer storage. This is not affected by /// overriding the byteLength property. /// (C++ naming convention) - size_t size(Runtime& runtime) const { + size_t size(IRuntime& runtime) const { return runtime.size(*this); } - size_t length(Runtime& runtime) const { + size_t length(IRuntime& runtime) const { return runtime.size(*this); } - uint8_t* data(Runtime& runtime) const { + uint8_t* data(IRuntime& runtime) const { return runtime.data(*this); } @@ -1381,7 +1482,7 @@ class JSI_EXPORT Function : public Object { /// any captured values, you are responsible for ensuring that their /// destructors are safe to call on any thread. static Function createFromHostFunction( - Runtime& runtime, + IRuntime& runtime, const jsi::PropNameID& name, unsigned int paramCount, jsi::HostFunctionType func); @@ -1392,7 +1493,7 @@ class JSI_EXPORT Function : public Object { /// \b Note: as with Function.prototype.apply, \c this may not always be /// \c undefined in the function itself. If the function is non-strict, /// \c this will be set to the global object. - Value call(Runtime& runtime, const Value* args, size_t count) const; + Value call(IRuntime& runtime, const Value* args, size_t count) const; /// Calls the function with a \c std::initializer_list of Value /// arguments. The \c this value of the JS function will not be set by the @@ -1401,7 +1502,7 @@ class JSI_EXPORT Function : public Object { /// \b Note: as with Function.prototype.apply, \c this may not always be /// \c undefined in the function itself. If the function is non-strict, /// \c this will be set to the global object. - Value call(Runtime& runtime, std::initializer_list args) const; + Value call(IRuntime& runtime, std::initializer_list args) const; /// Calls the function with any number of arguments similarly to /// Object::setProperty(). The \c this value of the JS function will not be @@ -1411,12 +1512,12 @@ class JSI_EXPORT Function : public Object { /// \c undefined in the function itself. If the function is non-strict, /// \c this will be set to the global object. template - Value call(Runtime& runtime, Args&&... args) const; + Value call(IRuntime& runtime, Args&&... args) const; /// Calls the function with \c count \c args and \c jsThis value passed /// as the \c this value. Value callWithThis( - Runtime& Runtime, + IRuntime& Runtime, const Object& jsThis, const Value* args, size_t count) const; @@ -1424,36 +1525,36 @@ class JSI_EXPORT Function : public Object { /// Calls the function with a \c std::initializer_list of Value /// arguments and \c jsThis passed as the \c this value. Value callWithThis( - Runtime& runtime, + IRuntime& runtime, const Object& jsThis, std::initializer_list args) const; /// Calls the function with any number of arguments similarly to /// Object::setProperty(), and with \c jsThis passed as the \c this value. template - Value callWithThis(Runtime& runtime, const Object& jsThis, Args&&... args) + Value callWithThis(IRuntime& runtime, const Object& jsThis, Args&&... args) const; /// Calls the function as a constructor with \c count \c args. Equivalent /// to calling `new Func` where `Func` is the js function reqresented by /// this. - Value callAsConstructor(Runtime& runtime, const Value* args, size_t count) + Value callAsConstructor(IRuntime& runtime, const Value* args, size_t count) const; /// Same as above `callAsConstructor`, except use an initializer_list to /// supply the arguments. - Value callAsConstructor(Runtime& runtime, std::initializer_list args) + Value callAsConstructor(IRuntime& runtime, std::initializer_list args) const; /// Same as above `callAsConstructor`, but automatically converts/wraps /// any argument with a jsi Value. template - Value callAsConstructor(Runtime& runtime, Args&&... args) const; + Value callAsConstructor(IRuntime& runtime, Args&&... args) const; /// Returns whether this was created with Function::createFromHostFunction. /// If true then you can use getHostFunction to get the underlying /// HostFunctionType. - bool isHostFunction(Runtime& runtime) const { + bool isHostFunction(IRuntime& runtime) const { return runtime.isHostFunction(*this); } @@ -1464,7 +1565,7 @@ class JSI_EXPORT Function : public Object { /// Note: The reference returned is borrowed from the JS object underlying /// \c this, and thus only lasts as long as the object underlying /// \c this does. - HostFunctionType& getHostFunction(Runtime& runtime) const { + HostFunctionType& getHostFunction(IRuntime& runtime) const { assert(isHostFunction(runtime)); return runtime.getHostFunction(*this); } @@ -1527,32 +1628,32 @@ class JSI_EXPORT Value { Value(Value&& other) noexcept; /// Copies a Symbol lvalue into a new JS value. - Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) { + Value(IRuntime& runtime, const Symbol& sym) : Value(SymbolKind) { new (&data_.pointer) Symbol(runtime.cloneSymbol(sym.ptr_)); } /// Copies a BigInt lvalue into a new JS value. - Value(Runtime& runtime, const BigInt& bigint) : Value(BigIntKind) { + Value(IRuntime& runtime, const BigInt& bigint) : Value(BigIntKind) { new (&data_.pointer) BigInt(runtime.cloneBigInt(bigint.ptr_)); } /// Copies a String lvalue into a new JS value. - Value(Runtime& runtime, const String& str) : Value(StringKind) { + Value(IRuntime& runtime, const String& str) : Value(StringKind) { new (&data_.pointer) String(runtime.cloneString(str.ptr_)); } /// Copies a Object lvalue into a new JS value. - Value(Runtime& runtime, const Object& obj) : Value(ObjectKind) { + Value(IRuntime& runtime, const Object& obj) : Value(ObjectKind) { new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_)); } /// Creates a JS value from another Value lvalue. - Value(Runtime& runtime, const Value& value); + Value(IRuntime& runtime, const Value& value); /// Value(rt, "foo") will treat foo as a bool. This makes doing /// that a compile error. template - Value(Runtime&, const char*) { + Value(IRuntime&, const char*) { static_assert( !std::is_same::value, "Value cannot be constructed directly from const char*"); @@ -1571,13 +1672,13 @@ class JSI_EXPORT Value { // \return a \c Value created from a utf8-encoded JSON string. static Value - createFromJsonUtf8(Runtime& runtime, const uint8_t* json, size_t length) { + createFromJsonUtf8(IRuntime& runtime, const uint8_t* json, size_t length) { return runtime.createValueFromJsonUtf8(json, length); } /// \return according to the Strict Equality Comparison algorithm, see: /// https://262.ecma-international.org/11.0/#sec-strict-equality-comparison - static bool strictEquals(Runtime& runtime, const Value& a, const Value& b); + static bool strictEquals(IRuntime& runtime, const Value& a, const Value& b); Value& operator=(Value&& other) noexcept { this->~Value(); @@ -1638,14 +1739,14 @@ class JSI_EXPORT Value { double asNumber() const; /// \return the Symbol value, or asserts if not a symbol. - Symbol getSymbol(Runtime& runtime) const& { + Symbol getSymbol(IRuntime& runtime) const& { assert(isSymbol()); return Symbol(runtime.cloneSymbol(data_.pointer.ptr_)); } /// \return the Symbol value, or asserts if not a symbol. /// Can be used on rvalue references to avoid cloning more symbols. - Symbol getSymbol(Runtime&) && { + Symbol getSymbol(IRuntime&) && { assert(isSymbol()); auto ptr = data_.pointer.ptr_; data_.pointer.ptr_ = nullptr; @@ -1654,18 +1755,18 @@ class JSI_EXPORT Value { /// \return the Symbol value, or throws JSIException if not a /// symbol - Symbol asSymbol(Runtime& runtime) const&; - Symbol asSymbol(Runtime& runtime) &&; + Symbol asSymbol(IRuntime& runtime) const&; + Symbol asSymbol(IRuntime& runtime) &&; /// \return the BigInt value, or asserts if not a bigint. - BigInt getBigInt(Runtime& runtime) const& { + BigInt getBigInt(IRuntime& runtime) const& { assert(isBigInt()); return BigInt(runtime.cloneBigInt(data_.pointer.ptr_)); } /// \return the BigInt value, or asserts if not a bigint. /// Can be used on rvalue references to avoid cloning more bigints. - BigInt getBigInt(Runtime&) && { + BigInt getBigInt(IRuntime&) && { assert(isBigInt()); auto ptr = data_.pointer.ptr_; data_.pointer.ptr_ = nullptr; @@ -1674,18 +1775,18 @@ class JSI_EXPORT Value { /// \return the BigInt value, or throws JSIException if not a /// bigint - BigInt asBigInt(Runtime& runtime) const&; - BigInt asBigInt(Runtime& runtime) &&; + BigInt asBigInt(IRuntime& runtime) const&; + BigInt asBigInt(IRuntime& runtime) &&; /// \return the String value, or asserts if not a string. - String getString(Runtime& runtime) const& { + String getString(IRuntime& runtime) const& { assert(isString()); return String(runtime.cloneString(data_.pointer.ptr_)); } /// \return the String value, or asserts if not a string. /// Can be used on rvalue references to avoid cloning more strings. - String getString(Runtime&) && { + String getString(IRuntime&) && { assert(isString()); auto ptr = data_.pointer.ptr_; data_.pointer.ptr_ = nullptr; @@ -1694,18 +1795,18 @@ class JSI_EXPORT Value { /// \return the String value, or throws JSIException if not a /// string. - String asString(Runtime& runtime) const&; - String asString(Runtime& runtime) &&; + String asString(IRuntime& runtime) const&; + String asString(IRuntime& runtime) &&; /// \return the Object value, or asserts if not an object. - Object getObject(Runtime& runtime) const& { + Object getObject(IRuntime& runtime) const& { assert(isObject()); return Object(runtime.cloneObject(data_.pointer.ptr_)); } /// \return the Object value, or asserts if not an object. /// Can be used on rvalue references to avoid cloning more objects. - Object getObject(Runtime&) && { + Object getObject(IRuntime&) && { assert(isObject()); auto ptr = data_.pointer.ptr_; data_.pointer.ptr_ = nullptr; @@ -1714,11 +1815,11 @@ class JSI_EXPORT Value { /// \return the Object value, or throws JSIException if not an /// object. - Object asObject(Runtime& runtime) const&; - Object asObject(Runtime& runtime) &&; + Object asObject(IRuntime& runtime) const&; + Object asObject(IRuntime& runtime) &&; // \return a String like JS .toString() would do. - String toString(Runtime& runtime) const; + String toString(IRuntime& runtime) const; private: friend class Runtime; @@ -1792,7 +1893,7 @@ class JSI_EXPORT Value { /// locking, provided that the lock (if any) is managed with RAII helpers. class JSI_EXPORT Scope { public: - explicit Scope(Runtime& rt) : rt_(rt), prv_(rt.pushScope()) {} + explicit Scope(IRuntime& rt) : rt_(rt), prv_(rt.pushScope()) {} ~Scope() { rt_.popScope(prv_); } @@ -1804,13 +1905,13 @@ class JSI_EXPORT Scope { Scope& operator=(Scope&&) = delete; template - static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) { + static auto callInNewScope(IRuntime& rt, F f) -> decltype(f()) { Scope s(rt); return f(); } private: - Runtime& rt_; + IRuntime& rt_; Runtime::ScopeState* prv_; }; @@ -1850,25 +1951,25 @@ class JSI_EXPORT JSINativeException : public JSIException { class JSI_EXPORT JSError : public JSIException { public: /// Creates a JSError referring to provided \c value - JSError(Runtime& r, Value&& value); + JSError(IRuntime& r, Value&& value); /// Creates a JSError referring to new \c Error instance capturing current /// JavaScript stack. The error message property is set to given \c message. - JSError(Runtime& rt, std::string message); + JSError(IRuntime& rt, std::string message); /// Creates a JSError referring to new \c Error instance capturing current /// JavaScript stack. The error message property is set to given \c message. - JSError(Runtime& rt, const char* message) + JSError(IRuntime& rt, const char* message) : JSError(rt, std::string(message)) {} /// Creates a JSError referring to a JavaScript Object having message and /// stack properties set to provided values. - JSError(Runtime& rt, std::string message, std::string stack); + JSError(IRuntime& rt, std::string message, std::string stack); /// Creates a JSError referring to provided value and what string /// set to provided message. This argument order is a bit weird, /// but necessary to avoid ambiguity with the above. - JSError(std::string what, Runtime& rt, Value&& value); + JSError(std::string what, IRuntime& rt, Value&& value); /// Creates a JSError referring to the provided value, message and stack. This /// constructor does not take a Runtime parameter, and therefore cannot result @@ -1896,7 +1997,7 @@ class JSI_EXPORT JSError : public JSIException { // This initializes the value_ member and does some other // validation, so it must be called by every branch through the // constructors. - void setValue(Runtime& rt, Value&& value); + void setValue(IRuntime& rt, Value&& value); // This needs to be on the heap, because throw requires the object // be copyable, and Value is not. diff --git a/packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp b/packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp index b11b2d8d7075..d55b5ef275fa 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp +++ b/packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp @@ -2011,6 +2011,116 @@ TEST_P(JSITest, DeleteProperty) { EXPECT_TRUE(hasRes); } +TEST_P(JSITest, ArrayPush) { + // This Runtime Decorator is used to test the default implementation of + // Runtime::push + class RD : public RuntimeDecorator { + public: + explicit RD(Runtime& rt) : RuntimeDecorator(rt) {} + + size_t push(const Array& arr, const Value* elements, size_t count) + override { + return Runtime::push(arr, elements, count); + } + }; + RD rd = RD(rt); + + // Push to an empty array + Array arr(rd, 0); + size_t newLength = arr.push(rd, 1, 2, 3); + EXPECT_EQ(newLength, 3); + EXPECT_EQ(arr.length(rd), 3); + + EXPECT_EQ(arr.getValueAtIndex(rd, 0).getNumber(), 1); + EXPECT_EQ(arr.getValueAtIndex(rd, 1).getNumber(), 2); + EXPECT_EQ(arr.getValueAtIndex(rd, 2).getNumber(), 3); + + // Push to an array already containing elements + arr = Array::createWithElements(rd, 1, true); + Object obj(rd); + newLength = arr.push(rd, "foobar", obj); + EXPECT_EQ(newLength, 4); + EXPECT_EQ(arr.length(rd), 4); + + EXPECT_EQ(arr.getValueAtIndex(rd, 0).getNumber(), 1); + EXPECT_TRUE(arr.getValueAtIndex(rd, 1).getBool()); + EXPECT_EQ(arr.getValueAtIndex(rd, 2).getString(rd).utf8(rd), "foobar"); + EXPECT_TRUE( + Object::strictEquals(rd, arr.getValueAtIndex(rd, 3).getObject(rd), obj)); + + // Push to a Proxy of a JS Array + arr = eval("new Proxy([1], {})").getObject(rd).getArray(rd); + EXPECT_EQ(arr.length(rd), 1); + + newLength = arr.push(rd, true, "foobar"); + EXPECT_EQ(newLength, 3); + EXPECT_EQ(arr.length(rd), 3); + + EXPECT_EQ(arr.getValueAtIndex(rd, 0).getNumber(), 1); + EXPECT_TRUE(arr.getValueAtIndex(rd, 1).getBool()); + EXPECT_EQ(arr.getValueAtIndex(rd, 2).getString(rd).utf8(rd), "foobar"); + + // Push to a Proxy of a JS Array, where getting the length returns a custom + // value + arr = eval( + "var arr = [1];" + "var handler = {" + " get(target, property) {" + " if (property == 'length') {" + " return target.length + 1;" + " }" + " return Reflect.get(target, property);" + " }" + "};" + "var proxy = new Proxy(arr, handler);" + "proxy;") + .getObject(rd) + .getArray(rd); + // The handler returns the underlying array's length plus 1 + EXPECT_EQ(arr.length(rd), 2); + + // The elements will be adding starting at element 2 + newLength = arr.push(rd, 3, 4); + EXPECT_EQ(newLength, 4); + + EXPECT_EQ(arr.length(rd), 5); + EXPECT_EQ(arr.getValueAtIndex(rd, 0).getNumber(), 1); + EXPECT_TRUE(arr.getValueAtIndex(rd, 1).isUndefined()); + EXPECT_EQ(arr.getValueAtIndex(rd, 2).getNumber(), 3); + EXPECT_EQ(arr.getValueAtIndex(rd, 3).getNumber(), 4); + + // Push to a Proxy of a JS Array, where setting the 'length' property is + // customized + arr = eval( + "var arr = [1];" + "var handler = {" + " set(target, property, value) {" + " if (property == 'length') {" + " return Reflect.set(target, property, value + 1);" + " }" + " return Reflect.set(target, property, value);" + " }" + "};" + "var proxy = new Proxy(arr, handler);" + "proxy;") + .getObject(rd) + .getArray(rd); + + EXPECT_EQ(arr.length(rd), 1); + EXPECT_EQ(arr.getValueAtIndex(rd, 0).getNumber(), 1); + + newLength = arr.push(rd, 2, 3); + EXPECT_EQ(newLength, 3); + + // When setting the 'length' property, the handler will actually set it to 3 + + // 1 + EXPECT_EQ(arr.length(rd), 4); + EXPECT_EQ(arr.getValueAtIndex(rd, 0).getNumber(), 1); + EXPECT_EQ(arr.getValueAtIndex(rd, 1).getNumber(), 2); + EXPECT_EQ(arr.getValueAtIndex(rd, 2).getNumber(), 3); + EXPECT_TRUE(arr.getValueAtIndex(rd, 3).isUndefined()); +} + INSTANTIATE_TEST_CASE_P( Runtimes, JSITest,