// Copyright © 2021-present 650 Industries, Inc. (aka Expo) #include "JavaScriptObject.h" #include "JavaScriptValue.h" #include "JavaScriptFunction.h" #include "JavaScriptRuntime.h" #include "JSITypeConverter.h" #include "ObjectDeallocator.h" #include "JavaReferencesCache.h" namespace expo { void JavaScriptObject::registerNatives() { registerHybrid({ makeNativeMethod("hasProperty", JavaScriptObject::jniHasProperty), makeNativeMethod("getProperty", JavaScriptObject::jniGetProperty), makeNativeMethod("getPropertyNames", JavaScriptObject::jniGetPropertyNames), makeNativeMethod("setBoolProperty", JavaScriptObject::setProperty), makeNativeMethod("setDoubleProperty", JavaScriptObject::setProperty), makeNativeMethod("setStringProperty", JavaScriptObject::setProperty>), makeNativeMethod("setJSValueProperty", JavaScriptObject::setProperty>), makeNativeMethod("setJSObjectProperty", JavaScriptObject::setProperty>), makeNativeMethod("unsetProperty", JavaScriptObject::unsetProperty), makeNativeMethod("defineBoolProperty", JavaScriptObject::defineProperty), makeNativeMethod("defineDoubleProperty", JavaScriptObject::defineProperty), makeNativeMethod("defineStringProperty", JavaScriptObject::defineProperty>), makeNativeMethod("defineJSValueProperty", JavaScriptObject::defineProperty>), makeNativeMethod("defineJSObjectProperty", JavaScriptObject::defineProperty>), makeNativeMethod("defineNativeDeallocator", JavaScriptObject::defineNativeDeallocator), }); } JavaScriptObject::JavaScriptObject( std::weak_ptr runtime, std::shared_ptr jsObject ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) { runtimeHolder.ensureRuntimeIsValid(); } JavaScriptObject::JavaScriptObject( WeakRuntimeHolder runtime, std::shared_ptr jsObject ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) { runtimeHolder.ensureRuntimeIsValid(); } std::shared_ptr JavaScriptObject::get() { return jsObject; } bool JavaScriptObject::hasProperty(const std::string &name) { auto &jsRuntime = runtimeHolder.getJSRuntime(); return jsObject->hasProperty(jsRuntime, name.c_str()); } jsi::Value JavaScriptObject::getProperty(const std::string &name) { auto &jsRuntime = runtimeHolder.getJSRuntime(); return jsObject->getProperty(jsRuntime, name.c_str()); } bool JavaScriptObject::jniHasProperty(jni::alias_ref name) { return hasProperty(name->toStdString()); } jni::local_ref JavaScriptObject::jniGetProperty( jni::alias_ref name ) { auto result = std::make_shared(getProperty(name->toStdString())); return JavaScriptValue::newObjectCxxArgs(runtimeHolder, result); } std::vector JavaScriptObject::getPropertyNames() { auto &jsRuntime = runtimeHolder.getJSRuntime(); jsi::Array properties = jsObject->getPropertyNames(jsRuntime); auto size = properties.size(jsRuntime); std::vector names(size); for (size_t i = 0; i < size; i++) { auto propertyName = properties .getValueAtIndex(jsRuntime, i) .asString(jsRuntime) .utf8(jsRuntime); names[i] = propertyName; } return names; } jni::local_ref> JavaScriptObject::jniGetPropertyNames() { std::vector cResult = getPropertyNames(); auto paredResult = jni::JArrayClass::newArray(cResult.size()); for (size_t i = 0; i < cResult.size(); i++) { paredResult->setElement(i, jni::make_jstring(cResult[i]).get()); } return paredResult; } jni::local_ref JavaScriptObject::jniAsFunction() { auto &jsRuntime = runtimeHolder.getJSRuntime(); auto jsFuncion = std::make_shared(jsObject->asFunction(jsRuntime)); return JavaScriptFunction::newObjectCxxArgs(runtimeHolder, jsFuncion); } void JavaScriptObject::setProperty(const std::string &name, jsi::Value value) { auto &jsRuntime = runtimeHolder.getJSRuntime(); jsObject->setProperty(jsRuntime, name.c_str(), value); } void JavaScriptObject::unsetProperty(jni::alias_ref name) { auto &jsRuntime = runtimeHolder.getJSRuntime(); auto cName = name->toStdString(); jsObject->setProperty( jsRuntime, cName.c_str(), jsi::Value::undefined() ); } jsi::Object JavaScriptObject::preparePropertyDescriptor( jsi::Runtime &jsRuntime, int options ) { jsi::Object descriptor(jsRuntime); descriptor.setProperty(jsRuntime, "configurable", (bool) ((1 << 0) & options)); descriptor.setProperty(jsRuntime, "enumerable", (bool) ((1 << 1) & options)); if ((bool) (1 << 2 & options)) { descriptor.setProperty(jsRuntime, "writable", true); } return descriptor; } void JavaScriptObject::defineProperty( jsi::Runtime &runtime, jsi::Object *jsthis, const std::string &name, jsi::Object descriptor ) { jsi::Object global = runtime.global(); jsi::Object objectClass = global.getPropertyAsObject(runtime, "Object"); jsi::Function definePropertyFunction = objectClass.getPropertyAsFunction( runtime, "defineProperty" ); // This call is basically the same as `Object.defineProperty(object, name, descriptor)` in JS definePropertyFunction.callWithThis(runtime, objectClass, { jsi::Value(runtime, *jsthis), jsi::String::createFromUtf8(runtime, name), std::move(descriptor), }); } void JavaScriptObject::defineNativeDeallocator( jni::alias_ref deallocator ) { auto &rt = runtimeHolder.getJSRuntime(); jni::global_ref globalRef = jni::make_global(deallocator); std::shared_ptr nativeDeallocator = std::make_shared( [globalRef = std::move(globalRef)]() mutable { auto args = jni::Environment::current()->NewObjectArray( 0, JavaReferencesCache::instance()->getJClass("java/lang/Object").clazz, nullptr ); globalRef->invoke(args); globalRef.reset(); }); auto descriptor = JavaScriptObject::preparePropertyDescriptor(rt, 0); descriptor.setProperty(rt, "value", jsi::Object::createFromHostObject(rt, nativeDeallocator)); jsObject->setProperty(rt, "__expo_shared_object_deallocator__", std::move(descriptor)); } } // namespace expo