newsmemory-ios-sdk/Frameworks/React-jsi.xcframework/ios-arm64/Headers/jsi/jsi.h

1318 lines
46 KiB
C
Raw Normal View History

2024-03-06 10:57:49 +00:00
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <cassert>
#include <cstring>
#include <exception>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#ifndef JSI_EXPORT
#ifdef _MSC_VER
#ifdef CREATE_SHARED_LIBRARY
#define JSI_EXPORT __declspec(dllexport)
#else
#define JSI_EXPORT
#endif // CREATE_SHARED_LIBRARY
#else // _MSC_VER
#define JSI_EXPORT __attribute__((visibility("default")))
#endif // _MSC_VER
#endif // !defined(JSI_EXPORT)
class FBJSRuntime;
namespace facebook {
namespace jsi {
class JSI_EXPORT Buffer {
public:
virtual ~Buffer();
virtual size_t size() const = 0;
virtual const uint8_t* data() const = 0;
};
class JSI_EXPORT StringBuffer : public Buffer {
public:
StringBuffer(std::string s) : s_(std::move(s)) {}
size_t size() const override {
return s_.size();
}
const uint8_t* data() const override {
return reinterpret_cast<const uint8_t*>(s_.data());
}
private:
std::string s_;
};
/// PreparedJavaScript is a base class representing JavaScript which is in a
/// form optimized for execution, in a runtime-specific way. Construct one via
/// jsi::Runtime::prepareJavaScript().
/// ** This is an experimental API that is subject to change. **
class JSI_EXPORT PreparedJavaScript {
protected:
PreparedJavaScript() = default;
public:
virtual ~PreparedJavaScript() = 0;
};
class Runtime;
class Pointer;
class PropNameID;
class Symbol;
class String;
class Object;
class WeakObject;
class Array;
class ArrayBuffer;
class Function;
class Value;
class Instrumentation;
class Scope;
class JSIException;
class JSError;
/// A function which has this type can be registered as a function
/// callable from JavaScript using Function::createFromHostFunction().
/// When the function is called, args will point to the arguments, and
/// count will indicate how many arguments are passed. The function
/// can return a Value to the caller, or throw an exception. If a C++
/// exception is thrown, a JS Error will be created and thrown into
/// JS; if the C++ exception extends std::exception, the Error's
/// message will be whatever what() returns. Note that it is undefined whether
/// HostFunctions may or may not be called in strict mode; that is `thisVal`
/// can be any value - it will not necessarily be coerced to an object or
/// or set to the global object.
using HostFunctionType = std::function<
Value(Runtime& rt, const Value& thisVal, const Value* args, size_t count)>;
/// An object which implements this interface can be registered as an
/// Object with the JS runtime.
class JSI_EXPORT HostObject {
public:
// The C++ object's dtor will be called when the GC finalizes this
// 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
// 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
// manage it externally.
virtual ~HostObject();
// When JS wants a property with a given name from the HostObject,
// it will call this method. If it throws an exception, the call
// will throw a JS \c Error object. By default this returns undefined.
// \return the value for the property.
virtual Value get(Runtime&, const PropNameID& name);
// When JS wants to set a property with a given name on the HostObject,
// it will call this method. If it throws an exception, the call will
// throw a JS \c Error object. By default this throws a type error exception
// mimicking the behavior of a frozen object in strict mode.
virtual void set(Runtime&, const PropNameID& name, const Value& value);
// When JS wants a list of property names for the HostObject, it will
// call this method. If it throws an exception, the call will throw a
// JS \c Error object. The default implementation returns empty vector.
virtual std::vector<PropNameID> getPropertyNames(Runtime& rt);
};
/// 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:
virtual ~Runtime();
/// Evaluates the given JavaScript \c buffer. \c sourceURL is used
/// to annotate the stack trace if there is an exception. The
/// contents may be utf8-encoded JS source code, or binary bytecode
/// whose format is specific to the implementation. If the input
/// format is unknown, or evaluation causes an error, a JSIException
/// will be thrown.
/// Note this function should ONLY be used when there isn't another means
/// through the JSI API. For example, it will be much slower to use this to
/// call a global function than using the JSI APIs to read the function
/// property from the global object and then calling it explicitly.
virtual Value evaluateJavaScript(
const std::shared_ptr<const Buffer>& buffer,
const std::string& sourceURL) = 0;
/// Prepares to evaluate the given JavaScript \c buffer by processing it into
/// a form optimized for execution. This may include pre-parsing, compiling,
/// etc. If the input is invalid (for example, cannot be parsed), a
/// JSIException will be thrown. The resulting object is tied to the
/// particular concrete type of Runtime from which it was created. It may be
/// used (via evaluatePreparedJavaScript) in any Runtime of the same concrete
/// type.
/// The PreparedJavaScript object may be passed to multiple VM instances, so
/// they can all share and benefit from the prepared script.
/// As with evaluateJavaScript(), using JavaScript code should be avoided
/// when the JSI API is sufficient.
virtual std::shared_ptr<const PreparedJavaScript> prepareJavaScript(
const std::shared_ptr<const Buffer>& buffer,
std::string sourceURL) = 0;
/// Evaluates a PreparedJavaScript. If evaluation causes an error, a
/// JSIException will be thrown.
/// As with evaluateJavaScript(), using JavaScript code should be avoided
/// when the JSI API is sufficient.
virtual Value evaluatePreparedJavaScript(
const std::shared_ptr<const PreparedJavaScript>& js) = 0;
/// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue.
///
/// \param maxMicrotasksHint a hint to tell an implementation that it should
/// make a best effort not execute more than the given number. It's default
/// to -1 for infinity (unbounded execution).
/// \return true if the queue is drained or false if there is more work to do.
///
/// When there were exceptions thrown from the execution of microtasks,
/// implementations shall discard the exceptional jobs. An implementation may
/// \throw a \c JSError object to signal the hosts to handle. In that case, an
/// implementation may or may not suspend the draining.
///
/// Hosts may call this function again to resume the draining if it was
/// suspended due to either exceptions or the \p maxMicrotasksHint bound.
/// E.g. a host may repetitively invoke this function until the queue is
/// drained to implement the "microtask checkpint" defined in WHATWG HTML
/// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint.
///
/// Note that error propagation is only a concern if a host needs to implement
/// `queueMicrotask`, a recent API that allows enqueueing aribitary functions
/// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are
/// handled internally to VMs and are never propagrated to hosts.
///
/// This API offers some queue management to hosts at its best effort due to
/// different behaviors and limitations imposed by different VMs and APIs. By
/// the time this is written, An implementation may swallow exceptions (JSC),
/// may not pause (V8), and may not support bounded executions.
virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0;
/// \return the global object
virtual Object global() = 0;
/// \return a short printable description of the instance. It should
/// at least include some human-readable indication of the runtime
/// implementation. This should only be used by logging, debugging,
/// and other developer-facing callers.
virtual std::string description() = 0;
/// \return whether or not the underlying runtime supports debugging via the
/// Chrome remote debugging protocol.
///
/// NOTE: the API for determining whether a runtime is debuggable and
/// registering a runtime with the debugger is still in flux, so please don't
/// use this API unless you know what you're doing.
virtual bool isInspectable() = 0;
/// \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();
protected:
friend class Pointer;
friend class PropNameID;
friend class Symbol;
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;
// Potential optimization: avoid the cloneFoo() virtual dispatch,
// and instead just fix the number of fields, and copy them, since
// in practice they are trivially copyable. Sufficient use of
// rvalue arguments/methods would also reduce the number of clones.
struct PointerValue {
virtual void invalidate() = 0;
protected:
virtual ~PointerValue() = default;
};
virtual PointerValue* cloneSymbol(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 PropNameID createPropNameIDFromAscii(
const char* str,
size_t length) = 0;
virtual PropNameID createPropNameIDFromUtf8(
const uint8_t* utf8,
size_t length) = 0;
virtual PropNameID createPropNameIDFromString(const String& str) = 0;
virtual std::string utf8(const PropNameID&) = 0;
virtual bool compare(const PropNameID&, const PropNameID&) = 0;
virtual std::string symbolToString(const Symbol&) = 0;
virtual String createStringFromAscii(const char* str, size_t length) = 0;
virtual String createStringFromUtf8(const uint8_t* utf8, 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 Object createObject() = 0;
virtual Object createObject(std::shared_ptr<HostObject> ho) = 0;
virtual std::shared_ptr<HostObject> getHostObject(const jsi::Object&) = 0;
virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0;
virtual Value getProperty(const Object&, const PropNameID& name) = 0;
virtual Value getProperty(const Object&, const String& name) = 0;
virtual bool hasProperty(const Object&, const PropNameID& name) = 0;
virtual bool hasProperty(const Object&, const String& name) = 0;
virtual void
setPropertyValue(Object&, const PropNameID& name, const Value& value) = 0;
virtual void
setPropertyValue(Object&, const String& name, const Value& value) = 0;
virtual bool isArray(const Object&) const = 0;
virtual bool isArrayBuffer(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 Array createArray(size_t length) = 0;
virtual size_t size(const Array&) = 0;
virtual size_t size(const ArrayBuffer&) = 0;
virtual uint8_t* data(const ArrayBuffer&) = 0;
virtual Value getValueAtIndex(const Array&, size_t i) = 0;
virtual void setValueAtIndexImpl(Array&, size_t i, const Value& value) = 0;
virtual Function createFunctionFromHostFunction(
const PropNameID& name,
unsigned int paramCount,
HostFunctionType func) = 0;
virtual Value call(
const Function&,
const Value& jsThis,
const Value* args,
size_t count) = 0;
virtual Value
callAsConstructor(const Function&, const Value* args, size_t count) = 0;
// Private data for managing scopes.
struct ScopeState;
virtual ScopeState* pushScope();
virtual void popScope(ScopeState*);
virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0;
virtual bool strictEquals(const String& a, const String& b) const = 0;
virtual bool strictEquals(const Object& a, const Object& b) const = 0;
virtual bool instanceOf(const Object& o, const Function& f) = 0;
// These exist so derived classes can access the private parts of
// Value, Symbol, String, and Object, which are all friends of Runtime.
template <typename T>
static T make(PointerValue* pv);
static PointerValue* getPointerValue(Pointer& pointer);
static const PointerValue* getPointerValue(const Pointer& pointer);
static const PointerValue* getPointerValue(const Value& value);
friend class ::FBJSRuntime;
template <typename Plain, typename Base>
friend class RuntimeDecorator;
};
// Base class for pointer-storing types.
class JSI_EXPORT Pointer {
protected:
explicit Pointer(Pointer&& other) : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
~Pointer() {
if (ptr_) {
ptr_->invalidate();
}
}
Pointer& operator=(Pointer&& other);
friend class Runtime;
friend class Value;
explicit Pointer(Runtime::PointerValue* ptr) : ptr_(ptr) {}
typename Runtime::PointerValue* ptr_;
};
/// Represents something that can be a JS property key. Movable, not copyable.
class JSI_EXPORT PropNameID : public Pointer {
public:
using Pointer::Pointer;
PropNameID(Runtime& runtime, const PropNameID& other)
: Pointer(runtime.clonePropNameID(other.ptr_)) {}
PropNameID(PropNameID&& other) = default;
PropNameID& operator=(PropNameID&& other) = default;
/// Create a JS property name id from ascii values. The data is
/// copied.
static PropNameID forAscii(Runtime& 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) {
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) {
return forAscii(runtime, str.c_str(), str.size());
}
/// Create a PropNameID from utf8 values. The data is copied.
static PropNameID
forUtf8(Runtime& 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.
static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) {
return runtime.createPropNameIDFromUtf8(
reinterpret_cast<const uint8_t*>(utf8.data()), utf8.size());
}
/// Create a PropNameID from a JS string.
static PropNameID forString(Runtime& runtime, const jsi::String& str) {
return runtime.createPropNameIDFromString(str);
}
// Creates a vector of PropNameIDs constructed from given arguments.
template <typename... Args>
static std::vector<PropNameID> names(Runtime& runtime, Args&&... args);
// Creates a vector of given PropNameIDs.
template <size_t N>
static std::vector<PropNameID> names(PropNameID(&&propertyNames)[N]);
/// Copies the data in a PropNameID as utf8 into a C++ string.
std::string utf8(Runtime& runtime) const {
return runtime.utf8(*this);
}
static bool compare(
Runtime& runtime,
const jsi::PropNameID& a,
const jsi::PropNameID& b) {
return runtime.compare(a, b);
}
friend class Runtime;
friend class Value;
};
/// Represents a JS Symbol (es6). Movable, not copyable.
/// TODO T40778724: this is a limited implementation sufficient for
/// the debugger not to crash when a Symbol is a property in an Object
/// or element in an array. Complete support for creating will come
/// later.
class JSI_EXPORT Symbol : public Pointer {
public:
using Pointer::Pointer;
Symbol(Symbol&& other) = default;
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) {
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 {
return runtime.symbolToString(*this);
}
friend class Runtime;
friend class Value;
};
/// Represents a JS String. Movable, not copyable.
class JSI_EXPORT String : public Pointer {
public:
using Pointer::Pointer;
String(String&& other) = default;
String& operator=(String&& other) = default;
/// Create a JS string from ascii values. The string data is
/// copied.
static String
createFromAscii(Runtime& 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) {
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) {
return createFromAscii(runtime, str.c_str(), str.size());
}
/// Create a JS string from utf8-encoded octets. The string data is
/// transformed and copied.
static String
createFromUtf8(Runtime& 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.
static String createFromUtf8(Runtime& runtime, const std::string& utf8) {
return runtime.createStringFromUtf8(
reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
}
/// \return whether a and b contain the same characters.
static bool strictEquals(Runtime& 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 {
return runtime.utf8(*this);
}
friend class Runtime;
friend class Value;
};
class Array;
class Function;
/// Represents a JS Object. Movable, not copyable.
class JSI_EXPORT Object : public Pointer {
public:
using Pointer::Pointer;
Object(Object&& other) = default;
Object& operator=(Object&& other) = default;
/// Creates a new Object instance, like '{}' in JS.
Object(Runtime& runtime) : Object(runtime.createObject()) {}
static Object createFromHostObject(
Runtime& runtime,
std::shared_ptr<HostObject> ho) {
return runtime.createObject(ho);
}
/// \return whether this and \c obj are the same JSObject or not.
static bool strictEquals(Runtime& 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) {
return rt.instanceOf(*this, ctor);
}
/// \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;
/// \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;
/// \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;
/// \return true if and only if the object has a property with the
/// given ascii name.
bool hasProperty(Runtime& 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;
/// \return true if and only if the object has a property with the
/// given PropNameID name.
bool hasProperty(Runtime& runtime, const PropNameID& 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 <typename T>
void setProperty(Runtime& runtime, const char* name, T&& value);
/// 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 <typename T>
void setProperty(Runtime& runtime, const String& name, T&& value);
/// 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 <typename T>
void setProperty(Runtime& runtime, const PropNameID& name, T&& value);
/// \return true iff JS \c Array.isArray() would return \c true. If
/// so, then \c getArray() will succeed.
bool isArray(Runtime& 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 {
return runtime.isArrayBuffer(*this);
}
/// \return true iff the Object is callable. If so, then \c
/// getFunction will succeed.
bool isFunction(Runtime& runtime) const {
return runtime.isFunction(*this);
}
/// \return true iff the Object was initialized with \c createFromHostObject
/// and the HostObject passed is of type \c T. If returns \c true then
/// \c getHostObject<T> will succeed.
template <typename T = HostObject>
bool isHostObject(Runtime& 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&;
/// \return an Array instance which refers to the same underlying
/// object. If \c isArray() would return false, this will assert.
Array getArray(Runtime& 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&;
/// \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) &&;
/// \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&;
/// \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 a Function instance which refers to the same underlying
/// object. If \c isFunction() would return false, this will assert.
Function getFunction(Runtime& 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) &&;
/// \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&;
/// \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) &&;
/// \return a shared_ptr<T> which refers to the same underlying
/// \c HostObject that was used to create this object. If \c isHostObject<T>
/// 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 <typename T = HostObject>
std::shared_ptr<T> getHostObject(Runtime& runtime) const;
/// \return a shared_ptr<T> which refers to the same underlying
/// \c HostObject that was used to crete this object. If \c isHostObject<T>
/// is false, this will throw.
template <typename T = HostObject>
std::shared_ptr<T> asHostObject(Runtime& runtime) const;
/// \return same as \c getProperty(name).asObject(), except with
/// a better exception message.
Object getPropertyAsObject(Runtime& 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;
/// \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;
protected:
void
setPropertyValue(Runtime& runtime, const String& name, const Value& value) {
return runtime.setPropertyValue(*this, name, value);
}
void setPropertyValue(
Runtime& runtime,
const PropNameID& name,
const Value& value) {
return runtime.setPropertyValue(*this, name, value);
}
friend class Runtime;
friend class Value;
};
/// Represents a weak reference to a JS Object. If the only reference
/// to an Object are these, the object is eligible for GC. Method
/// names are inspired by C++ weak_ptr. Movable, not copyable.
class JSI_EXPORT WeakObject : public Pointer {
public:
using Pointer::Pointer;
WeakObject(WeakObject&& other) = default;
WeakObject& operator=(WeakObject&& other) = default;
/// Create a WeakObject from an Object.
WeakObject(Runtime& 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);
friend class Runtime;
};
/// Represents a JS Object which can be efficiently used as an array
/// with integral indices.
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& operator=(Array&&) = default;
/// \return the size of the Array, according to its length property.
/// (C++ naming convention)
size_t size(Runtime& 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 {
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;
/// 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 <typename T>
void setValueAtIndex(Runtime& runtime, size_t i, T&& value);
/// There is no current API for changing the size of an array once
/// created. We'll probably need that eventually.
/// Creates a new Array instance from provided values
template <typename... Args>
static Array createWithElements(Runtime&, Args&&... args);
/// Creates a new Array instance from initializer list.
static Array createWithElements(
Runtime& runtime,
std::initializer_list<Value> elements);
private:
friend class Object;
friend class Value;
void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value) {
return runtime.setValueAtIndexImpl(*this, i, value);
}
Array(Runtime::PointerValue* value) : Object(value) {}
};
/// Represents a JSArrayBuffer
class JSI_EXPORT ArrayBuffer : public Object {
public:
ArrayBuffer(ArrayBuffer&&) = default;
ArrayBuffer& operator=(ArrayBuffer&&) = default;
/// \return the size of the ArrayBuffer, according to its byteLength property.
/// (C++ naming convention)
size_t size(Runtime& runtime) const {
return runtime.size(*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;
ArrayBuffer(Runtime::PointerValue* value) : Object(value) {}
};
/// Represents a JS Object which is guaranteed to be Callable.
class JSI_EXPORT Function : public Object {
public:
Function(Function&&) = default;
Function& operator=(Function&&) = default;
/// Create a function which, when invoked, calls C++ code. If the
/// function throws an exception, a JS Error will be created and
/// thrown.
/// \param name the name property for the function.
/// \param paramCount the length property for the function, which
/// may not be the number of arguments the function is passed.
static Function createFromHostFunction(
Runtime& runtime,
const jsi::PropNameID& name,
unsigned int paramCount,
jsi::HostFunctionType func);
/// Calls the function with \c count \c args. The \c this value of the JS
/// function will not be set by the C++ caller, similar to calling
/// Function.prototype.apply(undefined, args) in JS.
/// \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;
/// 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
/// C++ caller, similar to calling Function.prototype.apply(undefined, args)
/// in JS.
/// \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<Value> 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
/// set by the C++ caller, similar to calling
/// Function.prototype.call(undefined, ...args) in JS.
/// \b Note: as with Function.prototype.call, \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.
template <typename... Args>
Value call(Runtime& 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,
const Object& jsThis,
const Value* args,
size_t count) const;
/// 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,
const Object& jsThis,
std::initializer_list<Value> 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 <typename... Args>
Value callWithThis(Runtime& 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)
const;
/// Same as above `callAsConstructor`, except use an initializer_list to
/// supply the arguments.
Value callAsConstructor(Runtime& runtime, std::initializer_list<Value> args)
const;
/// Same as above `callAsConstructor`, but automatically converts/wraps
/// any argument with a jsi Value.
template <typename... Args>
Value callAsConstructor(Runtime& 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 {
return runtime.isHostFunction(*this);
}
/// Returns the underlying HostFunctionType iff isHostFunction returns true
/// and asserts otherwise. You can use this to use std::function<>::target
/// to get the object that was passed to create the HostFunctionType.
///
/// 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 {
assert(isHostFunction(runtime));
return runtime.getHostFunction(*this);
}
private:
friend class Object;
friend class Value;
Function(Runtime::PointerValue* value) : Object(value) {}
};
/// Represents any JS Value (undefined, null, boolean, number, symbol,
/// string, or object). Movable, or explicitly copyable (has no copy
/// ctor).
class JSI_EXPORT Value {
public:
/// Default ctor creates an \c undefined JS value.
Value() : Value(UndefinedKind) {}
/// Creates a \c null JS value.
/* implicit */ Value(std::nullptr_t) : kind_(NullKind) {}
/// Creates a boolean JS value.
/* implicit */ Value(bool b) : Value(BooleanKind) {
data_.boolean = b;
}
/// Creates a number JS value.
/* implicit */ Value(double d) : Value(NumberKind) {
data_.number = d;
}
/// Creates a number JS value.
/* implicit */ Value(int i) : Value(NumberKind) {
data_.number = i;
}
/// Moves a Symbol, String, or Object rvalue into a new JS value.
template <typename T>
/* implicit */ Value(T&& other) : Value(kindOf(other)) {
static_assert(
std::is_base_of<Symbol, T>::value ||
std::is_base_of<String, T>::value ||
std::is_base_of<Object, T>::value,
"Value cannot be implicitly move-constructed from this type");
new (&data_.pointer) T(std::move(other));
}
/// Value("foo") will treat foo as a bool. This makes doing that a
/// compile error.
template <typename T = void>
Value(const char*) {
static_assert(
!std::is_same<void, T>::value,
"Value cannot be constructed directly from const char*");
}
Value(Value&& value);
/// Copies a Symbol lvalue into a new JS value.
Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) {
new (&data_.pointer) String(runtime.cloneSymbol(sym.ptr_));
}
/// Copies a String lvalue into a new JS value.
Value(Runtime& 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) {
new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_));
}
/// Creates a JS value from another Value lvalue.
Value(Runtime& runtime, const Value& value);
/// Value(rt, "foo") will treat foo as a bool. This makes doing
/// that a compile error.
template <typename T = void>
Value(Runtime&, const char*) {
static_assert(
!std::is_same<T, void>::value,
"Value cannot be constructed directly from const char*");
}
~Value();
// \return the undefined \c Value.
static Value undefined() {
return Value();
}
// \return the null \c Value.
static Value null() {
return Value(nullptr);
}
// \return a \c Value created from a utf8-encoded JSON string.
static Value
createFromJsonUtf8(Runtime& 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);
Value& operator=(Value&& other) {
this->~Value();
new (this) Value(std::move(other));
return *this;
}
bool isUndefined() const {
return kind_ == UndefinedKind;
}
bool isNull() const {
return kind_ == NullKind;
}
bool isBool() const {
return kind_ == BooleanKind;
}
bool isNumber() const {
return kind_ == NumberKind;
}
bool isString() const {
return kind_ == StringKind;
}
bool isSymbol() const {
return kind_ == SymbolKind;
}
bool isObject() const {
return kind_ == ObjectKind;
}
/// \return the boolean value, or asserts if not a boolean.
bool getBool() const {
assert(isBool());
return data_.boolean;
}
/// \return the number value, or asserts if not a number.
double getNumber() const {
assert(isNumber());
return data_.number;
}
/// \return the number value, or throws JSIException if not a
/// number.
double asNumber() const;
/// \return the Symbol value, or asserts if not a symbol.
Symbol getSymbol(Runtime& 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&) && {
assert(isSymbol());
auto ptr = data_.pointer.ptr_;
data_.pointer.ptr_ = nullptr;
return static_cast<Symbol>(ptr);
}
/// \return the Symbol value, or throws JSIException if not a
/// symbol
Symbol asSymbol(Runtime& runtime) const&;
Symbol asSymbol(Runtime& runtime) &&;
/// \return the String value, or asserts if not a string.
String getString(Runtime& 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&) && {
assert(isString());
auto ptr = data_.pointer.ptr_;
data_.pointer.ptr_ = nullptr;
return static_cast<String>(ptr);
}
/// \return the String value, or throws JSIException if not a
/// string.
String asString(Runtime& runtime) const&;
String asString(Runtime& runtime) &&;
/// \return the Object value, or asserts if not an object.
Object getObject(Runtime& 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&) && {
assert(isObject());
auto ptr = data_.pointer.ptr_;
data_.pointer.ptr_ = nullptr;
return static_cast<Object>(ptr);
}
/// \return the Object value, or throws JSIException if not an
/// object.
Object asObject(Runtime& runtime) const&;
Object asObject(Runtime& runtime) &&;
// \return a String like JS .toString() would do.
String toString(Runtime& runtime) const;
private:
friend class Runtime;
enum ValueKind {
UndefinedKind,
NullKind,
BooleanKind,
NumberKind,
SymbolKind,
StringKind,
ObjectKind,
PointerKind = SymbolKind,
};
union Data {
// Value's ctor and dtor will manage the lifecycle of the contained Data.
Data() {
static_assert(
sizeof(Data) == sizeof(uint64_t),
"Value data should fit in a 64-bit register");
}
~Data() {}
// scalars
bool boolean;
double number;
// pointers
Pointer pointer; // Symbol, String, Object, Array, Function
};
Value(ValueKind kind) : kind_(kind) {}
constexpr static ValueKind kindOf(const Symbol&) {
return SymbolKind;
}
constexpr static ValueKind kindOf(const String&) {
return StringKind;
}
constexpr static ValueKind kindOf(const Object&) {
return ObjectKind;
}
ValueKind kind_;
Data data_;
// In the future: Value becomes NaN-boxed. See T40538354.
};
/// Not movable and not copyable RAII marker advising the underlying
/// JavaScript VM to track resources allocated since creation until
/// destruction so that they can be recycled eagerly when the Scope
/// goes out of scope instead of floating in the air until the next
/// garbage collection or any other delayed release occurs.
///
/// This API should be treated only as advice, implementations can
/// choose to ignore the fact that Scopes are created or destroyed.
///
/// This class is an exception to the rule allowing destructors to be
/// called without proper synchronization (see Runtime documentation).
/// The whole point of this class is to enable all sorts of clean ups
/// when the destructor is called and this proper synchronization is
/// required at that time.
///
/// Instances of this class are intended to be created as automatic stack
/// variables in which case destructor calls don't require any additional
/// 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()) {}
~Scope() {
rt_.popScope(prv_);
};
Scope(const Scope&) = delete;
Scope(Scope&&) = delete;
Scope& operator=(const Scope&) = delete;
Scope& operator=(Scope&&) = delete;
template <typename F>
static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) {
Scope s(rt);
return f();
}
private:
Runtime& rt_;
Runtime::ScopeState* prv_;
};
/// Base class for jsi exceptions
class JSI_EXPORT JSIException : public std::exception {
protected:
JSIException(){};
JSIException(std::string what) : what_(std::move(what)){};
public:
virtual const char* what() const noexcept override {
return what_.c_str();
}
virtual ~JSIException();
protected:
std::string what_;
};
/// This exception will be thrown by API functions on errors not related to
/// JavaScript execution.
class JSI_EXPORT JSINativeException : public JSIException {
public:
JSINativeException(std::string what) : JSIException(std::move(what)) {}
virtual ~JSINativeException();
};
/// This exception will be thrown by API functions whenever a JS
/// operation causes an exception as described by the spec, or as
/// otherwise described.
class JSI_EXPORT JSError : public JSIException {
public:
/// Creates a JSError referring to provided \c value
JSError(Runtime& 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);
/// 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(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);
/// 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);
virtual ~JSError();
const std::string& getStack() const {
return stack_;
}
const std::string& getMessage() const {
return message_;
}
const jsi::Value& value() const {
assert(value_);
return *value_;
}
private:
// 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);
// This needs to be on the heap, because throw requires the object
// be copyable, and Value is not.
std::shared_ptr<jsi::Value> value_;
std::string message_;
std::string stack_;
};
} // namespace jsi
} // namespace facebook
#include <jsi/jsi-inl.h>