-
Notifications
You must be signed in to change notification settings - Fork 4
/
HostObjectObjc.mm
113 lines (96 loc) · 5.16 KB
/
HostObjectObjc.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Forked from https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.mm
#import <objc/runtime.h>
#import <stdio.h>
#import <stdlib.h>
#import "HostObjectObjc.h"
#import "HostObjectClass.h"
#import "HostObjectClassInstance.h"
#import "HostObjectSelector.h"
#import "HostObjectProtocol.h"
#import "JSIUtils.h"
#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>
#import <React/RCTBridge+Private.h>
#import <ReactCommon/RCTTurboModule.h>
#import <ReactCommon/CallInvoker.h>
#import <React/RCTBridge.h>
#import <ReactCommon/TurboModuleUtils.h>
#import <jsi/jsi.h>
#import <dlfcn.h>
std::vector<jsi::PropNameID> HostObjectObjc::getPropertyNames(jsi::Runtime& rt) {
std::vector<jsi::PropNameID> result;
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString")));
// List out the classes
int numClasses = objc_getClassList(NULL, 0);
Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) *numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
result.push_back(jsi::PropNameID::forUtf8(rt, std::string(class_getName(classes[i]))));
}
free(classes);
// I'm not aware of any objc runtime function by which to list out selectors and protocols!
return result;
}
jsi::Value HostObjectObjc::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
auto name = propName.utf8(runtime);
NSString* nameNSString = [NSString stringWithUTF8String:name.c_str()];
if (name == "toString") {
auto toString = [this] (jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value {
NSString* string = [NSString stringWithFormat:@"[object HostObjectObjc]"];
return jsi::String::createFromUtf8(runtime, string.UTF8String);
};
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString);
}
if (name == "getSelector") {
auto getSelector = [this] (jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
if(!arguments[0].isString()){
throw jsi::JSError(runtime, "TypeError: expected to be passed a String.");
}
RCTBridge *bridge = [RCTBridge currentBridge];
auto jsCallInvoker = bridge.jsCallInvoker;
NSString *selectorName = convertJSIValueToObjCObject(runtime, arguments[0], jsCallInvoker);
SEL sel = NSSelectorFromString(selectorName);
if(!sel){
return jsi::Value::undefined();
}
jsi::Object object = jsi::Object::createFromHostObject(runtime, std::make_unique<HostObjectSelector>(sel));
return object;
};
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "getSelector"), 1, getSelector);
}
// Cover all the type lookup utilities one-by-one!
// @see https://developer.apple.com/documentation/foundation/object_runtime/objective-c_runtime_utilities?language=objc
Class clazz = NSClassFromString(nameNSString);
if (clazz != nil) {
// TODO: read up on std::make_shared, std::make_unique, etc. and choose the best one
jsi::Object object = jsi::Object::createFromHostObject(runtime, std::make_unique<HostObjectClass>(clazz));
return object;
}
Protocol *protocol = NSProtocolFromString(nameNSString);
if (protocol != nil) {
jsi::Object object = jsi::Object::createFromHostObject(runtime, std::make_unique<HostObjectProtocol>(protocol));
return object;
}
// We don't attempt implicit selector lookup because surprisingly the string @"NSStringTransformLatinToHiragana"
// yields a selector (when we'd expect it to yield a variable instead; it's a global variable).
void *value = dlsym(RTLD_MAIN_ONLY, [nameNSString cStringUsingEncoding:NSUTF8StringEncoding]);
if (!value) {
value = dlsym(RTLD_SELF, [nameNSString cStringUsingEncoding:NSUTF8StringEncoding]);
}
if (!value) {
value = dlsym(RTLD_DEFAULT, [nameNSString cStringUsingEncoding:NSUTF8StringEncoding]);
}
if(!value) {
throw jsi::JSError(runtime, [[NSString stringWithFormat:@"ReferenceError: Can't find symbol within this executable: %@", nameNSString] cStringUsingEncoding:NSUTF8StringEncoding]);
}
// dlsym() returns a pointer to the given data.
// e.g. if we looked up `NSString* NSStringTransformToLatin`, it would return us not the NSString* directly, but a pointer to that NSString*.
// Thus, we need to dereference it using this piece of witchcraft below.
// @see https://stackoverflow.com/questions/23742392/how-do-i-cast-void-to-nsstring-without-getting-a-runtime-error-in-objective-c
id valueObjc = *((__unsafe_unretained id*)value);
// isKindOfClass checks whether valueObjc is an instance of any subclass of NSObject or NSObject itself.
if(![valueObjc isKindOfClass:[NSObject class]]){
throw jsi::JSError(runtime, [[NSString stringWithFormat:@"TypeError: Did find the symbol named '%@', but it's not a type we can currently handle (expected a class instance).", nameNSString] cStringUsingEncoding:NSUTF8StringEncoding]);
}
return jsi::Object::createFromHostObject(runtime, std::make_shared<HostObjectClassInstance>(valueObjc));
}