UNPKG

43.6 kBtext/x-cView Raw
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the LICENSE
5 * file in the root directory of this source tree.
6 */
7#pragma once
8
9#include <cassert>
10#include <cstring>
11#include <exception>
12#include <functional>
13#include <memory>
14#include <string>
15#include <vector>
16
17#ifndef JSI_EXPORT
18#ifdef _MSC_VER
19#ifdef JSI_CREATE_SHARED_LIBRARY
20#define JSI_EXPORT __declspec(dllexport)
21#else
22#define JSI_EXPORT
23#endif // JSI_CREATE_SHARED_LIBRARY
24#else // _MSC_VER
25#define JSI_EXPORT __attribute__((visibility("default")))
26#endif // _MSC_VER
27#endif // !defined(JSI_EXPORT)
28
29class FBJSRuntime;
30namespace facebook {
31namespace jsi {
32
33class Buffer {
34 public:
35 virtual ~Buffer();
36 virtual size_t size() const = 0;
37 virtual const uint8_t* data() const = 0;
38};
39
40class StringBuffer : public Buffer {
41 public:
42 StringBuffer(std::string s) : s_(std::move(s)) {}
43 size_t size() const override {
44 return s_.size();
45 }
46 const uint8_t* data() const override {
47 return reinterpret_cast<const uint8_t*>(s_.data());
48 }
49
50 private:
51 std::string s_;
52};
53
54/// PreparedJavaScript is a base class representing JavaScript which is in a form
55/// optimized for execution, in a runtime-specific way. Construct one via
56/// jsi::Runtime::prepareJavaScript().
57/// ** This is an experimental API that is subject to change. **
58class PreparedJavaScript {
59 protected:
60 PreparedJavaScript() = default;
61
62 public:
63 virtual ~PreparedJavaScript() = 0;
64};
65
66class Runtime;
67class Pointer;
68class PropNameID;
69class Symbol;
70class String;
71class Object;
72class WeakObject;
73class Array;
74class ArrayBuffer;
75class Function;
76class Value;
77class Instrumentation;
78class Scope;
79class JSIException;
80class JSError;
81
82/// A function which has this type can be registered as a function
83/// callable from JavaScript using Function::createFromHostFunction().
84/// When the function is called, args will point to the arguments, and
85/// count will indicate how many arguments are passed. The function
86/// can return a Value to the caller, or throw an exception. If a C++
87/// exception is thrown, a JS Error will be created and thrown into
88/// JS; if the C++ exception extends std::exception, the Error's
89/// message will be whatever what() returns. Note that it is undefined whether
90/// HostFunctions may or may not be called in strict mode; that is `thisVal`
91/// can be any value - it will not necessarily be coerced to an object or
92/// or set to the global object.
93using HostFunctionType = std::function<
94 Value(Runtime& rt, const Value& thisVal, const Value* args, size_t count)>;
95
96/// An object which implements this interface can be registered as an
97/// Object with the JS runtime.
98class JSI_EXPORT HostObject {
99 public:
100 // The C++ object's dtor will be called when the GC finalizes this
101 // object. (This may be as late as when the Runtime is shut down.)
102 // You have no control over which thread it is called on. This will
103 // be called from inside the GC, so it is unsafe to do any VM
104 // operations which require a Runtime&. Derived classes' dtors
105 // should also avoid doing anything expensive. Calling the dtor on
106 // a jsi object is explicitly ok. If you want to do JS operations,
107 // or any nontrivial work, you should add it to a work queue, and
108 // manage it externally.
109 virtual ~HostObject();
110
111 // When JS wants a property with a given name from the HostObject,
112 // it will call this method. If it throws an exception, the call
113 // will throw a JS \c Error object. By default this returns undefined.
114 // \return the value for the property.
115 virtual Value get(Runtime&, const PropNameID& name);
116
117 // When JS wants to set a property with a given name on the HostObject,
118 // it will call this method. If it throws an exception, the call will
119 // throw a JS \c Error object. By default this throws a type error exception
120 // mimicking the behavior of a frozen object in strict mode.
121 virtual void set(Runtime&, const PropNameID& name, const Value& value);
122
123 // When JS wants a list of property names for the HostObject, it will
124 // call this method. If it throws an exception, the call will thow a
125 // JS \c Error object. The default implementation returns empty vector.
126 virtual std::vector<PropNameID> getPropertyNames(Runtime& rt);
127};
128
129/// Represents a JS runtime. Movable, but not copyable. Note that
130/// this object may not be thread-aware, but cannot be used safely from
131/// multiple threads at once. The application is responsible for
132/// ensuring that it is used safely. This could mean using the
133/// Runtime from a single thread, using a mutex, doing all work on a
134/// serial queue, etc. This restriction applies to the methods of
135/// this class, and any method in the API which take a Runtime& as an
136/// argument. Destructors (all but ~Scope), operators, or other methods
137/// which do not take Runtime& as an argument are safe to call from any
138/// thread, but it is still forbidden to make write operations on a single
139/// instance of any class from more than one thread. In addition, to
140/// make shutdown safe, destruction of objects associated with the Runtime
141/// must be destroyed before the Runtime is destroyed, or from the
142/// destructor of a managed HostObject or HostFunction. Informally, this
143/// means that the main source of unsafe behavior is to hold a jsi object
144/// in a non-Runtime-managed object, and not clean it up before the Runtime
145/// is shut down. If your lifecycle is such that avoiding this is hard,
146/// you will probably need to do use your own locks.
147class Runtime {
148 public:
149 virtual ~Runtime();
150
151 /// Evaluates the given JavaScript \c buffer. \c sourceURL is used
152 /// to annotate the stack trace if there is an exception. The
153 /// contents may be utf8-encoded JS source code, or binary bytecode
154 /// whose format is specific to the implementation. If the input
155 /// format is unknown, or evaluation causes an error, a JSIException
156 /// will be thrown.
157 /// Note this function should ONLY be used when there isn't another means
158 /// through the JSI API. For example, it will be much slower to use this to
159 /// call a global function than using the JSI APIs to read the function
160 /// property from the global object and then calling it explicitly.
161 virtual Value evaluateJavaScript(
162 const std::shared_ptr<const Buffer>& buffer,
163 const std::string& sourceURL) = 0;
164
165 /// Prepares to evaluate the given JavaScript \c buffer by processing it into
166 /// a form optimized for execution. This may include pre-parsing, compiling,
167 /// etc. If the input is invalid (for example, cannot be parsed), a
168 /// JSIException will be thrown. The resulting object is tied to the
169 /// particular concrete type of Runtime from which it was created. It may be
170 /// used (via evaluatePreparedJavaScript) in any Runtime of the same concrete
171 /// type.
172 /// The PreparedJavaScript object may be passed to multiple VM instances, so
173 /// they can all share and benefit from the prepared script.
174 /// As with evaluateJavaScript(), using JavaScript code should be avoided
175 /// when the JSI API is sufficient.
176 virtual std::shared_ptr<const PreparedJavaScript> prepareJavaScript(
177 const std::shared_ptr<const Buffer>& buffer,
178 std::string sourceURL) = 0;
179
180 /// Evaluates a PreparedJavaScript. If evaluation causes an error, a
181 /// JSIException will be thrown.
182 /// As with evaluateJavaScript(), using JavaScript code should be avoided
183 /// when the JSI API is sufficient.
184 virtual Value evaluatePreparedJavaScript(
185 const std::shared_ptr<const PreparedJavaScript>& js) = 0;
186
187 /// \return the global object
188 virtual Object global() = 0;
189
190 /// \return a short printable description of the instance. This
191 /// should only be used by logging, debugging, and other
192 /// developer-facing callers.
193 virtual std::string description() = 0;
194
195 /// \return whether or not the underlying runtime supports debugging via the
196 /// Chrome remote debugging protocol.
197 ///
198 /// NOTE: the API for determining whether a runtime is debuggable and
199 /// registering a runtime with the debugger is still in flux, so please don't
200 /// use this API unless you know what you're doing.
201 virtual bool isInspectable() = 0;
202
203 /// \return an interface to extract metrics from this \c Runtime. The default
204 /// implementation of this function returns an \c Instrumentation instance
205 /// which returns no metrics.
206 virtual Instrumentation& instrumentation();
207
208 protected:
209 friend class Pointer;
210 friend class PropNameID;
211 friend class Symbol;
212 friend class String;
213 friend class Object;
214 friend class WeakObject;
215 friend class Array;
216 friend class ArrayBuffer;
217 friend class Function;
218 friend class Value;
219 friend class Scope;
220 friend class JSError;
221
222 // Potential optimization: avoid the cloneFoo() virtual dispatch,
223 // and instead just fix the number of fields, and copy them, since
224 // in practice they are trivially copyable. Sufficient use of
225 // rvalue arguments/methods would also reduce the number of clones.
226
227 struct PointerValue {
228 virtual void invalidate() = 0;
229
230 protected:
231 virtual ~PointerValue() = default;
232 };
233
234 virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0;
235 virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0;
236 virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0;
237 virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0;
238
239 virtual PropNameID createPropNameIDFromAscii(
240 const char* str,
241 size_t length) = 0;
242 virtual PropNameID createPropNameIDFromUtf8(
243 const uint8_t* utf8,
244 size_t length) = 0;
245 virtual PropNameID createPropNameIDFromString(const String& str) = 0;
246 virtual std::string utf8(const PropNameID&) = 0;
247 virtual bool compare(const PropNameID&, const PropNameID&) = 0;
248
249 virtual std::string symbolToString(const Symbol&) = 0;
250
251 virtual String createStringFromAscii(const char* str, size_t length) = 0;
252 virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0;
253 virtual std::string utf8(const String&) = 0;
254
255 virtual Object createObject() = 0;
256 virtual Object createObject(std::shared_ptr<HostObject> ho) = 0;
257 virtual std::shared_ptr<HostObject> getHostObject(const jsi::Object&) = 0;
258 virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0;
259
260 virtual Value getProperty(const Object&, const PropNameID& name) = 0;
261 virtual Value getProperty(const Object&, const String& name) = 0;
262 virtual bool hasProperty(const Object&, const PropNameID& name) = 0;
263 virtual bool hasProperty(const Object&, const String& name) = 0;
264 virtual void
265 setPropertyValue(Object&, const PropNameID& name, const Value& value) = 0;
266 virtual void
267 setPropertyValue(Object&, const String& name, const Value& value) = 0;
268
269 virtual bool isArray(const Object&) const = 0;
270 virtual bool isArrayBuffer(const Object&) const = 0;
271 virtual bool isFunction(const Object&) const = 0;
272 virtual bool isHostObject(const jsi::Object&) const = 0;
273 virtual bool isHostFunction(const jsi::Function&) const = 0;
274 virtual Array getPropertyNames(const Object&) = 0;
275
276 virtual WeakObject createWeakObject(const Object&) = 0;
277 virtual Value lockWeakObject(const WeakObject&) = 0;
278
279 virtual Array createArray(size_t length) = 0;
280 virtual size_t size(const Array&) = 0;
281 virtual size_t size(const ArrayBuffer&) = 0;
282 virtual uint8_t* data(const ArrayBuffer&) = 0;
283 virtual Value getValueAtIndex(const Array&, size_t i) = 0;
284 virtual void setValueAtIndexImpl(Array&, size_t i, const Value& value) = 0;
285
286 virtual Function createFunctionFromHostFunction(
287 const PropNameID& name,
288 unsigned int paramCount,
289 HostFunctionType func) = 0;
290 virtual Value call(
291 const Function&,
292 const Value& jsThis,
293 const Value* args,
294 size_t count) = 0;
295 virtual Value
296 callAsConstructor(const Function&, const Value* args, size_t count) = 0;
297
298 // Private data for managing scopes.
299 struct ScopeState;
300 virtual ScopeState* pushScope();
301 virtual void popScope(ScopeState*);
302
303 virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0;
304 virtual bool strictEquals(const String& a, const String& b) const = 0;
305 virtual bool strictEquals(const Object& a, const Object& b) const = 0;
306
307 virtual bool instanceOf(const Object& o, const Function& f) = 0;
308
309 // These exist so derived classes can access the private parts of
310 // Value, Symbol, String, and Object, which are all friends of Runtime.
311 template <typename T>
312 static T make(PointerValue* pv);
313 static const PointerValue* getPointerValue(const Pointer& pointer);
314 static const PointerValue* getPointerValue(const Value& value);
315
316 friend class ::FBJSRuntime;
317 template <typename Plain, typename Base>
318 friend class RuntimeDecorator;
319};
320
321// Base class for pointer-storing types.
322class Pointer {
323 protected:
324 explicit Pointer(Pointer&& other) : ptr_(other.ptr_) {
325 other.ptr_ = nullptr;
326 }
327
328 ~Pointer() {
329 if (ptr_) {
330 ptr_->invalidate();
331 }
332 }
333
334 Pointer& operator=(Pointer&& other);
335
336 friend class Runtime;
337 friend class Value;
338
339 explicit Pointer(Runtime::PointerValue* ptr) : ptr_(ptr) {}
340
341 typename Runtime::PointerValue* ptr_;
342};
343
344/// Represents something that can be a JS property key. Movable, not copyable.
345class PropNameID : public Pointer {
346 public:
347 using Pointer::Pointer;
348
349 PropNameID(Runtime& runtime, const PropNameID& other)
350 : Pointer(runtime.clonePropNameID(other.ptr_)) {}
351
352 PropNameID(PropNameID&& other) = default;
353 PropNameID& operator=(PropNameID&& other) = default;
354
355 /// Create a JS property name id from ascii values. The data is
356 /// copied.
357 static PropNameID forAscii(Runtime& runtime, const char* str, size_t length) {
358 return runtime.createPropNameIDFromAscii(str, length);
359 }
360
361 /// Create a property name id from a nul-terminated C ascii name. The data is
362 /// copied.
363 static PropNameID forAscii(Runtime& runtime, const char* str) {
364 return forAscii(runtime, str, strlen(str));
365 }
366
367 /// Create a PropNameID from a C++ string. The string is copied.
368 static PropNameID forAscii(Runtime& runtime, const std::string& str) {
369 return forAscii(runtime, str.c_str(), str.size());
370 }
371
372 /// Create a PropNameID from utf8 values. The data is copied.
373 static PropNameID
374 forUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) {
375 return runtime.createPropNameIDFromUtf8(utf8, length);
376 }
377
378 /// Create a PropNameID from utf8-encoded octets stored in a
379 /// std::string. The string data is transformed and copied.
380 static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) {
381 return runtime.createPropNameIDFromUtf8(
382 reinterpret_cast<const uint8_t*>(utf8.data()), utf8.size());
383 }
384
385 /// Create a PropNameID from a JS string.
386 static PropNameID forString(Runtime& runtime, const jsi::String& str) {
387 return runtime.createPropNameIDFromString(str);
388 }
389
390 // Creates a vector of PropNameIDs constructed from given arguments.
391 template <typename... Args>
392 static std::vector<PropNameID> names(Runtime& runtime, Args&&... args);
393
394 // Creates a vector of given PropNameIDs.
395 template <size_t N>
396 static std::vector<PropNameID> names(PropNameID(&&propertyNames)[N]);
397
398 /// Copies the data in a PropNameID as utf8 into a C++ string.
399 std::string utf8(Runtime& runtime) const {
400 return runtime.utf8(*this);
401 }
402
403 static bool compare(
404 Runtime& runtime,
405 const jsi::PropNameID& a,
406 const jsi::PropNameID& b) {
407 return runtime.compare(a, b);
408 }
409
410 friend class Runtime;
411 friend class Value;
412};
413
414/// Represents a JS Symbol (es6). Movable, not copyable.
415/// TODO T40778724: this is a limited implementation sufficient for
416/// the debugger not to crash when a Symbol is a property in an Object
417/// or element in an array. Complete support for creating will come
418/// later.
419class Symbol : public Pointer {
420 public:
421 using Pointer::Pointer;
422
423 Symbol(Symbol&& other) = default;
424 Symbol& operator=(Symbol&& other) = default;
425
426 /// \return whether a and b refer to the same symbol.
427 static bool strictEquals(Runtime& runtime, const Symbol& a, const Symbol& b) {
428 return runtime.strictEquals(a, b);
429 }
430
431 /// Converts a Symbol into a C++ string as JS .toString would. The output
432 /// will look like \c Symbol(description) .
433 std::string toString(Runtime& runtime) const {
434 return runtime.symbolToString(*this);
435 }
436
437 friend class Runtime;
438 friend class Value;
439};
440
441/// Represents a JS String. Movable, not copyable.
442class String : public Pointer {
443 public:
444 using Pointer::Pointer;
445
446 String(String&& other) = default;
447 String& operator=(String&& other) = default;
448
449 /// Create a JS string from ascii values. The string data is
450 /// copied.
451 static String
452 createFromAscii(Runtime& runtime, const char* str, size_t length) {
453 return runtime.createStringFromAscii(str, length);
454 }
455
456 /// Create a JS string from a nul-terminated C ascii string. The
457 /// string data is copied.
458 static String createFromAscii(Runtime& runtime, const char* str) {
459 return createFromAscii(runtime, str, strlen(str));
460 }
461
462 /// Create a JS string from a C++ string. The string data is
463 /// copied.
464 static String createFromAscii(Runtime& runtime, const std::string& str) {
465 return createFromAscii(runtime, str.c_str(), str.size());
466 }
467
468 /// Create a JS string from utf8-encoded octets. The string data is
469 /// transformed and copied.
470 static String
471 createFromUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) {
472 return runtime.createStringFromUtf8(utf8, length);
473 }
474
475 /// Create a JS string from utf8-encoded octets stored in a
476 /// std::string. The string data is transformed and copied.
477 static String createFromUtf8(Runtime& runtime, const std::string& utf8) {
478 return runtime.createStringFromUtf8(
479 reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
480 }
481
482 /// \return whether a and b contain the same characters.
483 static bool strictEquals(Runtime& runtime, const String& a, const String& b) {
484 return runtime.strictEquals(a, b);
485 }
486
487 /// Copies the data in a JS string as utf8 into a C++ string.
488 std::string utf8(Runtime& runtime) const {
489 return runtime.utf8(*this);
490 }
491
492 friend class Runtime;
493 friend class Value;
494};
495
496class Array;
497class Function;
498
499/// Represents a JS Object. Movable, not copyable.
500class Object : public Pointer {
501 public:
502 using Pointer::Pointer;
503
504 Object(Object&& other) = default;
505 Object& operator=(Object&& other) = default;
506
507 /// Creates a new Object instance, like '{}' in JS.
508 Object(Runtime& runtime) : Object(runtime.createObject()) {}
509
510 static Object createFromHostObject(
511 Runtime& runtime,
512 std::shared_ptr<HostObject> ho) {
513 return runtime.createObject(ho);
514 }
515
516 /// \return whether this and \c obj are the same JSObject or not.
517 static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) {
518 return runtime.strictEquals(a, b);
519 }
520
521 /// \return the result of `this instanceOf ctor` in JS.
522 bool instanceOf(Runtime& rt, const Function& ctor) {
523 return rt.instanceOf(*this, ctor);
524 }
525
526 /// \return the property of the object with the given ascii name.
527 /// If the name isn't a property on the object, returns the
528 /// undefined value.
529 Value getProperty(Runtime& runtime, const char* name) const;
530
531 /// \return the property of the object with the String name.
532 /// If the name isn't a property on the object, returns the
533 /// undefined value.
534 Value getProperty(Runtime& runtime, const String& name) const;
535
536 /// \return the property of the object with the given JS PropNameID
537 /// name. If the name isn't a property on the object, returns the
538 /// undefined value.
539 Value getProperty(Runtime& runtime, const PropNameID& name) const;
540
541 /// \return true if and only if the object has a property with the
542 /// given ascii name.
543 bool hasProperty(Runtime& runtime, const char* name) const;
544
545 /// \return true if and only if the object has a property with the
546 /// given String name.
547 bool hasProperty(Runtime& runtime, const String& name) const;
548
549 /// \return true if and only if the object has a property with the
550 /// given PropNameID name.
551 bool hasProperty(Runtime& runtime, const PropNameID& name) const;
552
553 /// Sets the property value from a Value or anything which can be
554 /// used to make one: nullptr_t, bool, double, int, const char*,
555 /// String, or Object.
556 template <typename T>
557 void setProperty(Runtime& runtime, const char* name, T&& value);
558
559 /// Sets the property value from a Value or anything which can be
560 /// used to make one: nullptr_t, bool, double, int, const char*,
561 /// String, or Object.
562 template <typename T>
563 void setProperty(Runtime& runtime, const String& name, T&& value);
564
565 /// Sets the property value from a Value or anything which can be
566 /// used to make one: nullptr_t, bool, double, int, const char*,
567 /// String, or Object.
568 template <typename T>
569 void setProperty(Runtime& runtime, const PropNameID& name, T&& value);
570
571 /// \return true iff JS \c Array.isArray() would return \c true. If
572 /// so, then \c getArray() will succeed.
573 bool isArray(Runtime& runtime) const {
574 return runtime.isArray(*this);
575 }
576
577 /// \return true iff the Object is an ArrayBuffer. If so, then \c
578 /// getArrayBuffer() will succeed.
579 bool isArrayBuffer(Runtime& runtime) const {
580 return runtime.isArrayBuffer(*this);
581 }
582
583 /// \return true iff the Object is callable. If so, then \c
584 /// getFunction will succeed.
585 bool isFunction(Runtime& runtime) const {
586 return runtime.isFunction(*this);
587 }
588
589 /// \return true iff the Object was initialized with \c createFromHostObject
590 /// and the HostObject passed is of type \c T. If returns \c true then
591 /// \c getHostObject<T> will succeed.
592 template <typename T = HostObject>
593 bool isHostObject(Runtime& runtime) const;
594
595 /// \return an Array instance which refers to the same underlying
596 /// object. If \c isArray() would return false, this will assert.
597 Array getArray(Runtime& runtime) const&;
598
599 /// \return an Array instance which refers to the same underlying
600 /// object. If \c isArray() would return false, this will assert.
601 Array getArray(Runtime& runtime) &&;
602
603 /// \return an Array instance which refers to the same underlying
604 /// object. If \c isArray() would return false, this will throw
605 /// JSIException.
606 Array asArray(Runtime& runtime) const&;
607
608 /// \return an Array instance which refers to the same underlying
609 /// object. If \c isArray() would return false, this will throw
610 /// JSIException.
611 Array asArray(Runtime& runtime) &&;
612
613 /// \return an ArrayBuffer instance which refers to the same underlying
614 /// object. If \c isArrayBuffer() would return false, this will assert.
615 ArrayBuffer getArrayBuffer(Runtime& runtime) const&;
616
617 /// \return an ArrayBuffer instance which refers to the same underlying
618 /// object. If \c isArrayBuffer() would return false, this will assert.
619 ArrayBuffer getArrayBuffer(Runtime& runtime) &&;
620
621 /// \return a Function instance which refers to the same underlying
622 /// object. If \c isFunction() would return false, this will assert.
623 Function getFunction(Runtime& runtime) const&;
624
625 /// \return a Function instance which refers to the same underlying
626 /// object. If \c isFunction() would return false, this will assert.
627 Function getFunction(Runtime& runtime) &&;
628
629 /// \return a Function instance which refers to the same underlying
630 /// object. If \c isFunction() would return false, this will throw
631 /// JSIException.
632 Function asFunction(Runtime& runtime) const&;
633
634 /// \return a Function instance which refers to the same underlying
635 /// object. If \c isFunction() would return false, this will throw
636 /// JSIException.
637 Function asFunction(Runtime& runtime) &&;
638
639 /// \return a shared_ptr<T> which refers to the same underlying
640 /// \c HostObject that was used to create this object. If \c isHostObject<T>
641 /// is false, this will assert. Note that this does a type check and will
642 /// assert if the underlying HostObject isn't of type \c T
643 template <typename T = HostObject>
644 std::shared_ptr<T> getHostObject(Runtime& runtime) const;
645
646 /// \return a shared_ptr<T> which refers to the same underlying
647 /// \c HostObject that was used to crete this object. If \c isHostObject<T>
648 /// is false, this will throw.
649 template <typename T = HostObject>
650 std::shared_ptr<T> asHostObject(Runtime& runtime) const;
651
652 /// \return same as \c getProperty(name).asObject(), except with
653 /// a better exception message.
654 Object getPropertyAsObject(Runtime& runtime, const char* name) const;
655
656 /// \return similar to \c
657 /// getProperty(name).getObject().getFunction(), except it will
658 /// throw JSIException instead of asserting if the property is
659 /// not an object, or the object is not callable.
660 Function getPropertyAsFunction(Runtime& runtime, const char* name) const;
661
662 /// \return an Array consisting of all enumerable property names in
663 /// the object and its prototype chain. All values in the return
664 /// will be isString(). (This is probably not optimal, but it
665 /// works. I only need it in one place.)
666 Array getPropertyNames(Runtime& runtime) const;
667
668 protected:
669 void
670 setPropertyValue(Runtime& runtime, const String& name, const Value& value) {
671 return runtime.setPropertyValue(*this, name, value);
672 }
673
674 void setPropertyValue(
675 Runtime& runtime,
676 const PropNameID& name,
677 const Value& value) {
678 return runtime.setPropertyValue(*this, name, value);
679 }
680
681 friend class Runtime;
682 friend class Value;
683};
684
685/// Represents a weak reference to a JS Object. If the only reference
686/// to an Object are these, the object is eligible for GC. Method
687/// names are inspired by C++ weak_ptr. Movable, not copyable.
688class WeakObject : public Pointer {
689 public:
690 using Pointer::Pointer;
691
692 WeakObject(WeakObject&& other) = default;
693 WeakObject& operator=(WeakObject&& other) = default;
694
695 /// Create a WeakObject from an Object.
696 WeakObject(Runtime& runtime, const Object& o)
697 : WeakObject(runtime.createWeakObject(o)) {}
698
699 /// \return a Value representing the underlying Object if it is still valid;
700 /// otherwise returns \c undefined. Note that this method has nothing to do
701 /// with threads or concurrency. The name is based on std::weak_ptr::lock()
702 /// which serves a similar purpose.
703 Value lock(Runtime& runtime);
704
705 friend class Runtime;
706};
707
708/// Represents a JS Object which can be efficiently used as an array
709/// with integral indices.
710class Array : public Object {
711 public:
712 Array(Array&&) = default;
713 /// Creates a new Array instance, with \c length undefined elements.
714 Array(Runtime& runtime, size_t length) : Array(runtime.createArray(length)) {}
715
716 Array& operator=(Array&&) = default;
717
718 /// \return the size of the Array, according to its length property.
719 /// (C++ naming convention)
720 size_t size(Runtime& runtime) const {
721 return runtime.size(*this);
722 }
723
724 /// \return the size of the Array, according to its length property.
725 /// (JS naming convention)
726 size_t length(Runtime& runtime) const {
727 return size(runtime);
728 }
729
730 /// \return the property of the array at index \c i. If there is no
731 /// such property, returns the undefined value. If \c i is out of
732 /// range [ 0..\c length ] throws a JSIException.
733 Value getValueAtIndex(Runtime& runtime, size_t i) const;
734
735 /// Sets the property of the array at index \c i. The argument
736 /// value behaves as with Object::setProperty(). If \c i is out of
737 /// range [ 0..\c length ] throws a JSIException.
738 template <typename T>
739 void setValueAtIndex(Runtime& runtime, size_t i, T&& value);
740
741 /// There is no current API for changing the size of an array once
742 /// created. We'll probably need that eventually.
743
744 /// Creates a new Array instance from provided values
745 template <typename... Args>
746 static Array createWithElements(Runtime&, Args&&... args);
747
748 /// Creates a new Array instance from initializer list.
749 static Array createWithElements(
750 Runtime& runtime,
751 std::initializer_list<Value> elements);
752
753 private:
754 friend class Object;
755 friend class Value;
756
757 void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value) {
758 return runtime.setValueAtIndexImpl(*this, i, value);
759 }
760
761 Array(Runtime::PointerValue* value) : Object(value) {}
762};
763
764/// Represents a JSArrayBuffer
765class ArrayBuffer : public Object {
766 public:
767 ArrayBuffer(ArrayBuffer&&) = default;
768 ArrayBuffer& operator=(ArrayBuffer&&) = default;
769
770 /// \return the size of the ArrayBuffer, according to its byteLength property.
771 /// (C++ naming convention)
772 size_t size(Runtime& runtime) const {
773 return runtime.size(*this);
774 }
775
776 size_t length(Runtime& runtime) const {
777 return runtime.size(*this);
778 }
779
780 uint8_t* data(Runtime& runtime) {
781 return runtime.data(*this);
782 }
783
784 private:
785 friend class Object;
786 friend class Value;
787
788 ArrayBuffer(Runtime::PointerValue* value) : Object(value) {}
789};
790
791/// Represents a JS Object which is guaranteed to be Callable.
792class Function : public Object {
793 public:
794 Function(Function&&) = default;
795 Function& operator=(Function&&) = default;
796
797 /// Create a function which, when invoked, calls C++ code. If the
798 /// function throws an exception, a JS Error will be created and
799 /// thrown.
800 /// \param name the name property for the function.
801 /// \param paramCount the length property for the function, which
802 /// may not be the number of arguments the function is passed.
803 static Function createFromHostFunction(
804 Runtime& runtime,
805 const jsi::PropNameID& name,
806 unsigned int paramCount,
807 jsi::HostFunctionType func);
808
809 /// Calls the function with \c count \c args. The \c this value of
810 /// the JS function will be undefined.
811 Value call(Runtime& runtime, const Value* args, size_t count) const;
812
813 /// Calls the function with a \c std::initializer_list of Value
814 /// arguments. The \c this value of the JS function will be
815 /// undefined.
816 Value call(Runtime& runtime, std::initializer_list<Value> args) const;
817
818 /// Calls the function with any number of arguments similarly to
819 /// Object::setProperty(). The \c this value of the JS function
820 /// will be undefined.
821 template <typename... Args>
822 Value call(Runtime& runtime, Args&&... args) const;
823
824 /// Calls the function with \c count \c args and \c jsThis value passed
825 /// as this value.
826 Value callWithThis(
827 Runtime& Runtime,
828 const Object& jsThis,
829 const Value* args,
830 size_t count) const;
831
832 /// Calls the function with a \c std::initializer_list of Value
833 /// arguments. The \c this value of the JS function will be
834 /// undefined.
835 Value callWithThis(
836 Runtime& runtime,
837 const Object& jsThis,
838 std::initializer_list<Value> args) const;
839
840 /// Calls the function with any number of arguments similarly to
841 /// Object::setProperty(). The \c this value of the JS function
842 /// will be undefined.
843 template <typename... Args>
844 Value callWithThis(Runtime& runtime, const Object& jsThis, Args&&... args)
845 const;
846
847 /// Calls the function as a constructor with \c count \c args. Equivalent
848 /// to calling `new Func` where `Func` is the js function reqresented by
849 /// this.
850 Value callAsConstructor(Runtime& runtime, const Value* args, size_t count)
851 const;
852
853 /// Same as above `callAsConstructor`, except use an initializer_list to
854 /// supply the arguments.
855 Value callAsConstructor(Runtime& runtime, std::initializer_list<Value> args)
856 const;
857
858 /// Same as above `callAsConstructor`, but automatically converts/wraps
859 /// any argument with a jsi Value.
860 template <typename... Args>
861 Value callAsConstructor(Runtime& runtime, Args&&... args) const;
862
863 /// Returns whether this was created with Function::createFromHostFunction.
864 /// If true then you can use getHostFunction to get the underlying
865 /// HostFunctionType.
866 bool isHostFunction(Runtime& runtime) const {
867 return runtime.isHostFunction(*this);
868 }
869
870 /// Returns the underlying HostFunctionType iff isHostFunction returns true
871 /// and asserts otherwise. You can use this to use std::function<>::target
872 /// to get the object that was passed to create the HostFunctionType.
873 ///
874 /// Note: The reference returned is borrowed from the JS object underlying
875 /// \c this, and thus only lasts as long as the object underlying
876 /// \c this does.
877 HostFunctionType& getHostFunction(Runtime& runtime) const {
878 assert(isHostFunction(runtime));
879 return runtime.getHostFunction(*this);
880 }
881
882 private:
883 friend class Object;
884 friend class Value;
885
886 Function(Runtime::PointerValue* value) : Object(value) {}
887};
888
889/// Represents any JS Value (undefined, null, boolean, number, symbol,
890/// string, or object). Movable, or explicitly copyable (has no copy
891/// ctor).
892class Value {
893 public:
894 /// Default ctor creates an \c undefined JS value.
895 Value() : Value(UndefinedKind) {}
896
897 /// Creates a \c null JS value.
898 /* implicit */ Value(std::nullptr_t) : kind_(NullKind) {}
899
900 /// Creates a boolean JS value.
901 /* implicit */ Value(bool b) : Value(BooleanKind) {
902 data_.boolean = b;
903 }
904
905 /// Creates a number JS value.
906 /* implicit */ Value(double d) : Value(NumberKind) {
907 data_.number = d;
908 }
909
910 /// Creates a number JS value.
911 /* implicit */ Value(int i) : Value(NumberKind) {
912 data_.number = i;
913 }
914
915 /// Moves a Symbol, String, or Object rvalue into a new JS value.
916 template <typename T>
917 /* implicit */ Value(T&& other) : Value(kindOf(other)) {
918 static_assert(
919 std::is_base_of<Symbol, T>::value ||
920 std::is_base_of<String, T>::value ||
921 std::is_base_of<Object, T>::value,
922 "Value cannot be implicitly move-constructed from this type");
923 new (&data_.pointer) T(std::move(other));
924 }
925
926 /// Value("foo") will treat foo as a bool. This makes doing that a
927 /// compile error.
928 template <typename T = void>
929 Value(const char*) {
930 static_assert(
931 !std::is_same<void, T>::value,
932 "Value cannot be constructed directly from const char*");
933 }
934
935 Value(Value&& value);
936
937 /// Copies a Symbol lvalue into a new JS value.
938 Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) {
939 new (&data_.pointer) String(runtime.cloneSymbol(sym.ptr_));
940 }
941
942 /// Copies a String lvalue into a new JS value.
943 Value(Runtime& runtime, const String& str) : Value(StringKind) {
944 new (&data_.pointer) String(runtime.cloneString(str.ptr_));
945 }
946
947 /// Copies a Object lvalue into a new JS value.
948 Value(Runtime& runtime, const Object& obj) : Value(ObjectKind) {
949 new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_));
950 }
951
952 /// Creates a JS value from another Value lvalue.
953 Value(Runtime& runtime, const Value& value);
954
955 /// Value(rt, "foo") will treat foo as a bool. This makes doing
956 /// that a compile error.
957 template <typename T = void>
958 Value(Runtime&, const char*) {
959 static_assert(
960 !std::is_same<T, void>::value,
961 "Value cannot be constructed directly from const char*");
962 }
963
964 ~Value();
965 // \return the undefined \c Value.
966 static Value undefined() {
967 return Value();
968 }
969
970 // \return the null \c Value.
971 static Value null() {
972 return Value(nullptr);
973 }
974
975 // \return a \c Value created from a utf8-encoded JSON string.
976 static Value
977 createFromJsonUtf8(Runtime& runtime, const uint8_t* json, size_t length);
978
979 /// \return according to the SameValue algorithm see more here:
980 // https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4
981 static bool strictEquals(Runtime& runtime, const Value& a, const Value& b);
982
983 Value& operator=(Value&& other) {
984 this->~Value();
985 new (this) Value(std::move(other));
986 return *this;
987 }
988
989 bool isUndefined() const {
990 return kind_ == UndefinedKind;
991 }
992
993 bool isNull() const {
994 return kind_ == NullKind;
995 }
996
997 bool isBool() const {
998 return kind_ == BooleanKind;
999 }
1000
1001 bool isNumber() const {
1002 return kind_ == NumberKind;
1003 }
1004
1005 bool isString() const {
1006 return kind_ == StringKind;
1007 }
1008
1009 bool isSymbol() const {
1010 return kind_ == SymbolKind;
1011 }
1012
1013 bool isObject() const {
1014 return kind_ == ObjectKind;
1015 }
1016
1017 /// \return the boolean value, or asserts if not a boolean.
1018 bool getBool() const {
1019 assert(isBool());
1020 return data_.boolean;
1021 }
1022
1023 /// \return the number value, or asserts if not a number.
1024 double getNumber() const {
1025 assert(isNumber());
1026 return data_.number;
1027 }
1028
1029 /// \return the number value, or throws JSIException if not a
1030 /// number.
1031 double asNumber() const;
1032
1033 /// \return the Symbol value, or asserts if not a symbol.
1034 Symbol getSymbol(Runtime& runtime) const& {
1035 assert(isSymbol());
1036 return Symbol(runtime.cloneSymbol(data_.pointer.ptr_));
1037 }
1038
1039 /// \return the Symbol value, or asserts if not a symbol.
1040 /// Can be used on rvalue references to avoid cloning more symbols.
1041 Symbol getSymbol(Runtime&) && {
1042 assert(isSymbol());
1043 auto ptr = data_.pointer.ptr_;
1044 data_.pointer.ptr_ = nullptr;
1045 return static_cast<Symbol>(ptr);
1046 }
1047
1048 /// \return the Symbol value, or throws JSIException if not a
1049 /// symbol
1050 Symbol asSymbol(Runtime& runtime) const&;
1051 Symbol asSymbol(Runtime& runtime) &&;
1052
1053 /// \return the String value, or asserts if not a string.
1054 String getString(Runtime& runtime) const& {
1055 assert(isString());
1056 return String(runtime.cloneString(data_.pointer.ptr_));
1057 }
1058
1059 /// \return the String value, or asserts if not a string.
1060 /// Can be used on rvalue references to avoid cloning more strings.
1061 String getString(Runtime&) && {
1062 assert(isString());
1063 auto ptr = data_.pointer.ptr_;
1064 data_.pointer.ptr_ = nullptr;
1065 return static_cast<String>(ptr);
1066 }
1067
1068 /// \return the String value, or throws JSIException if not a
1069 /// string.
1070 String asString(Runtime& runtime) const&;
1071 String asString(Runtime& runtime) &&;
1072
1073 /// \return the Object value, or asserts if not an object.
1074 Object getObject(Runtime& runtime) const& {
1075 assert(isObject());
1076 return Object(runtime.cloneObject(data_.pointer.ptr_));
1077 }
1078
1079 /// \return the Object value, or asserts if not an object.
1080 /// Can be used on rvalue references to avoid cloning more objects.
1081 Object getObject(Runtime&) && {
1082 assert(isObject());
1083 auto ptr = data_.pointer.ptr_;
1084 data_.pointer.ptr_ = nullptr;
1085 return static_cast<Object>(ptr);
1086 }
1087
1088 /// \return the Object value, or throws JSIException if not an
1089 /// object.
1090 Object asObject(Runtime& runtime) const&;
1091 Object asObject(Runtime& runtime) &&;
1092
1093 // \return a String like JS .toString() would do.
1094 String toString(Runtime& runtime) const;
1095
1096 private:
1097 friend class Runtime;
1098
1099 enum ValueKind {
1100 UndefinedKind,
1101 NullKind,
1102 BooleanKind,
1103 NumberKind,
1104 SymbolKind,
1105 StringKind,
1106 ObjectKind,
1107 PointerKind = SymbolKind,
1108 };
1109
1110 union Data {
1111 // Value's ctor and dtor will manage the lifecycle of the contained Data.
1112 Data() {
1113 static_assert(
1114 sizeof(Data) == sizeof(uint64_t),
1115 "Value data should fit in a 64-bit register");
1116 }
1117 ~Data() {}
1118
1119 // scalars
1120 bool boolean;
1121 double number;
1122 // pointers
1123 Pointer pointer; // Symbol, String, Object, Array, Function
1124 };
1125
1126 Value(ValueKind kind) : kind_(kind) {}
1127
1128 constexpr static ValueKind kindOf(const Symbol&) {
1129 return SymbolKind;
1130 }
1131 constexpr static ValueKind kindOf(const String&) {
1132 return StringKind;
1133 }
1134 constexpr static ValueKind kindOf(const Object&) {
1135 return ObjectKind;
1136 }
1137
1138 ValueKind kind_;
1139 Data data_;
1140
1141 // In the future: Value becomes NaN-boxed. See T40538354.
1142};
1143
1144/// Not movable and not copyable RAII marker advising the underlying
1145/// JavaScript VM to track resources allocated since creation until
1146/// destruction so that they can be recycled eagerly when the Scope
1147/// goes out of scope instead of floating in the air until the next
1148/// garbage collection or any other delayed release occurs.
1149///
1150/// This API should be treated only as advice, implementations can
1151/// choose to ignore the fact that Scopes are created or destroyed.
1152///
1153/// This class is an exception to the rule allowing destructors to be
1154/// called without proper synchronization (see Runtime documentation).
1155/// The whole point of this class is to enable all sorts of clean ups
1156/// when the destructor is called and this proper synchronization is
1157/// required at that time.
1158///
1159/// Instances of this class are intended to be created as automatic stack
1160/// variables in which case destructor calls don't require any additional
1161/// locking, provided that the lock (if any) is managed with RAII helpers.
1162class Scope {
1163 public:
1164 explicit Scope(Runtime& rt) : rt_(rt), prv_(rt.pushScope()) {}
1165 ~Scope() {
1166 rt_.popScope(prv_);
1167 };
1168
1169 Scope(const Scope&) = delete;
1170 Scope(Scope&&) = delete;
1171
1172 Scope& operator=(const Scope&) = delete;
1173 Scope& operator=(Scope&&) = delete;
1174
1175 template <typename F>
1176 static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) {
1177 Scope s(rt);
1178 return f();
1179 }
1180
1181 private:
1182 Runtime& rt_;
1183 Runtime::ScopeState* prv_;
1184};
1185
1186/// Base class for jsi exceptions
1187class JSIException : public std::exception {
1188 protected:
1189 JSIException(){};
1190 JSIException(std::string what) : what_(std::move(what)){};
1191
1192 public:
1193 virtual const char* what() const noexcept override {
1194 return what_.c_str();
1195 }
1196
1197 protected:
1198 std::string what_;
1199};
1200
1201/// This exception will be thrown by API functions on errors not related to
1202/// JavaScript execution.
1203class JSINativeException : public JSIException {
1204 public:
1205 JSINativeException(std::string what) : JSIException(std::move(what)) {}
1206};
1207
1208/// This exception will be thrown by API functions whenever a JS
1209/// operation causes an exception as described by the spec, or as
1210/// otherwise described.
1211class JSError : public JSIException {
1212 public:
1213 /// Creates a JSError referring to provided \c value
1214 JSError(Runtime& r, Value&& value);
1215
1216 /// Creates a JSError referring to new \c Error instance capturing current
1217 /// JavaScript stack. The error message property is set to given \c message.
1218 JSError(Runtime& rt, std::string message);
1219
1220 /// Creates a JSError referring to new \c Error instance capturing current
1221 /// JavaScript stack. The error message property is set to given \c message.
1222 JSError(Runtime& rt, const char* message)
1223 : JSError(rt, std::string(message)){};
1224
1225 /// Creates a JSError referring to a JavaScript Object having message and
1226 /// stack properties set to provided values.
1227 JSError(Runtime& rt, std::string message, std::string stack);
1228
1229 /// Creates a JSError referring to provided value and what string
1230 /// set to provided message. This argument order is a bit weird,
1231 /// but necessary to avoid ambiguity with the above.
1232 JSError(std::string what, Runtime& rt, Value&& value);
1233
1234 const std::string& getStack() const {
1235 return stack_;
1236 }
1237
1238 const std::string& getMessage() const {
1239 return message_;
1240 }
1241
1242 const jsi::Value& value() const {
1243 assert(value_);
1244 return *value_;
1245 }
1246
1247 private:
1248 // This initializes the value_ member and does some other
1249 // validation, so it must be called by every branch through the
1250 // constructors.
1251 void setValue(Runtime& rt, Value&& value);
1252
1253 // This needs to be on the heap, because throw requires the object
1254 // be copyable, and Value is not.
1255 std::shared_ptr<jsi::Value> value_;
1256 std::string message_;
1257 std::string stack_;
1258};
1259
1260} // namespace jsi
1261} // namespace facebook
1262
1263#include <jsi/jsi-inl.h>