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 8 namespace expo { 9 void JavaScriptObject::registerNatives() { 10 registerHybrid({ 11 makeNativeMethod("hasProperty", JavaScriptObject::jniHasProperty), 12 makeNativeMethod("getProperty", JavaScriptObject::jniGetProperty), 13 makeNativeMethod("getPropertyNames", JavaScriptObject::jniGetPropertyNames), 14 makeNativeMethod("setBoolProperty", JavaScriptObject::setProperty<bool>), 15 makeNativeMethod("setDoubleProperty", JavaScriptObject::setProperty<double>), 16 makeNativeMethod("setStringProperty", 17 JavaScriptObject::setProperty<jni::alias_ref<jstring>>), 18 makeNativeMethod("setJSValueProperty", 19 JavaScriptObject::setProperty<jni::alias_ref<JavaScriptValue::javaobject>>), 20 makeNativeMethod("setJSObjectProperty", 21 JavaScriptObject::setProperty<jni::alias_ref<JavaScriptObject::javaobject>>), 22 makeNativeMethod("unsetProperty", JavaScriptObject::unsetProperty), 23 makeNativeMethod("defineBoolProperty", JavaScriptObject::defineProperty<bool>), 24 makeNativeMethod("defineDoubleProperty", 25 JavaScriptObject::defineProperty<double>), 26 makeNativeMethod("defineStringProperty", 27 JavaScriptObject::defineProperty<jni::alias_ref<jstring>>), 28 makeNativeMethod("defineJSValueProperty", 29 JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptValue::javaobject>>), 30 makeNativeMethod("defineJSObjectProperty", 31 JavaScriptObject::defineProperty<jni::alias_ref<JavaScriptObject::javaobject>>), 32 }); 33 } 34 35 JavaScriptObject::JavaScriptObject( 36 std::weak_ptr<JavaScriptRuntime> runtime, 37 std::shared_ptr<jsi::Object> jsObject 38 ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) { 39 runtimeHolder.ensureRuntimeIsValid(); 40 } 41 42 JavaScriptObject::JavaScriptObject( 43 WeakRuntimeHolder runtime, 44 std::shared_ptr<jsi::Object> jsObject 45 ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) { 46 runtimeHolder.ensureRuntimeIsValid(); 47 } 48 49 std::shared_ptr<jsi::Object> JavaScriptObject::get() { 50 return jsObject; 51 } 52 53 bool JavaScriptObject::hasProperty(const std::string &name) { 54 auto &jsRuntime = runtimeHolder.getJSRuntime(); 55 return jsObject->hasProperty(jsRuntime, name.c_str()); 56 } 57 58 jsi::Value JavaScriptObject::getProperty(const std::string &name) { 59 auto &jsRuntime = runtimeHolder.getJSRuntime(); 60 return jsObject->getProperty(jsRuntime, name.c_str()); 61 } 62 63 bool JavaScriptObject::jniHasProperty(jni::alias_ref<jstring> name) { 64 return hasProperty(name->toStdString()); 65 } 66 67 jni::local_ref<JavaScriptValue::javaobject> JavaScriptObject::jniGetProperty( 68 jni::alias_ref<jstring> name 69 ) { 70 auto result = std::make_shared<jsi::Value>(getProperty(name->toStdString())); 71 return JavaScriptValue::newObjectCxxArgs(runtimeHolder, result); 72 } 73 74 std::vector<std::string> JavaScriptObject::getPropertyNames() { 75 auto &jsRuntime = runtimeHolder.getJSRuntime(); 76 77 jsi::Array properties = jsObject->getPropertyNames(jsRuntime); 78 auto size = properties.size(jsRuntime); 79 80 std::vector<std::string> names(size); 81 for (size_t i = 0; i < size; i++) { 82 auto propertyName = properties 83 .getValueAtIndex(jsRuntime, i) 84 .asString(jsRuntime) 85 .utf8(jsRuntime); 86 names[i] = propertyName; 87 } 88 89 return names; 90 } 91 92 jni::local_ref<jni::JArrayClass<jstring>> JavaScriptObject::jniGetPropertyNames() { 93 std::vector<std::string> cResult = getPropertyNames(); 94 auto paredResult = jni::JArrayClass<jstring>::newArray(cResult.size()); 95 for (size_t i = 0; i < cResult.size(); i++) { 96 paredResult->setElement(i, jni::make_jstring(cResult[i]).get()); 97 } 98 99 return paredResult; 100 } 101 102 void JavaScriptObject::setProperty(const std::string &name, jsi::Value value) { 103 auto &jsRuntime = runtimeHolder.getJSRuntime(); 104 jsObject->setProperty(jsRuntime, name.c_str(), value); 105 } 106 107 void JavaScriptObject::unsetProperty(jni::alias_ref<jstring> name) { 108 auto &jsRuntime = runtimeHolder.getJSRuntime(); 109 auto cName = name->toStdString(); 110 jsObject->setProperty( 111 jsRuntime, 112 cName.c_str(), 113 jsi::Value::undefined() 114 ); 115 } 116 117 jsi::Object JavaScriptObject::preparePropertyDescriptor( 118 jsi::Runtime &jsRuntime, 119 int options 120 ) { 121 jsi::Object descriptor(jsRuntime); 122 descriptor.setProperty(jsRuntime, "configurable", (bool) ((1 << 0) & options)); 123 descriptor.setProperty(jsRuntime, "enumerable", (bool) ((1 << 1) & options)); 124 descriptor.setProperty(jsRuntime, "writable", (bool) ((1 << 2) & options)); 125 return descriptor; 126 } 127 128 void JavaScriptObject::defineProperty( 129 jsi::Runtime &runtime, 130 std::shared_ptr<jsi::Object> &jsthis, 131 const std::string &name, 132 jsi::Object descriptor 133 ) { 134 jsi::Object global = runtime.global(); 135 jsi::Object objectClass = global.getPropertyAsObject(runtime, "Object"); 136 jsi::Function definePropertyFunction = objectClass.getPropertyAsFunction( 137 runtime, 138 "defineProperty" 139 ); 140 141 // This call is basically the same as `Object.defineProperty(object, name, descriptor)` in JS 142 definePropertyFunction.callWithThis(runtime, objectClass, { 143 jsi::Value(runtime, *jsthis), 144 jsi::String::createFromUtf8(runtime, name), 145 std::move(descriptor), 146 }); 147 } 148 } // namespace expo 149