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