1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo) 2 3 #include "JavaScriptObject.h" 4 #include "JavaScriptValue.h" 5 #include "JavaScriptRuntime.h" 6 #include "JSITypeConverter.h" 7 #include "ObjectDeallocator.h" 8 #include "JavaReferencesCache.h" 9 10 namespace expo { 11 void JavaScriptObject::registerNatives() { 12 registerHybrid({ 13 makeNativeMethod("hasProperty", JavaScriptObject::jniHasProperty), 14 makeNativeMethod("getProperty", JavaScriptObject::jniGetProperty), 15 makeNativeMethod("getPropertyNames", JavaScriptObject::jniGetPropertyNames), 16 makeNativeMethod("setBoolProperty", JavaScriptObject::setProperty<bool>), 17 makeNativeMethod("setDoubleProperty", JavaScriptObject::setProperty<double>), 18 makeNativeMethod("setStringProperty", 19 JavaScriptObject::setProperty<jni::alias_ref<jstring>>), 20 makeNativeMethod("setJSValueProperty", 21 JavaScriptObject::setProperty<jni::alias_ref<JavaScriptValue::javaobject>>), 22 makeNativeMethod("setJSObjectProperty", 23 JavaScriptObject::setProperty<jni::alias_ref<JavaScriptObject::javaobject>>), 24 makeNativeMethod("unsetProperty", JavaScriptObject::unsetProperty), 25 makeNativeMethod("defineBoolProperty", JavaScriptObject::defineProperty<bool>), 26 makeNativeMethod("defineDoubleProperty", 27 JavaScriptObject::defineProperty<double>), 28 makeNativeMethod("defineStringProperty", 29 JavaScriptObject::defineProperty<jni::alias_ref<jstring>>), 30 makeNativeMethod("defineJSValueProperty", 31 JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptValue::javaobject>>), 32 makeNativeMethod("defineJSObjectProperty", 33 JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptObject::javaobject>>), 34 makeNativeMethod("defineNativeDeallocator", 35 JavaScriptObject::defineNativeDeallocator), 36 }); 37 } 38 39 JavaScriptObject::JavaScriptObject( 40 std::weak_ptr<JavaScriptRuntime> runtime, 41 std::shared_ptr<jsi::Object> jsObject 42 ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) { 43 runtimeHolder.ensureRuntimeIsValid(); 44 } 45 46 JavaScriptObject::JavaScriptObject( 47 WeakRuntimeHolder runtime, 48 std::shared_ptr<jsi::Object> jsObject 49 ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) { 50 runtimeHolder.ensureRuntimeIsValid(); 51 } 52 53 std::shared_ptr<jsi::Object> JavaScriptObject::get() { 54 return jsObject; 55 } 56 57 bool JavaScriptObject::hasProperty(const std::string &name) { 58 auto &jsRuntime = runtimeHolder.getJSRuntime(); 59 return jsObject->hasProperty(jsRuntime, name.c_str()); 60 } 61 62 jsi::Value JavaScriptObject::getProperty(const std::string &name) { 63 auto &jsRuntime = runtimeHolder.getJSRuntime(); 64 return jsObject->getProperty(jsRuntime, name.c_str()); 65 } 66 67 bool JavaScriptObject::jniHasProperty(jni::alias_ref<jstring> name) { 68 return hasProperty(name->toStdString()); 69 } 70 71 jni::local_ref<JavaScriptValue::javaobject> JavaScriptObject::jniGetProperty( 72 jni::alias_ref<jstring> name 73 ) { 74 auto result = std::make_shared<jsi::Value>(getProperty(name->toStdString())); 75 return JavaScriptValue::newObjectCxxArgs(runtimeHolder, result); 76 } 77 78 std::vector<std::string> JavaScriptObject::getPropertyNames() { 79 auto &jsRuntime = runtimeHolder.getJSRuntime(); 80 81 jsi::Array properties = jsObject->getPropertyNames(jsRuntime); 82 auto size = properties.size(jsRuntime); 83 84 std::vector<std::string> names(size); 85 for (size_t i = 0; i < size; i++) { 86 auto propertyName = properties 87 .getValueAtIndex(jsRuntime, i) 88 .asString(jsRuntime) 89 .utf8(jsRuntime); 90 names[i] = propertyName; 91 } 92 93 return names; 94 } 95 96 jni::local_ref<jni::JArrayClass<jstring>> JavaScriptObject::jniGetPropertyNames() { 97 std::vector<std::string> cResult = getPropertyNames(); 98 auto paredResult = jni::JArrayClass<jstring>::newArray(cResult.size()); 99 for (size_t i = 0; i < cResult.size(); i++) { 100 paredResult->setElement(i, jni::make_jstring(cResult[i]).get()); 101 } 102 103 return paredResult; 104 } 105 106 void JavaScriptObject::setProperty(const std::string &name, jsi::Value value) { 107 auto &jsRuntime = runtimeHolder.getJSRuntime(); 108 jsObject->setProperty(jsRuntime, name.c_str(), value); 109 } 110 111 void JavaScriptObject::unsetProperty(jni::alias_ref<jstring> name) { 112 auto &jsRuntime = runtimeHolder.getJSRuntime(); 113 auto cName = name->toStdString(); 114 jsObject->setProperty( 115 jsRuntime, 116 cName.c_str(), 117 jsi::Value::undefined() 118 ); 119 } 120 121 jsi::Object JavaScriptObject::preparePropertyDescriptor( 122 jsi::Runtime &jsRuntime, 123 int options 124 ) { 125 jsi::Object descriptor(jsRuntime); 126 descriptor.setProperty(jsRuntime, "configurable", (bool) ((1 << 0) & options)); 127 descriptor.setProperty(jsRuntime, "enumerable", (bool) ((1 << 1) & options)); 128 if ((bool) (1 << 2 & options)) { 129 descriptor.setProperty(jsRuntime, "writable", true); 130 } 131 return descriptor; 132 } 133 134 void JavaScriptObject::defineProperty( 135 jsi::Runtime &runtime, 136 jsi::Object *jsthis, 137 const std::string &name, 138 jsi::Object descriptor 139 ) { 140 jsi::Object global = runtime.global(); 141 jsi::Object objectClass = global.getPropertyAsObject(runtime, "Object"); 142 jsi::Function definePropertyFunction = objectClass.getPropertyAsFunction( 143 runtime, 144 "defineProperty" 145 ); 146 147 // This call is basically the same as `Object.defineProperty(object, name, descriptor)` in JS 148 definePropertyFunction.callWithThis(runtime, objectClass, { 149 jsi::Value(runtime, *jsthis), 150 jsi::String::createFromUtf8(runtime, name), 151 std::move(descriptor), 152 }); 153 } 154 155 void JavaScriptObject::defineNativeDeallocator( 156 jni::alias_ref<JNIFunctionBody::javaobject> deallocator 157 ) { 158 auto &rt = runtimeHolder.getJSRuntime(); 159 jni::global_ref<JNIFunctionBody::javaobject> globalRef = jni::make_global(deallocator); 160 std::shared_ptr<ObjectDeallocator> nativeDeallocator = std::make_shared<ObjectDeallocator>( 161 [globalRef = std::move(globalRef)]() mutable { 162 auto args = jni::Environment::current()->NewObjectArray( 163 0, 164 JavaReferencesCache::instance()->getJClass("java/lang/Object").clazz, 165 nullptr 166 ); 167 globalRef->invoke(args); 168 globalRef.reset(); 169 }); 170 auto descriptor = JavaScriptObject::preparePropertyDescriptor(rt, 0); 171 descriptor.setProperty(rt, "value", jsi::Object::createFromHostObject(rt, nativeDeallocator)); 172 jsObject->setProperty(rt, "__expo_shared_object_deallocator__", std::move(descriptor)); 173 } 174 } // namespace expo 175