123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473 |
- /*
- * 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.
- */
- #include "JSCRuntime.h"
- #include <JavaScriptCore/JavaScript.h>
- #include <jsi/jsilib.h>
- #include <array>
- #include <atomic>
- #include <condition_variable>
- #include <cstdlib>
- #include <mutex>
- #include <queue>
- #include <sstream>
- #include <thread>
- namespace facebook {
- namespace jsc {
- namespace detail {
- class ArgsConverter;
- } // namespace detail
- class JSCRuntime;
- struct Lock {
- void lock(const jsc::JSCRuntime &) const {}
- void unlock(const jsc::JSCRuntime &) const {}
- };
- class JSCRuntime : public jsi::Runtime {
- public:
- // Creates new context in new context group
- JSCRuntime();
- // Retains ctx
- JSCRuntime(JSGlobalContextRef ctx);
- ~JSCRuntime();
- std::shared_ptr<const jsi::PreparedJavaScript> prepareJavaScript(
- const std::shared_ptr<const jsi::Buffer> &buffer,
- std::string sourceURL) override;
- jsi::Value evaluatePreparedJavaScript(
- const std::shared_ptr<const jsi::PreparedJavaScript> &js) override;
- jsi::Value evaluateJavaScript(
- const std::shared_ptr<const jsi::Buffer> &buffer,
- const std::string &sourceURL) override;
- jsi::Object global() override;
- std::string description() override;
- bool isInspectable() override;
- void setDescription(const std::string &desc);
- // Please don't use the following two functions, only exposed for
- // integration efforts.
- JSGlobalContextRef getContext() {
- return ctx_;
- }
- // JSValueRef->JSValue (needs make.*Value so it must be member function)
- jsi::Value createValue(JSValueRef value) const;
- // Value->JSValueRef (similar to above)
- JSValueRef valueRef(const jsi::Value &value);
- protected:
- friend class detail::ArgsConverter;
- class JSCSymbolValue final : public PointerValue {
- #ifndef NDEBUG
- JSCSymbolValue(
- JSGlobalContextRef ctx,
- const std::atomic<bool> &ctxInvalid,
- JSValueRef sym,
- std::atomic<intptr_t> &counter);
- #else
- JSCSymbolValue(
- JSGlobalContextRef ctx,
- const std::atomic<bool> &ctxInvalid,
- JSValueRef sym);
- #endif
- void invalidate() override;
- JSGlobalContextRef ctx_;
- const std::atomic<bool> &ctxInvalid_;
- // There is no C type in the JSC API to represent Symbol, so this stored
- // a JSValueRef which contains the Symbol.
- JSValueRef sym_;
- #ifndef NDEBUG
- std::atomic<intptr_t> &counter_;
- #endif
- protected:
- friend class JSCRuntime;
- };
- class JSCStringValue final : public PointerValue {
- #ifndef NDEBUG
- JSCStringValue(JSStringRef str, std::atomic<intptr_t> &counter);
- #else
- JSCStringValue(JSStringRef str);
- #endif
- void invalidate() override;
- JSStringRef str_;
- #ifndef NDEBUG
- std::atomic<intptr_t> &counter_;
- #endif
- protected:
- friend class JSCRuntime;
- };
- class JSCObjectValue final : public PointerValue {
- JSCObjectValue(
- JSGlobalContextRef ctx,
- const std::atomic<bool> &ctxInvalid,
- JSObjectRef obj
- #ifndef NDEBUG
- ,
- std::atomic<intptr_t> &counter
- #endif
- );
- void invalidate() override;
- JSGlobalContextRef ctx_;
- const std::atomic<bool> &ctxInvalid_;
- JSObjectRef obj_;
- #ifndef NDEBUG
- std::atomic<intptr_t> &counter_;
- #endif
- protected:
- friend class JSCRuntime;
- };
- PointerValue *cloneSymbol(const Runtime::PointerValue *pv) override;
- PointerValue *cloneString(const Runtime::PointerValue *pv) override;
- PointerValue *cloneObject(const Runtime::PointerValue *pv) override;
- PointerValue *clonePropNameID(const Runtime::PointerValue *pv) override;
- jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length)
- override;
- jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length)
- override;
- jsi::PropNameID createPropNameIDFromString(const jsi::String &str) override;
- std::string utf8(const jsi::PropNameID &) override;
- bool compare(const jsi::PropNameID &, const jsi::PropNameID &) override;
- std::string symbolToString(const jsi::Symbol &) override;
- jsi::String createStringFromAscii(const char *str, size_t length) override;
- jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override;
- std::string utf8(const jsi::String &) override;
- jsi::Object createObject() override;
- jsi::Object createObject(std::shared_ptr<jsi::HostObject> ho) override;
- virtual std::shared_ptr<jsi::HostObject> getHostObject(
- const jsi::Object &) override;
- jsi::HostFunctionType &getHostFunction(const jsi::Function &) override;
- jsi::Value getProperty(const jsi::Object &, const jsi::String &name) override;
- jsi::Value getProperty(const jsi::Object &, const jsi::PropNameID &name)
- override;
- bool hasProperty(const jsi::Object &, const jsi::String &name) override;
- bool hasProperty(const jsi::Object &, const jsi::PropNameID &name) override;
- void setPropertyValue(
- jsi::Object &,
- const jsi::String &name,
- const jsi::Value &value) override;
- void setPropertyValue(
- jsi::Object &,
- const jsi::PropNameID &name,
- const jsi::Value &value) override;
- bool isArray(const jsi::Object &) const override;
- bool isArrayBuffer(const jsi::Object &) const override;
- bool isFunction(const jsi::Object &) const override;
- bool isHostObject(const jsi::Object &) const override;
- bool isHostFunction(const jsi::Function &) const override;
- jsi::Array getPropertyNames(const jsi::Object &) override;
- // TODO: revisit this implementation
- jsi::WeakObject createWeakObject(const jsi::Object &) override;
- jsi::Value lockWeakObject(const jsi::WeakObject &) override;
- jsi::Array createArray(size_t length) override;
- size_t size(const jsi::Array &) override;
- size_t size(const jsi::ArrayBuffer &) override;
- uint8_t *data(const jsi::ArrayBuffer &) override;
- jsi::Value getValueAtIndex(const jsi::Array &, size_t i) override;
- void setValueAtIndexImpl(jsi::Array &, size_t i, const jsi::Value &value)
- override;
- jsi::Function createFunctionFromHostFunction(
- const jsi::PropNameID &name,
- unsigned int paramCount,
- jsi::HostFunctionType func) override;
- jsi::Value call(
- const jsi::Function &,
- const jsi::Value &jsThis,
- const jsi::Value *args,
- size_t count) override;
- jsi::Value callAsConstructor(
- const jsi::Function &,
- const jsi::Value *args,
- size_t count) override;
- bool strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) const override;
- bool strictEquals(const jsi::String &a, const jsi::String &b) const override;
- bool strictEquals(const jsi::Object &a, const jsi::Object &b) const override;
- bool instanceOf(const jsi::Object &o, const jsi::Function &f) override;
- private:
- // Basically convenience casts
- static JSValueRef symbolRef(const jsi::Symbol &str);
- static JSStringRef stringRef(const jsi::String &str);
- static JSStringRef stringRef(const jsi::PropNameID &sym);
- static JSObjectRef objectRef(const jsi::Object &obj);
- #ifdef RN_FABRIC_ENABLED
- static JSObjectRef objectRef(const jsi::WeakObject &obj);
- #endif
- // Factory methods for creating String/Object
- jsi::Symbol createSymbol(JSValueRef symbolRef) const;
- jsi::String createString(JSStringRef stringRef) const;
- jsi::PropNameID createPropNameID(JSStringRef stringRef);
- jsi::Object createObject(JSObjectRef objectRef) const;
- // Used by factory methods and clone methods
- jsi::Runtime::PointerValue *makeSymbolValue(JSValueRef sym) const;
- jsi::Runtime::PointerValue *makeStringValue(JSStringRef str) const;
- jsi::Runtime::PointerValue *makeObjectValue(JSObjectRef obj) const;
- void checkException(JSValueRef exc);
- void checkException(JSValueRef res, JSValueRef exc);
- void checkException(JSValueRef exc, const char *msg);
- void checkException(JSValueRef res, JSValueRef exc, const char *msg);
- JSGlobalContextRef ctx_;
- std::atomic<bool> ctxInvalid_;
- std::string desc_;
- #ifndef NDEBUG
- mutable std::atomic<intptr_t> objectCounter_;
- mutable std::atomic<intptr_t> symbolCounter_;
- mutable std::atomic<intptr_t> stringCounter_;
- #endif
- };
- #ifndef __has_builtin
- #define __has_builtin(x) 0
- #endif
- #if __has_builtin(__builtin_expect) || defined(__GNUC__)
- #define JSC_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true)
- #define JSC_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false)
- #else
- #define JSC_LIKELY(EXPR) (EXPR)
- #define JSC_UNLIKELY(EXPR) (EXPR)
- #endif
- #define JSC_ASSERT(x) \
- do { \
- if (JSC_UNLIKELY(!!(x))) { \
- abort(); \
- } \
- } while (0)
- #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
- // This takes care of watch and tvos (due to backwards compatibility in
- // Availability.h
- #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0
- #define _JSC_FAST_IS_ARRAY
- #endif
- #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0
- #define _JSC_NO_ARRAY_BUFFERS
- #endif
- #endif
- #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
- #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11
- // Only one of these should be set for a build. If somehow that's not
- // true, this will be a compile-time error and it can be resolved when
- // we understand why.
- #define _JSC_FAST_IS_ARRAY
- #endif
- #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12
- #define _JSC_NO_ARRAY_BUFFERS
- #endif
- #endif
- // JSStringRef utilities
- namespace {
- std::string JSStringToSTLString(JSStringRef str) {
- // Small string optimization: Avoid one heap allocation for strings that fit
- // in stackBuffer.size() bytes of UTF-8 (including the null terminator).
- std::array<char, 20> stackBuffer;
- std::unique_ptr<char[]> heapBuffer;
- char *buffer;
- // NOTE: By definition, maxBytes >= 1 since the null terminator is included.
- size_t maxBytes = JSStringGetMaximumUTF8CStringSize(str);
- if (maxBytes <= stackBuffer.size()) {
- buffer = stackBuffer.data();
- } else {
- heapBuffer = std::make_unique<char[]>(maxBytes);
- buffer = heapBuffer.get();
- }
- size_t actualBytes = JSStringGetUTF8CString(str, buffer, maxBytes);
- if (!actualBytes) {
- // Happens if maxBytes == 0 (never the case here) or if str contains
- // invalid UTF-16 data, since JSStringGetUTF8CString attempts a strict
- // conversion.
- // When converting an invalid string, JSStringGetUTF8CString writes a null
- // terminator before returning. So we can reliably treat our buffer as a C
- // string and return the truncated data to our caller. This is slightly
- // slower than if we knew the length (like below) but better than crashing.
- // TODO(T62295565): Perform a non-strict, best effort conversion of the
- // full string instead, like we did before the JSI migration.
- return std::string(buffer);
- }
- return std::string(buffer, actualBytes - 1);
- }
- JSStringRef getLengthString() {
- static JSStringRef length = JSStringCreateWithUTF8CString("length");
- return length;
- }
- JSStringRef getNameString() {
- static JSStringRef name = JSStringCreateWithUTF8CString("name");
- return name;
- }
- JSStringRef getFunctionString() {
- static JSStringRef func = JSStringCreateWithUTF8CString("Function");
- return func;
- }
- #if !defined(_JSC_FAST_IS_ARRAY)
- JSStringRef getArrayString() {
- static JSStringRef array = JSStringCreateWithUTF8CString("Array");
- return array;
- }
- JSStringRef getIsArrayString() {
- static JSStringRef isArray = JSStringCreateWithUTF8CString("isArray");
- return isArray;
- }
- #endif
- } // namespace
- // std::string utility
- namespace {
- std::string to_string(void *value) {
- std::ostringstream ss;
- ss << value;
- return ss.str();
- }
- } // namespace
- JSCRuntime::JSCRuntime()
- : JSCRuntime(JSGlobalContextCreateInGroup(nullptr, nullptr)) {
- JSGlobalContextRelease(ctx_);
- }
- JSCRuntime::JSCRuntime(JSGlobalContextRef ctx)
- : ctx_(JSGlobalContextRetain(ctx)),
- ctxInvalid_(false)
- #ifndef NDEBUG
- ,
- objectCounter_(0),
- stringCounter_(0)
- #endif
- {
- }
- JSCRuntime::~JSCRuntime() {
- // On shutting down and cleaning up: when JSC is actually torn down,
- // it calls JSC::Heap::lastChanceToFinalize internally which
- // finalizes anything left over. But at this point,
- // JSValueUnprotect() can no longer be called. We use an
- // atomic<bool> to avoid unsafe unprotects happening after shutdown
- // has started.
- ctxInvalid_ = true;
- JSGlobalContextRelease(ctx_);
- #ifndef NDEBUG
- assert(
- objectCounter_ == 0 && "JSCRuntime destroyed with a dangling API object");
- assert(
- stringCounter_ == 0 && "JSCRuntime destroyed with a dangling API string");
- #endif
- }
- std::shared_ptr<const jsi::PreparedJavaScript> JSCRuntime::prepareJavaScript(
- const std::shared_ptr<const jsi::Buffer> &buffer,
- std::string sourceURL) {
- return std::make_shared<jsi::SourceJavaScriptPreparation>(
- buffer, std::move(sourceURL));
- }
- jsi::Value JSCRuntime::evaluatePreparedJavaScript(
- const std::shared_ptr<const jsi::PreparedJavaScript> &js) {
- assert(
- dynamic_cast<const jsi::SourceJavaScriptPreparation *>(js.get()) &&
- "preparedJavaScript must be a SourceJavaScriptPreparation");
- auto sourceJs =
- std::static_pointer_cast<const jsi::SourceJavaScriptPreparation>(js);
- return evaluateJavaScript(sourceJs, sourceJs->sourceURL());
- }
- jsi::Value JSCRuntime::evaluateJavaScript(
- const std::shared_ptr<const jsi::Buffer> &buffer,
- const std::string &sourceURL) {
- std::string tmp(
- reinterpret_cast<const char *>(buffer->data()), buffer->size());
- JSStringRef sourceRef = JSStringCreateWithUTF8CString(tmp.c_str());
- JSStringRef sourceURLRef = nullptr;
- if (!sourceURL.empty()) {
- sourceURLRef = JSStringCreateWithUTF8CString(sourceURL.c_str());
- }
- JSValueRef exc = nullptr;
- JSValueRef res =
- JSEvaluateScript(ctx_, sourceRef, nullptr, sourceURLRef, 0, &exc);
- JSStringRelease(sourceRef);
- if (sourceURLRef) {
- JSStringRelease(sourceURLRef);
- }
- checkException(res, exc);
- return createValue(res);
- }
- jsi::Object JSCRuntime::global() {
- return createObject(JSContextGetGlobalObject(ctx_));
- }
- std::string JSCRuntime::description() {
- if (desc_.empty()) {
- desc_ = std::string("<JSCRuntime@") + to_string(this) + ">";
- }
- return desc_;
- }
- bool JSCRuntime::isInspectable() {
- return false;
- }
- namespace {
- bool smellsLikeES6Symbol(JSGlobalContextRef ctx, JSValueRef ref) {
- // Since iOS 13, JSValueGetType will return kJSTypeSymbol
- // Before: Empirically, an es6 Symbol is not an object, but its type is
- // object. This makes no sense, but we'll run with it.
- // https://github.com/WebKit/webkit/blob/master/Source/JavaScriptCore/API/JSValueRef.cpp#L79-L82
- JSType type = JSValueGetType(ctx, ref);
- if (type == /* kJSTypeSymbol */ 6) {
- return true;
- }
- return (!JSValueIsObject(ctx, ref) && type == kJSTypeObject);
- }
- } // namespace
- JSCRuntime::JSCSymbolValue::JSCSymbolValue(
- JSGlobalContextRef ctx,
- const std::atomic<bool> &ctxInvalid,
- JSValueRef sym
- #ifndef NDEBUG
- ,
- std::atomic<intptr_t> &counter
- #endif
- )
- : ctx_(ctx),
- ctxInvalid_(ctxInvalid),
- sym_(sym)
- #ifndef NDEBUG
- ,
- counter_(counter)
- #endif
- {
- assert(smellsLikeES6Symbol(ctx_, sym_));
- JSValueProtect(ctx_, sym_);
- #ifndef NDEBUG
- counter_ += 1;
- #endif
- }
- void JSCRuntime::JSCSymbolValue::invalidate() {
- #ifndef NDEBUG
- counter_ -= 1;
- #endif
- if (!ctxInvalid_) {
- JSValueUnprotect(ctx_, sym_);
- }
- delete this;
- }
- #ifndef NDEBUG
- JSCRuntime::JSCStringValue::JSCStringValue(
- JSStringRef str,
- std::atomic<intptr_t> &counter)
- : str_(JSStringRetain(str)), counter_(counter) {
- // Since std::atomic returns a copy instead of a reference when calling
- // operator+= we must do this explicitly in the constructor
- counter_ += 1;
- }
- #else
- JSCRuntime::JSCStringValue::JSCStringValue(JSStringRef str)
- : str_(JSStringRetain(str)) {}
- #endif
- void JSCRuntime::JSCStringValue::invalidate() {
- // These JSC{String,Object}Value objects are implicitly owned by the
- // {String,Object} objects, thus when a String/Object is destructed
- // the JSC{String,Object}Value should be released.
- #ifndef NDEBUG
- counter_ -= 1;
- #endif
- JSStringRelease(str_);
- // Angery reaccs only
- delete this;
- }
- JSCRuntime::JSCObjectValue::JSCObjectValue(
- JSGlobalContextRef ctx,
- const std::atomic<bool> &ctxInvalid,
- JSObjectRef obj
- #ifndef NDEBUG
- ,
- std::atomic<intptr_t> &counter
- #endif
- )
- : ctx_(ctx),
- ctxInvalid_(ctxInvalid),
- obj_(obj)
- #ifndef NDEBUG
- ,
- counter_(counter)
- #endif
- {
- JSValueProtect(ctx_, obj_);
- #ifndef NDEBUG
- counter_ += 1;
- #endif
- }
- void JSCRuntime::JSCObjectValue::invalidate() {
- #ifndef NDEBUG
- counter_ -= 1;
- #endif
- // When shutting down the VM, if there is a HostObject which
- // contains or otherwise owns a jsi::Object, then the final GC will
- // finalize the HostObject, leading to a call to invalidate(). But
- // at that point, making calls to JSValueUnprotect will crash.
- // It is up to the application to make sure that any other calls to
- // invalidate() happen before VM destruction; see the comment on
- // jsi::Runtime.
- //
- // Another potential concern here is that in the non-shutdown case,
- // if a HostObject is GCd, JSValueUnprotect will be called from the
- // JSC finalizer. The documentation warns against this: "You must
- // not call any function that may cause a garbage collection or an
- // allocation of a garbage collected object from within a
- // JSObjectFinalizeCallback. This includes all functions that have a
- // JSContextRef parameter." However, an audit of the source code for
- // JSValueUnprotect in late 2018 shows that it cannot cause
- // allocation or a GC, and further, this code has not changed in
- // about two years. In the future, we may choose to reintroduce the
- // mechanism previously used here which uses a separate thread for
- // JSValueUnprotect, in order to conform to the documented API, but
- // use the "unsafe" synchronous version on iOS 11 and earlier.
- if (!ctxInvalid_) {
- JSValueUnprotect(ctx_, obj_);
- }
- delete this;
- }
- jsi::Runtime::PointerValue *JSCRuntime::cloneSymbol(
- const jsi::Runtime::PointerValue *pv) {
- if (!pv) {
- return nullptr;
- }
- const JSCSymbolValue *symbol = static_cast<const JSCSymbolValue *>(pv);
- return makeSymbolValue(symbol->sym_);
- }
- jsi::Runtime::PointerValue *JSCRuntime::cloneString(
- const jsi::Runtime::PointerValue *pv) {
- if (!pv) {
- return nullptr;
- }
- const JSCStringValue *string = static_cast<const JSCStringValue *>(pv);
- return makeStringValue(string->str_);
- }
- jsi::Runtime::PointerValue *JSCRuntime::cloneObject(
- const jsi::Runtime::PointerValue *pv) {
- if (!pv) {
- return nullptr;
- }
- const JSCObjectValue *object = static_cast<const JSCObjectValue *>(pv);
- assert(
- object->ctx_ == ctx_ &&
- "Don't try to clone an object backed by a different Runtime");
- return makeObjectValue(object->obj_);
- }
- jsi::Runtime::PointerValue *JSCRuntime::clonePropNameID(
- const jsi::Runtime::PointerValue *pv) {
- if (!pv) {
- return nullptr;
- }
- const JSCStringValue *string = static_cast<const JSCStringValue *>(pv);
- return makeStringValue(string->str_);
- }
- jsi::PropNameID JSCRuntime::createPropNameIDFromAscii(
- const char *str,
- size_t length) {
- // For system JSC this must is identical to a string
- std::string tmp(str, length);
- JSStringRef strRef = JSStringCreateWithUTF8CString(tmp.c_str());
- auto res = createPropNameID(strRef);
- JSStringRelease(strRef);
- return res;
- }
- jsi::PropNameID JSCRuntime::createPropNameIDFromUtf8(
- const uint8_t *utf8,
- size_t length) {
- std::string tmp(reinterpret_cast<const char *>(utf8), length);
- JSStringRef strRef = JSStringCreateWithUTF8CString(tmp.c_str());
- auto res = createPropNameID(strRef);
- JSStringRelease(strRef);
- return res;
- }
- jsi::PropNameID JSCRuntime::createPropNameIDFromString(const jsi::String &str) {
- return createPropNameID(stringRef(str));
- }
- std::string JSCRuntime::utf8(const jsi::PropNameID &sym) {
- return JSStringToSTLString(stringRef(sym));
- }
- bool JSCRuntime::compare(const jsi::PropNameID &a, const jsi::PropNameID &b) {
- return JSStringIsEqual(stringRef(a), stringRef(b));
- }
- std::string JSCRuntime::symbolToString(const jsi::Symbol &sym) {
- return jsi::Value(*this, sym).toString(*this).utf8(*this);
- }
- jsi::String JSCRuntime::createStringFromAscii(const char *str, size_t length) {
- // Yes we end up double casting for semantic reasons (UTF8 contains ASCII,
- // not the other way around)
- return this->createStringFromUtf8(
- reinterpret_cast<const uint8_t *>(str), length);
- }
- jsi::String JSCRuntime::createStringFromUtf8(
- const uint8_t *str,
- size_t length) {
- std::string tmp(reinterpret_cast<const char *>(str), length);
- JSStringRef stringRef = JSStringCreateWithUTF8CString(tmp.c_str());
- auto result = createString(stringRef);
- JSStringRelease(stringRef);
- return result;
- }
- std::string JSCRuntime::utf8(const jsi::String &str) {
- return JSStringToSTLString(stringRef(str));
- }
- jsi::Object JSCRuntime::createObject() {
- return createObject(static_cast<JSObjectRef>(nullptr));
- }
- // HostObject details
- namespace detail {
- struct HostObjectProxyBase {
- HostObjectProxyBase(
- JSCRuntime &rt,
- const std::shared_ptr<jsi::HostObject> &sho)
- : runtime(rt), hostObject(sho) {}
- JSCRuntime &runtime;
- std::shared_ptr<jsi::HostObject> hostObject;
- };
- } // namespace detail
- namespace {
- std::once_flag hostObjectClassOnceFlag;
- JSClassRef hostObjectClass{};
- } // namespace
- jsi::Object JSCRuntime::createObject(std::shared_ptr<jsi::HostObject> ho) {
- struct HostObjectProxy : public detail::HostObjectProxyBase {
- static JSValueRef getProperty(
- JSContextRef ctx,
- JSObjectRef object,
- JSStringRef propName,
- JSValueRef *exception) {
- auto proxy = static_cast<HostObjectProxy *>(JSObjectGetPrivate(object));
- auto &rt = proxy->runtime;
- jsi::PropNameID sym = rt.createPropNameID(propName);
- jsi::Value ret;
- try {
- ret = proxy->hostObject->get(rt, sym);
- } catch (const jsi::JSError &error) {
- *exception = rt.valueRef(error.value());
- return JSValueMakeUndefined(ctx);
- } catch (const std::exception &ex) {
- auto excValue =
- rt.global()
- .getPropertyAsFunction(rt, "Error")
- .call(
- rt,
- std::string("Exception in HostObject::get(propName:") +
- JSStringToSTLString(propName) + std::string("): ") +
- ex.what());
- *exception = rt.valueRef(excValue);
- return JSValueMakeUndefined(ctx);
- } catch (...) {
- auto excValue =
- rt.global()
- .getPropertyAsFunction(rt, "Error")
- .call(
- rt,
- std::string("Exception in HostObject::get(propName:") +
- JSStringToSTLString(propName) +
- std::string("): <unknown>"));
- *exception = rt.valueRef(excValue);
- return JSValueMakeUndefined(ctx);
- }
- return rt.valueRef(ret);
- }
- #define JSC_UNUSED(x) (void)(x);
- static bool setProperty(
- JSContextRef ctx,
- JSObjectRef object,
- JSStringRef propName,
- JSValueRef value,
- JSValueRef *exception) {
- JSC_UNUSED(ctx);
- auto proxy = static_cast<HostObjectProxy *>(JSObjectGetPrivate(object));
- auto &rt = proxy->runtime;
- jsi::PropNameID sym = rt.createPropNameID(propName);
- try {
- proxy->hostObject->set(rt, sym, rt.createValue(value));
- } catch (const jsi::JSError &error) {
- *exception = rt.valueRef(error.value());
- return false;
- } catch (const std::exception &ex) {
- auto excValue =
- rt.global()
- .getPropertyAsFunction(rt, "Error")
- .call(
- rt,
- std::string("Exception in HostObject::set(propName:") +
- JSStringToSTLString(propName) + std::string("): ") +
- ex.what());
- *exception = rt.valueRef(excValue);
- return false;
- } catch (...) {
- auto excValue =
- rt.global()
- .getPropertyAsFunction(rt, "Error")
- .call(
- rt,
- std::string("Exception in HostObject::set(propName:") +
- JSStringToSTLString(propName) +
- std::string("): <unknown>"));
- *exception = rt.valueRef(excValue);
- return false;
- }
- return true;
- }
- // JSC does not provide means to communicate errors from this callback,
- // so the error handling strategy is very brutal - we'll just crash
- // due to noexcept.
- static void getPropertyNames(
- JSContextRef ctx,
- JSObjectRef object,
- JSPropertyNameAccumulatorRef propertyNames) noexcept {
- JSC_UNUSED(ctx);
- auto proxy = static_cast<HostObjectProxy *>(JSObjectGetPrivate(object));
- auto &rt = proxy->runtime;
- auto names = proxy->hostObject->getPropertyNames(rt);
- for (auto &name : names) {
- JSPropertyNameAccumulatorAddName(propertyNames, stringRef(name));
- }
- }
- #undef JSC_UNUSED
- static void finalize(JSObjectRef obj) {
- auto hostObject = static_cast<HostObjectProxy *>(JSObjectGetPrivate(obj));
- JSObjectSetPrivate(obj, nullptr);
- delete hostObject;
- }
- using HostObjectProxyBase::HostObjectProxyBase;
- };
- std::call_once(hostObjectClassOnceFlag, []() {
- JSClassDefinition hostObjectClassDef = kJSClassDefinitionEmpty;
- hostObjectClassDef.version = 0;
- hostObjectClassDef.attributes = kJSClassAttributeNoAutomaticPrototype;
- hostObjectClassDef.finalize = HostObjectProxy::finalize;
- hostObjectClassDef.getProperty = HostObjectProxy::getProperty;
- hostObjectClassDef.setProperty = HostObjectProxy::setProperty;
- hostObjectClassDef.getPropertyNames = HostObjectProxy::getPropertyNames;
- hostObjectClass = JSClassCreate(&hostObjectClassDef);
- });
- JSObjectRef obj =
- JSObjectMake(ctx_, hostObjectClass, new HostObjectProxy(*this, ho));
- return createObject(obj);
- }
- std::shared_ptr<jsi::HostObject> JSCRuntime::getHostObject(
- const jsi::Object &obj) {
- // We are guaranteed at this point to have isHostObject(obj) == true
- // so the private data should be HostObjectMetadata
- JSObjectRef object = objectRef(obj);
- auto metadata =
- static_cast<detail::HostObjectProxyBase *>(JSObjectGetPrivate(object));
- assert(metadata);
- return metadata->hostObject;
- }
- jsi::Value JSCRuntime::getProperty(
- const jsi::Object &obj,
- const jsi::String &name) {
- JSObjectRef objRef = objectRef(obj);
- JSValueRef exc = nullptr;
- JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc);
- checkException(exc);
- return createValue(res);
- }
- jsi::Value JSCRuntime::getProperty(
- const jsi::Object &obj,
- const jsi::PropNameID &name) {
- JSObjectRef objRef = objectRef(obj);
- JSValueRef exc = nullptr;
- JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc);
- checkException(exc);
- return createValue(res);
- }
- bool JSCRuntime::hasProperty(const jsi::Object &obj, const jsi::String &name) {
- JSObjectRef objRef = objectRef(obj);
- return JSObjectHasProperty(ctx_, objRef, stringRef(name));
- }
- bool JSCRuntime::hasProperty(
- const jsi::Object &obj,
- const jsi::PropNameID &name) {
- JSObjectRef objRef = objectRef(obj);
- return JSObjectHasProperty(ctx_, objRef, stringRef(name));
- }
- void JSCRuntime::setPropertyValue(
- jsi::Object &object,
- const jsi::PropNameID &name,
- const jsi::Value &value) {
- JSValueRef exc = nullptr;
- JSObjectSetProperty(
- ctx_,
- objectRef(object),
- stringRef(name),
- valueRef(value),
- kJSPropertyAttributeNone,
- &exc);
- checkException(exc);
- }
- void JSCRuntime::setPropertyValue(
- jsi::Object &object,
- const jsi::String &name,
- const jsi::Value &value) {
- JSValueRef exc = nullptr;
- JSObjectSetProperty(
- ctx_,
- objectRef(object),
- stringRef(name),
- valueRef(value),
- kJSPropertyAttributeNone,
- &exc);
- checkException(exc);
- }
- bool JSCRuntime::isArray(const jsi::Object &obj) const {
- #if !defined(_JSC_FAST_IS_ARRAY)
- JSObjectRef global = JSContextGetGlobalObject(ctx_);
- JSStringRef arrayString = getArrayString();
- JSValueRef exc = nullptr;
- JSValueRef arrayCtorValue =
- JSObjectGetProperty(ctx_, global, arrayString, &exc);
- JSC_ASSERT(exc);
- JSObjectRef arrayCtor = JSValueToObject(ctx_, arrayCtorValue, &exc);
- JSC_ASSERT(exc);
- JSStringRef isArrayString = getIsArrayString();
- JSValueRef isArrayValue =
- JSObjectGetProperty(ctx_, arrayCtor, isArrayString, &exc);
- JSC_ASSERT(exc);
- JSObjectRef isArray = JSValueToObject(ctx_, isArrayValue, &exc);
- JSC_ASSERT(exc);
- JSValueRef arg = objectRef(obj);
- JSValueRef result =
- JSObjectCallAsFunction(ctx_, isArray, nullptr, 1, &arg, &exc);
- JSC_ASSERT(exc);
- return JSValueToBoolean(ctx_, result);
- #else
- return JSValueIsArray(ctx_, objectRef(obj));
- #endif
- }
- bool JSCRuntime::isArrayBuffer(const jsi::Object &obj) const {
- #if defined(_JSC_NO_ARRAY_BUFFERS)
- throw std::runtime_error("Unsupported");
- #else
- auto typedArrayType = JSValueGetTypedArrayType(ctx_, objectRef(obj), nullptr);
- return typedArrayType == kJSTypedArrayTypeArrayBuffer;
- #endif
- }
- uint8_t *JSCRuntime::data(const jsi::ArrayBuffer &obj) {
- #if defined(_JSC_NO_ARRAY_BUFFERS)
- throw std::runtime_error("Unsupported");
- #else
- return static_cast<uint8_t *>(
- JSObjectGetArrayBufferBytesPtr(ctx_, objectRef(obj), nullptr));
- #endif
- }
- size_t JSCRuntime::size(const jsi::ArrayBuffer &obj) {
- #if defined(_JSC_NO_ARRAY_BUFFERS)
- throw std::runtime_error("Unsupported");
- #else
- return JSObjectGetArrayBufferByteLength(ctx_, objectRef(obj), nullptr);
- #endif
- }
- bool JSCRuntime::isFunction(const jsi::Object &obj) const {
- return JSObjectIsFunction(ctx_, objectRef(obj));
- }
- bool JSCRuntime::isHostObject(const jsi::Object &obj) const {
- auto cls = hostObjectClass;
- return cls != nullptr && JSValueIsObjectOfClass(ctx_, objectRef(obj), cls);
- }
- // Very expensive
- jsi::Array JSCRuntime::getPropertyNames(const jsi::Object &obj) {
- JSPropertyNameArrayRef names =
- JSObjectCopyPropertyNames(ctx_, objectRef(obj));
- size_t len = JSPropertyNameArrayGetCount(names);
- // Would be better if we could create an array with explicit elements
- auto result = createArray(len);
- for (size_t i = 0; i < len; i++) {
- JSStringRef str = JSPropertyNameArrayGetNameAtIndex(names, i);
- result.setValueAtIndex(*this, i, createString(str));
- }
- JSPropertyNameArrayRelease(names);
- return result;
- }
- jsi::WeakObject JSCRuntime::createWeakObject(const jsi::Object &obj) {
- #ifdef RN_FABRIC_ENABLED
- // TODO: revisit this implementation
- JSObjectRef objRef = objectRef(obj);
- return make<jsi::WeakObject>(makeObjectValue(objRef));
- #else
- throw std::logic_error("Not implemented");
- #endif
- }
- jsi::Value JSCRuntime::lockWeakObject(const jsi::WeakObject &obj) {
- #ifdef RN_FABRIC_ENABLED
- // TODO: revisit this implementation
- JSObjectRef objRef = objectRef(obj);
- return jsi::Value(createObject(objRef));
- #else
- throw std::logic_error("Not implemented");
- #endif
- }
- jsi::Array JSCRuntime::createArray(size_t length) {
- JSValueRef exc = nullptr;
- JSObjectRef obj = JSObjectMakeArray(ctx_, 0, nullptr, &exc);
- checkException(obj, exc);
- JSObjectSetProperty(
- ctx_,
- obj,
- getLengthString(),
- JSValueMakeNumber(ctx_, static_cast<double>(length)),
- 0,
- &exc);
- checkException(exc);
- return createObject(obj).getArray(*this);
- }
- size_t JSCRuntime::size(const jsi::Array &arr) {
- return static_cast<size_t>(
- getProperty(arr, createPropNameID(getLengthString())).getNumber());
- }
- jsi::Value JSCRuntime::getValueAtIndex(const jsi::Array &arr, size_t i) {
- JSValueRef exc = nullptr;
- auto res = JSObjectGetPropertyAtIndex(ctx_, objectRef(arr), (int)i, &exc);
- checkException(exc);
- return createValue(res);
- }
- void JSCRuntime::setValueAtIndexImpl(
- jsi::Array &arr,
- size_t i,
- const jsi::Value &value) {
- JSValueRef exc = nullptr;
- JSObjectSetPropertyAtIndex(
- ctx_, objectRef(arr), (int)i, valueRef(value), &exc);
- checkException(exc);
- }
- namespace {
- std::once_flag hostFunctionClassOnceFlag;
- JSClassRef hostFunctionClass{};
- class HostFunctionProxy {
- public:
- HostFunctionProxy(jsi::HostFunctionType hostFunction)
- : hostFunction_(hostFunction) {}
- jsi::HostFunctionType &getHostFunction() {
- return hostFunction_;
- }
- protected:
- jsi::HostFunctionType hostFunction_;
- };
- } // namespace
- jsi::Function JSCRuntime::createFunctionFromHostFunction(
- const jsi::PropNameID &name,
- unsigned int paramCount,
- jsi::HostFunctionType func) {
- class HostFunctionMetadata : public HostFunctionProxy {
- public:
- static void initialize(JSContextRef ctx, JSObjectRef object) {
- // We need to set up the prototype chain properly here. In theory we
- // could set func.prototype.prototype = Function.prototype to get the
- // same result. Not sure which approach is better.
- HostFunctionMetadata *metadata =
- static_cast<HostFunctionMetadata *>(JSObjectGetPrivate(object));
- JSValueRef exc = nullptr;
- JSObjectSetProperty(
- ctx,
- object,
- getLengthString(),
- JSValueMakeNumber(ctx, metadata->argCount),
- kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum |
- kJSPropertyAttributeDontDelete,
- &exc);
- if (exc) {
- // Silently fail to set length
- exc = nullptr;
- }
- JSStringRef name = nullptr;
- std::swap(metadata->name, name);
- JSObjectSetProperty(
- ctx,
- object,
- getNameString(),
- JSValueMakeString(ctx, name),
- kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum |
- kJSPropertyAttributeDontDelete,
- &exc);
- JSStringRelease(name);
- if (exc) {
- // Silently fail to set name
- exc = nullptr;
- }
- JSObjectRef global = JSContextGetGlobalObject(ctx);
- JSValueRef value =
- JSObjectGetProperty(ctx, global, getFunctionString(), &exc);
- // If we don't have Function then something bad is going on.
- if (JSC_UNLIKELY(exc)) {
- abort();
- }
- JSObjectRef funcCtor = JSValueToObject(ctx, value, &exc);
- if (!funcCtor) {
- // We can't do anything if Function is not an object
- return;
- }
- JSValueRef funcProto = JSObjectGetPrototype(ctx, funcCtor);
- JSObjectSetPrototype(ctx, object, funcProto);
- }
- static JSValueRef makeError(JSCRuntime &rt, const std::string &desc) {
- jsi::Value value =
- rt.global().getPropertyAsFunction(rt, "Error").call(rt, desc);
- return rt.valueRef(value);
- }
- static JSValueRef call(
- JSContextRef ctx,
- JSObjectRef function,
- JSObjectRef thisObject,
- size_t argumentCount,
- const JSValueRef arguments[],
- JSValueRef *exception) {
- HostFunctionMetadata *metadata =
- static_cast<HostFunctionMetadata *>(JSObjectGetPrivate(function));
- JSCRuntime &rt = *(metadata->runtime);
- const unsigned maxStackArgCount = 8;
- jsi::Value stackArgs[maxStackArgCount];
- std::unique_ptr<jsi::Value[]> heapArgs;
- jsi::Value *args;
- if (argumentCount > maxStackArgCount) {
- heapArgs = std::make_unique<jsi::Value[]>(argumentCount);
- for (size_t i = 0; i < argumentCount; i++) {
- heapArgs[i] = rt.createValue(arguments[i]);
- }
- args = heapArgs.get();
- } else {
- for (size_t i = 0; i < argumentCount; i++) {
- stackArgs[i] = rt.createValue(arguments[i]);
- }
- args = stackArgs;
- }
- JSValueRef res;
- jsi::Value thisVal(rt.createObject(thisObject));
- try {
- res = rt.valueRef(
- metadata->hostFunction_(rt, thisVal, args, argumentCount));
- } catch (const jsi::JSError &error) {
- *exception = rt.valueRef(error.value());
- res = JSValueMakeUndefined(ctx);
- } catch (const std::exception &ex) {
- std::string exceptionString("Exception in HostFunction: ");
- exceptionString += ex.what();
- *exception = makeError(rt, exceptionString);
- res = JSValueMakeUndefined(ctx);
- } catch (...) {
- std::string exceptionString("Exception in HostFunction: <unknown>");
- *exception = makeError(rt, exceptionString);
- res = JSValueMakeUndefined(ctx);
- }
- return res;
- }
- static void finalize(JSObjectRef object) {
- HostFunctionMetadata *metadata =
- static_cast<HostFunctionMetadata *>(JSObjectGetPrivate(object));
- JSObjectSetPrivate(object, nullptr);
- delete metadata;
- }
- HostFunctionMetadata(
- JSCRuntime *rt,
- jsi::HostFunctionType hf,
- unsigned ac,
- JSStringRef n)
- : HostFunctionProxy(hf),
- runtime(rt),
- argCount(ac),
- name(JSStringRetain(n)) {}
- JSCRuntime *runtime;
- unsigned argCount;
- JSStringRef name;
- };
- std::call_once(hostFunctionClassOnceFlag, []() {
- JSClassDefinition functionClass = kJSClassDefinitionEmpty;
- functionClass.version = 0;
- functionClass.attributes = kJSClassAttributeNoAutomaticPrototype;
- functionClass.initialize = HostFunctionMetadata::initialize;
- functionClass.finalize = HostFunctionMetadata::finalize;
- functionClass.callAsFunction = HostFunctionMetadata::call;
- hostFunctionClass = JSClassCreate(&functionClass);
- });
- JSObjectRef funcRef = JSObjectMake(
- ctx_,
- hostFunctionClass,
- new HostFunctionMetadata(this, func, paramCount, stringRef(name)));
- return createObject(funcRef).getFunction(*this);
- }
- namespace detail {
- class ArgsConverter {
- public:
- ArgsConverter(JSCRuntime &rt, const jsi::Value *args, size_t count) {
- JSValueRef *destination = inline_;
- if (count > maxStackArgs) {
- outOfLine_ = std::make_unique<JSValueRef[]>(count);
- destination = outOfLine_.get();
- }
- for (size_t i = 0; i < count; ++i) {
- destination[i] = rt.valueRef(args[i]);
- }
- }
- operator JSValueRef *() {
- return outOfLine_ ? outOfLine_.get() : inline_;
- }
- private:
- constexpr static unsigned maxStackArgs = 8;
- JSValueRef inline_[maxStackArgs];
- std::unique_ptr<JSValueRef[]> outOfLine_;
- };
- } // namespace detail
- bool JSCRuntime::isHostFunction(const jsi::Function &obj) const {
- auto cls = hostFunctionClass;
- return cls != nullptr && JSValueIsObjectOfClass(ctx_, objectRef(obj), cls);
- }
- jsi::HostFunctionType &JSCRuntime::getHostFunction(const jsi::Function &obj) {
- // We know that isHostFunction(obj) is true here, so its safe to proceed
- auto proxy =
- static_cast<HostFunctionProxy *>(JSObjectGetPrivate(objectRef(obj)));
- return proxy->getHostFunction();
- }
- jsi::Value JSCRuntime::call(
- const jsi::Function &f,
- const jsi::Value &jsThis,
- const jsi::Value *args,
- size_t count) {
- JSValueRef exc = nullptr;
- auto res = JSObjectCallAsFunction(
- ctx_,
- objectRef(f),
- jsThis.isUndefined() ? nullptr : objectRef(jsThis.getObject(*this)),
- count,
- detail::ArgsConverter(*this, args, count),
- &exc);
- checkException(exc);
- return createValue(res);
- }
- jsi::Value JSCRuntime::callAsConstructor(
- const jsi::Function &f,
- const jsi::Value *args,
- size_t count) {
- JSValueRef exc = nullptr;
- auto res = JSObjectCallAsConstructor(
- ctx_,
- objectRef(f),
- count,
- detail::ArgsConverter(*this, args, count),
- &exc);
- checkException(exc);
- return createValue(res);
- }
- bool JSCRuntime::strictEquals(const jsi::Symbol &a, const jsi::Symbol &b)
- const {
- JSValueRef exc = nullptr;
- bool ret = JSValueIsEqual(ctx_, symbolRef(a), symbolRef(b), &exc);
- const_cast<JSCRuntime *>(this)->checkException(exc);
- return ret;
- }
- bool JSCRuntime::strictEquals(const jsi::String &a, const jsi::String &b)
- const {
- return JSStringIsEqual(stringRef(a), stringRef(b));
- }
- bool JSCRuntime::strictEquals(const jsi::Object &a, const jsi::Object &b)
- const {
- return objectRef(a) == objectRef(b);
- }
- bool JSCRuntime::instanceOf(const jsi::Object &o, const jsi::Function &f) {
- JSValueRef exc = nullptr;
- bool res =
- JSValueIsInstanceOfConstructor(ctx_, objectRef(o), objectRef(f), &exc);
- checkException(exc);
- return res;
- }
- jsi::Runtime::PointerValue *JSCRuntime::makeSymbolValue(
- JSValueRef symbolRef) const {
- #ifndef NDEBUG
- return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef, symbolCounter_);
- #else
- return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef);
- #endif
- }
- namespace {
- JSStringRef getEmptyString() {
- static JSStringRef empty = JSStringCreateWithUTF8CString("");
- return empty;
- }
- } // namespace
- jsi::Runtime::PointerValue *JSCRuntime::makeStringValue(
- JSStringRef stringRef) const {
- if (!stringRef) {
- stringRef = getEmptyString();
- }
- #ifndef NDEBUG
- return new JSCStringValue(stringRef, stringCounter_);
- #else
- return new JSCStringValue(stringRef);
- #endif
- }
- jsi::Symbol JSCRuntime::createSymbol(JSValueRef sym) const {
- return make<jsi::Symbol>(makeSymbolValue(sym));
- }
- jsi::String JSCRuntime::createString(JSStringRef str) const {
- return make<jsi::String>(makeStringValue(str));
- }
- jsi::PropNameID JSCRuntime::createPropNameID(JSStringRef str) {
- return make<jsi::PropNameID>(makeStringValue(str));
- }
- jsi::Runtime::PointerValue *JSCRuntime::makeObjectValue(
- JSObjectRef objectRef) const {
- if (!objectRef) {
- objectRef = JSObjectMake(ctx_, nullptr, nullptr);
- }
- #ifndef NDEBUG
- return new JSCObjectValue(ctx_, ctxInvalid_, objectRef, objectCounter_);
- #else
- return new JSCObjectValue(ctx_, ctxInvalid_, objectRef);
- #endif
- }
- jsi::Object JSCRuntime::createObject(JSObjectRef obj) const {
- return make<jsi::Object>(makeObjectValue(obj));
- }
- jsi::Value JSCRuntime::createValue(JSValueRef value) const {
- JSType type = JSValueGetType(ctx_, value);
- switch (type) {
- case kJSTypeNumber:
- return jsi::Value(JSValueToNumber(ctx_, value, nullptr));
- case kJSTypeBoolean:
- return jsi::Value(JSValueToBoolean(ctx_, value));
- case kJSTypeNull:
- return jsi::Value(nullptr);
- case kJSTypeUndefined:
- return jsi::Value();
- case kJSTypeString: {
- JSStringRef str = JSValueToStringCopy(ctx_, value, nullptr);
- auto result = jsi::Value(createString(str));
- JSStringRelease(str);
- return result;
- }
- case kJSTypeObject: {
- JSObjectRef objRef = JSValueToObject(ctx_, value, nullptr);
- return jsi::Value(createObject(objRef));
- }
- // TODO: Uncomment this when all supported JSC versions have this symbol
- // case kJSTypeSymbol:
- default: {
- if (smellsLikeES6Symbol(ctx_, value)) {
- return jsi::Value(createSymbol(value));
- } else {
- // WHAT ARE YOU
- abort();
- }
- }
- }
- }
- JSValueRef JSCRuntime::valueRef(const jsi::Value &value) {
- // I would rather switch on value.kind_
- if (value.isUndefined()) {
- return JSValueMakeUndefined(ctx_);
- } else if (value.isNull()) {
- return JSValueMakeNull(ctx_);
- } else if (value.isBool()) {
- return JSValueMakeBoolean(ctx_, value.getBool());
- } else if (value.isNumber()) {
- return JSValueMakeNumber(ctx_, value.getNumber());
- } else if (value.isSymbol()) {
- return symbolRef(value.getSymbol(*this));
- } else if (value.isString()) {
- return JSValueMakeString(ctx_, stringRef(value.getString(*this)));
- } else if (value.isObject()) {
- return objectRef(value.getObject(*this));
- } else {
- // What are you?
- abort();
- }
- }
- JSValueRef JSCRuntime::symbolRef(const jsi::Symbol &sym) {
- return static_cast<const JSCSymbolValue *>(getPointerValue(sym))->sym_;
- }
- JSStringRef JSCRuntime::stringRef(const jsi::String &str) {
- return static_cast<const JSCStringValue *>(getPointerValue(str))->str_;
- }
- JSStringRef JSCRuntime::stringRef(const jsi::PropNameID &sym) {
- return static_cast<const JSCStringValue *>(getPointerValue(sym))->str_;
- }
- JSObjectRef JSCRuntime::objectRef(const jsi::Object &obj) {
- return static_cast<const JSCObjectValue *>(getPointerValue(obj))->obj_;
- }
- #ifdef RN_FABRIC_ENABLED
- JSObjectRef JSCRuntime::objectRef(const jsi::WeakObject &obj) {
- // TODO: revisit this implementation
- return static_cast<const JSCObjectValue *>(getPointerValue(obj))->obj_;
- }
- #endif
- void JSCRuntime::checkException(JSValueRef exc) {
- if (JSC_UNLIKELY(exc)) {
- throw jsi::JSError(*this, createValue(exc));
- }
- }
- void JSCRuntime::checkException(JSValueRef res, JSValueRef exc) {
- if (JSC_UNLIKELY(!res)) {
- throw jsi::JSError(*this, createValue(exc));
- }
- }
- void JSCRuntime::checkException(JSValueRef exc, const char *msg) {
- if (JSC_UNLIKELY(exc)) {
- throw jsi::JSError(std::string(msg), *this, createValue(exc));
- }
- }
- void JSCRuntime::checkException(
- JSValueRef res,
- JSValueRef exc,
- const char *msg) {
- if (JSC_UNLIKELY(!res)) {
- throw jsi::JSError(std::string(msg), *this, createValue(exc));
- }
- }
- std::unique_ptr<jsi::Runtime> makeJSCRuntime() {
- return std::make_unique<JSCRuntime>();
- }
- } // namespace jsc
- } // namespace facebook
|