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