/* * 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 #include #include #include #include #include #include #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(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 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& 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 prepareJavaScript( const std::shared_ptr& 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& 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 ho) = 0; virtual std::shared_ptr 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 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 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(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 static std::vector names(Runtime& 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 { 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(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 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 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 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 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 will succeed. template 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 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; /// \return a shared_ptr which refers to the same underlying /// \c HostObject that was used to crete this object. If \c isHostObject /// is false, this will throw. template std::shared_ptr 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 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 static Array createWithElements(Runtime&, Args&&... args); /// Creates a new Array instance from initializer list. static Array createWithElements( Runtime& runtime, std::initializer_list 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 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 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 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) 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 args) const; /// Same as above `callAsConstructor`, but automatically converts/wraps /// any argument with a jsi Value. template 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 /* implicit */ Value(T&& other) : Value(kindOf(other)) { static_assert( std::is_base_of::value || std::is_base_of::value || std::is_base_of::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 Value(const char*) { static_assert( !std::is_same::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 Value(Runtime&, const char*) { static_assert( !std::is_same::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(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(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(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 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 value_; std::string message_; std::string stack_; }; } // namespace jsi } // namespace facebook #include