diff --git a/Apps/Playground/node_modules/@babylonjs/react-native/JSCRuntime.cpp b/Apps/Playground/node_modules/@babylonjs/react-native/JSCRuntime.cpp new file mode 100644 index 000000000..75da25cc0 --- /dev/null +++ b/Apps/Playground/node_modules/@babylonjs/react-native/JSCRuntime.cpp @@ -0,0 +1,1504 @@ +// NOTE: This file was copied from the JSCRuntime.cpp included in React Native 0.63.1 with modifications in order to: +// 1. Access the underlying JSC JSGlobalContextRef. +// 2. Create a custom JSCRuntime with bug fixes. + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 prepareJavaScript( + const std::shared_ptr &buffer, + std::string sourceURL) override; + + jsi::Value evaluatePreparedJavaScript( + const std::shared_ptr &js) override; + + jsi::Value evaluateJavaScript( + const std::shared_ptr &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 &ctxInvalid, + JSValueRef sym, + std::atomic &counter); +#else + JSCSymbolValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSValueRef sym); +#endif + void invalidate() override; + + JSGlobalContextRef ctx_; + const std::atomic &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 &counter_; +#endif + protected: + friend class JSCRuntime; + }; + + class JSCStringValue final : public PointerValue { +#ifndef NDEBUG + JSCStringValue(JSStringRef str, std::atomic &counter); +#else + JSCStringValue(JSStringRef str); +#endif + void invalidate() override; + + JSStringRef str_; +#ifndef NDEBUG + std::atomic &counter_; +#endif + protected: + friend class JSCRuntime; + }; + + class JSCObjectValue final : public PointerValue { + JSCObjectValue( + JSGlobalContextRef ctx, + const std::atomic &ctxInvalid, + JSObjectRef obj +#ifndef NDEBUG + , + std::atomic &counter +#endif + ); + + void invalidate() override; + + JSGlobalContextRef ctx_; + const std::atomic &ctxInvalid_; + JSObjectRef obj_; +#ifndef NDEBUG + std::atomic &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 ho) override; + virtual std::shared_ptr 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 ctxInvalid_; + std::string desc_; +#ifndef NDEBUG + mutable std::atomic objectCounter_; + mutable std::atomic symbolCounter_; + mutable std::atomic 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 stackBuffer; + std::unique_ptr 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(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 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 JSCRuntime::prepareJavaScript( + const std::shared_ptr &buffer, + std::string sourceURL) { + return std::make_shared( + buffer, std::move(sourceURL)); +} + +jsi::Value JSCRuntime::evaluatePreparedJavaScript( + const std::shared_ptr &js) { + assert( + dynamic_cast(js.get()) && + "preparedJavaScript must be a SourceJavaScriptPreparation"); + auto sourceJs = + std::static_pointer_cast(js); + return evaluateJavaScript(sourceJs, sourceJs->sourceURL()); +} + +jsi::Value JSCRuntime::evaluateJavaScript( + const std::shared_ptr &buffer, + const std::string &sourceURL) { + std::string tmp( + reinterpret_cast(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(""; + } + 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 &ctxInvalid, + JSValueRef sym +#ifndef NDEBUG + , + std::atomic &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 &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 &ctxInvalid, + JSObjectRef obj +#ifndef NDEBUG + , + std::atomic &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(pv); + return makeSymbolValue(symbol->sym_); +} + +jsi::Runtime::PointerValue *JSCRuntime::cloneString( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCStringValue *string = static_cast(pv); + return makeStringValue(string->str_); +} + +jsi::Runtime::PointerValue *JSCRuntime::cloneObject( + const jsi::Runtime::PointerValue *pv) { + if (!pv) { + return nullptr; + } + const JSCObjectValue *object = static_cast(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(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(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(str), length); +} + +jsi::String JSCRuntime::createStringFromUtf8( + const uint8_t *str, + size_t length) { + + std::vector chars(length); + + { + const char *from = reinterpret_cast(str); + char16_t *to = reinterpret_cast(chars.data()); + + std::locale::global(std::locale("en_US.UTF-8")); + auto& facet = std::use_facet>(std::locale()); + std::mbstate_t state{}; + const char *from_next; + char16_t *to_next; + auto result = facet.in(state, from, from + length, from_next, to, to + length, to_next); + if (result != std::codecvt_base::ok) { + throw std::invalid_argument{"Invalid unicode string"}; + } + chars.resize(to_next - to); + } + + JSStringRef stringRef = JSStringCreateWithCharacters(chars.data(), chars.size()); + 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(nullptr)); +} + +// HostObject details +namespace detail { +struct HostObjectProxyBase { + HostObjectProxyBase( + JSCRuntime &rt, + const std::shared_ptr &sho) + : runtime(rt), hostObject(sho) {} + + JSCRuntime &runtime; + std::shared_ptr hostObject; +}; +} // namespace detail + +namespace { +std::once_flag hostObjectClassOnceFlag; +JSClassRef hostObjectClass{}; +} // namespace + +jsi::Object JSCRuntime::createObject(std::shared_ptr ho) { + struct HostObjectProxy : public detail::HostObjectProxyBase { + static JSValueRef getProperty( + JSContextRef ctx, + JSObjectRef object, + JSStringRef propName, + JSValueRef *exception) { + auto proxy = static_cast(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("): ")); + *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(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("): ")); + *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(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(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 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(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( + 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(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(length)), + 0, + &exc); + checkException(exc); + return createObject(obj).getArray(*this); +} + +size_t JSCRuntime::size(const jsi::Array &arr) { + return static_cast( + 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(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(JSObjectGetPrivate(function)); + JSCRuntime &rt = *(metadata->runtime); + const unsigned maxStackArgCount = 8; + jsi::Value stackArgs[maxStackArgCount]; + std::unique_ptr heapArgs; + jsi::Value *args; + if (argumentCount > maxStackArgCount) { + heapArgs = std::make_unique(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: "); + *exception = makeError(rt, exceptionString); + res = JSValueMakeUndefined(ctx); + } + return res; + } + + static void finalize(JSObjectRef object) { + HostFunctionMetadata *metadata = + static_cast(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(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 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(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(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(makeSymbolValue(sym)); +} + +jsi::String JSCRuntime::createString(JSStringRef str) const { + return make(makeStringValue(str)); +} + +jsi::PropNameID JSCRuntime::createPropNameID(JSStringRef str) { + return make(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(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(getPointerValue(sym))->sym_; +} + +JSStringRef JSCRuntime::stringRef(const jsi::String &str) { + return static_cast(getPointerValue(str))->str_; +} + +JSStringRef JSCRuntime::stringRef(const jsi::PropNameID &sym) { + return static_cast(getPointerValue(sym))->str_; +} + +JSObjectRef JSCRuntime::objectRef(const jsi::Object &obj) { + return static_cast(getPointerValue(obj))->obj_; +} + +#ifdef RN_FABRIC_ENABLED +JSObjectRef JSCRuntime::objectRef(const jsi::WeakObject &obj) { + // TODO: revisit this implementation + return static_cast(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 makeJSCRuntime() { + return std::make_unique(); +} + +std::unique_ptr makeJSCRuntime(JSGlobalContextRef ctx) { + return std::make_unique(ctx); +} + +JSGlobalContextRef getJSGlobalContextRefFromJSCRuntime(jsi::Runtime &runtime) { + return reinterpret_cast(runtime).getContext(); +} + +} // namespace jsc +} // namespace facebook diff --git a/Apps/Playground/node_modules/@babylonjs/react-native/JSCRuntime.h b/Apps/Playground/node_modules/@babylonjs/react-native/JSCRuntime.h index f2bf63c49..b72d21320 100644 --- a/Apps/Playground/node_modules/@babylonjs/react-native/JSCRuntime.h +++ b/Apps/Playground/node_modules/@babylonjs/react-native/JSCRuntime.h @@ -1,251 +1,26 @@ -// NOTE: This header was manually extracted from the JSCRuntime.cpp included in React Native 0.62.1 in order to access the underlying JSC JSGlobalContextRef until Babylon Native fully supports JSI. +// NOTE: This file was copied from the JSCRuntime.h included in React Native 0.63.1 with modifications in order to: +// 1. Access the underlying JSC JSGlobalContextRef. +// 2. Create a custom JSCRuntime with bug fixes. + +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ #pragma once #include #include - #include -#include -#include -#include -#include -#include -#include -#include -#include namespace facebook { namespace jsc { -std::unique_ptr makeJSCRuntime(); - -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 prepareJavaScript( - const std::shared_ptr &buffer, - std::string sourceURL) override; - - jsi::Value evaluatePreparedJavaScript( - const std::shared_ptr& js) override; - - jsi::Value evaluateJavaScript( - const std::shared_ptr &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& ctxInvalid, - JSValueRef sym, std::atomic& counter); -#else - JSCSymbolValue(JSGlobalContextRef ctx, - const std::atomic& ctxInvalid, - JSValueRef sym); -#endif - void invalidate() override; - - JSGlobalContextRef ctx_; - const std::atomic& 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& counter_; -#endif - protected: - friend class JSCRuntime; - }; - - class JSCStringValue final : public PointerValue { -#ifndef NDEBUG - JSCStringValue(JSStringRef str, std::atomic& counter); -#else - JSCStringValue(JSStringRef str); -#endif - void invalidate() override; - - JSStringRef str_; -#ifndef NDEBUG - std::atomic& counter_; -#endif - protected: - friend class JSCRuntime; - }; - - class JSCObjectValue final : public PointerValue { - JSCObjectValue( - JSGlobalContextRef ctx, - const std::atomic& ctxInvalid, - JSObjectRef obj -#ifndef NDEBUG - , - std::atomic& counter -#endif - ); - - void invalidate() override; - - JSGlobalContextRef ctx_; - const std::atomic& ctxInvalid_; - JSObjectRef obj_; -#ifndef NDEBUG - std::atomic& 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 ho) override; - virtual std::shared_ptr 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); +std::unique_ptr makeJSCRuntime(JSGlobalContextRef ctx); - JSGlobalContextRef ctx_; - std::atomic ctxInvalid_; - std::string desc_; -#ifndef NDEBUG - mutable std::atomic objectCounter_; - mutable std::atomic symbolCounter_; - mutable std::atomic stringCounter_; -#endif -}; +JSGlobalContextRef getJSGlobalContextRefFromJSCRuntime(jsi::Runtime& runtime); } // namespace jsc } // namespace facebook diff --git a/Apps/Playground/node_modules/@babylonjs/react-native/android/CMakeLists.txt b/Apps/Playground/node_modules/@babylonjs/react-native/android/CMakeLists.txt index 18999174f..96a255edd 100644 --- a/Apps/Playground/node_modules/@babylonjs/react-native/android/CMakeLists.txt +++ b/Apps/Playground/node_modules/@babylonjs/react-native/android/CMakeLists.txt @@ -24,12 +24,8 @@ set(BABYLON_NATIVE_PLATFORM "Android") set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") -# configure Babylon Native to use JSC (specifically the one that is part of the React Native application) -set(NAPI_JAVASCRIPT_ENGINE "JavaScriptCore" CACHE STRING "The JavaScript engine to power N-API") -add_library(javascript_engine SHARED IMPORTED GLOBAL) -set_target_properties(javascript_engine PROPERTIES IMPORTED_LOCATION "${ANDROID_JSC_LIBPATH}/${ANDROID_ABI}/libjsc.so") -target_include_directories(javascript_engine INTERFACE "${ANDROID_JSC_INCPATH}") - +# configure Babylon Native to use JSI +set(NAPI_JAVASCRIPT_ENGINE "JSI" CACHE STRING "The JavaScript engine to power N-API") set(REACTNATIVE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../react-native/") add_subdirectory(${REACTNATIVE_DIR}/ReactCommon/jsi/jsi ${CMAKE_CURRENT_BINARY_DIR}/jsi) target_include_directories(jsi INTERFACE ${REACTNATIVE_DIR}/ReactCommon/jsi) @@ -40,9 +36,10 @@ set(BABYLON_NATIVE_DIR "${CMAKE_CURRENT_LIST_DIR}/../submodules/BabylonNative") add_subdirectory(${BABYLON_NATIVE_DIR} ${BABYLON_NATIVE_DIR}/build/Android_${CMAKE_ANDROID_ARCH_ABI}/) add_library(BabylonNative SHARED - src/main/cpp/BabylonNativeInterop.cpp) + src/main/cpp/BabylonNativeInterop.cpp + ../JSCRuntime.cpp) -target_compile_definitions(BabylonNative PRIVATE NODE_ADDON_API_DISABLE_DEPRECATED) +target_include_directories(BabylonNative PRIVATE ${ANDROID_JSC_INCPATH}) target_link_libraries(BabylonNative GLESv3 @@ -58,4 +55,5 @@ target_link_libraries(BabylonNative NativeEngine NativeInput NativeXr - Window) \ No newline at end of file + Window + "${ANDROID_JSC_LIBPATH}/${ANDROID_ABI}/libjsc.so") diff --git a/Apps/Playground/node_modules/@babylonjs/react-native/android/src/main/cpp/BabylonNativeInterop.cpp b/Apps/Playground/node_modules/@babylonjs/react-native/android/src/main/cpp/BabylonNativeInterop.cpp index 9b3078862..37495bf31 100644 --- a/Apps/Playground/node_modules/@babylonjs/react-native/android/src/main/cpp/BabylonNativeInterop.cpp +++ b/Apps/Playground/node_modules/@babylonjs/react-native/android/src/main/cpp/BabylonNativeInterop.cpp @@ -1,5 +1,3 @@ -#include "../../../../JSCRuntime.h" - #include #include @@ -13,8 +11,6 @@ #include -#include - #include #include #include @@ -23,6 +19,11 @@ #include #include +#include +#include + +#include "../../../../JSCRuntime.h" + namespace Babylon { namespace @@ -38,7 +39,8 @@ namespace Babylon public: // This class must be constructed from the JavaScript thread Native(facebook::jsi::Runtime* jsiRuntime, ANativeWindow* windowPtr) - : m_env{ Napi::Attach(reinterpret_cast(jsiRuntime)->getContext()) } + : m_jsiRuntime{ facebook::jsc::makeJSCRuntime(facebook::jsc::getJSGlobalContextRefFromJSCRuntime(*jsiRuntime)) } + , m_env{ Napi::Attach(*m_jsiRuntime) } { auto looper_scheduler = std::make_shared(looper_scheduler_t::get_for_current_thread()); @@ -100,6 +102,10 @@ namespace Babylon private: using looper_scheduler_t = arcana::looper_scheduler) + sizeof(std::function)>; + + // custom jsi runtime based on custom jsc runtime + std::unique_ptr m_jsiRuntime; + Napi::Env m_env; JsRuntime* m_runtime; Plugins::NativeInput* m_nativeInput; @@ -163,4 +169,4 @@ extern "C" JNIEXPORT void JNICALL Java_com_reactlibrary_BabylonNativeInterop_des { auto native = reinterpret_cast(instanceRef); delete native; -} \ No newline at end of file +} diff --git a/Apps/Playground/node_modules/@babylonjs/react-native/ios/BabylonNative.cpp b/Apps/Playground/node_modules/@babylonjs/react-native/ios/BabylonNative.cpp index 518128b3f..c34905bb8 100644 --- a/Apps/Playground/node_modules/@babylonjs/react-native/ios/BabylonNative.cpp +++ b/Apps/Playground/node_modules/@babylonjs/react-native/ios/BabylonNative.cpp @@ -11,6 +11,7 @@ #include #include +#include #include @@ -26,10 +27,14 @@ namespace Babylon { public: Impl(facebook::jsi::Runtime* jsiRuntime) - : env{ Napi::Attach(reinterpret_cast(jsiRuntime)->getContext())} + : jsiRuntime{ facebook::jsc::makeJSCRuntime(facebook::jsc::getJSGlobalContextRefFromJSCRuntime(*jsiRuntime)) } + , env{ Napi::Attach(*jsiRuntime) } { } + // custom jsi runtime based on custom jsc runtime + std::unique_ptr jsiRuntime; + Napi::Env env; JsRuntime* runtime{}; Plugins::NativeInput* nativeInput{}; diff --git a/Apps/Playground/node_modules/@babylonjs/react-native/ios/CMakeLists.txt b/Apps/Playground/node_modules/@babylonjs/react-native/ios/CMakeLists.txt index 01e0f66f3..8b084663a 100644 --- a/Apps/Playground/node_modules/@babylonjs/react-native/ios/CMakeLists.txt +++ b/Apps/Playground/node_modules/@babylonjs/react-native/ios/CMakeLists.txt @@ -4,6 +4,9 @@ project(ReactNativeBabylon) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") +# configure Babylon Native to use JSI +set(NAPI_JAVASCRIPT_ENGINE "JSI" CACHE STRING "The JavaScript engine to power N-API") + set(REACTNATIVE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../react-native/") add_subdirectory(${REACTNATIVE_DIR}/ReactCommon/jsi/jsi ${CMAKE_CURRENT_BINARY_DIR}/jsi) target_include_directories(jsi INTERFACE ${REACTNATIVE_DIR}/ReactCommon/jsi) @@ -15,15 +18,14 @@ add_subdirectory(${BABYLON_NATIVE_DIR} ${BABYLON_NATIVE_DIR}/build/ios/) add_library(BabylonNative ${CMAKE_CURRENT_LIST_DIR}/BabylonNative.h - ${CMAKE_CURRENT_LIST_DIR}/BabylonNative.cpp) + ${CMAKE_CURRENT_LIST_DIR}/BabylonNative.cpp + ${CMAKE_CURRENT_LIST_DIR}/../JSCRuntime.cpp) find_library(JSCORE_LIBRARY JavaScriptCore) target_compile_definitions(BabylonNative PRIVATE UNICODE) target_compile_definitions(BabylonNative PRIVATE _UNICODE) -target_compile_definitions(BabylonNative PRIVATE NODE_ADDON_API_DISABLE_DEPRECATED) - target_include_directories(BabylonNative PUBLIC ${CMAKE_CURRENT_LIST_DIR}) target_link_libraries(BabylonNative diff --git a/Apps/Playground/node_modules/@babylonjs/react-native/submodules/BabylonNative b/Apps/Playground/node_modules/@babylonjs/react-native/submodules/BabylonNative index f1d37f1ea..7c1c51587 160000 --- a/Apps/Playground/node_modules/@babylonjs/react-native/submodules/BabylonNative +++ b/Apps/Playground/node_modules/@babylonjs/react-native/submodules/BabylonNative @@ -1 +1 @@ -Subproject commit f1d37f1ea32327227a2c5ca53b598eb738246a11 +Subproject commit 7c1c51587eac90ed5538626bde2dd2760bf39625