Skip to content

Commit ce189b6

Browse files
committed
fix(react-native): optimize native api interop
1 parent 0308ace commit ce189b6

16 files changed

Lines changed: 1479 additions & 140 deletions

NativeScript/ffi/shared/jsi/NativeApiJsiBridge.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@ thread_local int gSynchronousNativeInvocationDepth = 0;
44
thread_local int gNativeCallerThreadJsiCallbackDepth = 0;
55
thread_local std::vector<std::string*> gNativeCallbackExceptionCaptureStack;
66
std::atomic<int> gActiveSynchronousNativeInvocationDepth{0};
7+
static char gNativeApiJsiExtendedClassKey;
8+
9+
void markNativeApiJsiExtendedClass(Class cls) {
10+
if (cls == Nil) {
11+
return;
12+
}
13+
objc_setAssociatedObject(cls, &gNativeApiJsiExtendedClassKey, @YES,
14+
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
15+
}
16+
17+
bool isNativeApiJsiExtendedClass(Class cls) {
18+
Class current = cls;
19+
while (current != Nil) {
20+
if (objc_getAssociatedObject(current, &gNativeApiJsiExtendedClassKey) != nil) {
21+
return true;
22+
}
23+
current = class_getSuperclass(current);
24+
}
25+
return false;
26+
}
727

828
class ScopedNativeApiUINativeCallDispatch final {
929
public:

NativeScript/ffi/shared/jsi/NativeApiJsiCallbacks.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,8 @@ class NativeApiJsiCallback final
522522
bridge_->invokeCallbacksOnNativeCallerThread();
523523
bool nativeCallerThreadCallback =
524524
nativeCallerThreadCallbacks && !currentThreadIsJs &&
525-
(block_ || (activeSynchronousNativeInvocation && !returnsVoid));
525+
(block_ || bindThis_ ||
526+
(activeSynchronousNativeInvocation && !returnsVoid));
526527
bool direct = currentThreadIsJs ||
527528
gExecutingDispatchedUINativeCall ||
528529
gSynchronousNativeInvocationDepth > 0 ||
@@ -1941,9 +1942,10 @@ std::shared_ptr<NativeApiJsiCallback> createJsiMethodCallback(
19411942

19421943
auto signature =
19431944
std::make_shared<NativeApiJsiSignature>(std::move(*parsed));
1945+
auto threadPolicy = readJsiCallbackThreadPolicy(runtime, function);
19441946
auto callback = std::make_shared<NativeApiJsiCallback>(
19451947
runtime, bridge, std::move(signature), std::move(function), false,
1946-
NativeApiJsiCallbackThreadPolicy::Default, true);
1948+
threadPolicy, true);
19471949
bridge->retainJsiLifetime(callback);
19481950
return callback;
19491951
}
@@ -1961,9 +1963,10 @@ std::shared_ptr<NativeApiJsiCallback> createJsiMethodCallback(
19611963

19621964
auto sharedSignature =
19631965
std::make_shared<NativeApiJsiSignature>(std::move(signature));
1966+
auto threadPolicy = readJsiCallbackThreadPolicy(runtime, function);
19641967
auto callback = std::make_shared<NativeApiJsiCallback>(
19651968
runtime, bridge, std::move(sharedSignature), std::move(function), false,
1966-
NativeApiJsiCallbackThreadPolicy::Default, true);
1969+
threadPolicy, true);
19671970
bridge->retainJsiLifetime(callback);
19681971
return callback;
19691972
}

NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ Value extendNativeApiJsiClass(
555555
throw facebook::jsi::JSError(runtime, "Failed to allocate Objective-C class.");
556556
}
557557

558+
markNativeApiJsiExtendedClass(nativeClass);
558559
class_addProtocol(nativeClass, @protocol(NativeApiJsiClassBuilderProtocol));
559560
rememberNativeApiJsiClassBuilder(runtime, bridge, nativeClass);
560561

NativeScript/ffi/shared/jsi/NativeApiJsiHostObject.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,26 @@ class NativeApiHostObject final : public HostObject {
248248
return Value::undefined();
249249
});
250250
}
251+
if (property == "__rememberObjectClassWrapper") {
252+
auto bridge = bridge_;
253+
return Function::createFromHostFunction(
254+
runtime, PropNameID::forAscii(runtime, "__rememberObjectClassWrapper"),
255+
2,
256+
[bridge](Runtime& runtime, const Value&, const Value* args,
257+
size_t count) -> Value {
258+
if (count < 2) {
259+
return Value::undefined();
260+
}
261+
id object = NativeApiObjectHostObject::nativeObjectFromValue(
262+
runtime, args[0]);
263+
if (object == nil) {
264+
return Value::undefined();
265+
}
266+
bridge->setObjectExpando(runtime, object,
267+
"__nativeApiClassWrapper", args[1]);
268+
return Value::undefined();
269+
});
270+
}
251271
if (property == "CC_SHA256") {
252272
auto bridge = bridge_;
253273
return Function::createFromHostFunction(
@@ -449,6 +469,7 @@ class NativeApiHostObject final : public HostObject {
449469
addPropertyName(runtime, names, "__extendClass");
450470
addPropertyName(runtime, names, "__invokeBase");
451471
addPropertyName(runtime, names, "__rememberClassWrapper");
472+
addPropertyName(runtime, names, "__rememberObjectClassWrapper");
452473
addPropertyName(runtime, names, "getFunction");
453474
addPropertyName(runtime, names, "getConstant");
454475
addPropertyName(runtime, names, "getEnum");

NativeScript/ffi/shared/jsi/NativeApiJsiHostObjects.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,11 @@ class NativeApiFastEnumerationIteratorHostObject final : public HostObject {
262262
NativeApiFastEnumerationIteratorHostObject(
263263
std::shared_ptr<NativeApiJsiBridge> bridge, id<NSFastEnumeration> collection)
264264
: bridge_(std::move(bridge)), collection_(collection) {
265-
[collection_ retain];
265+
[(id)collection_ retain];
266266
}
267267

268268
~NativeApiFastEnumerationIteratorHostObject() override {
269-
[collection_ release];
269+
[(id)collection_ release];
270270
collection_ = nil;
271271
}
272272

@@ -977,7 +977,20 @@ class NativeApiObjectHostObject final
977977
return;
978978
}
979979

980-
bridge_->setObjectExpando(runtime, object_, property, value);
980+
if (property == "__nativeApiClassWrapper" ||
981+
isNativeApiJsiExtendedClass(object_getClass(object_))) {
982+
bridge_->setObjectExpando(runtime, object_, property, value);
983+
return;
984+
}
985+
986+
const char* className = object_getClassName(object_);
987+
throw facebook::jsi::JSError(
988+
runtime,
989+
"Cannot assign JavaScript property '" + property +
990+
"' to native proxy " +
991+
(className != nullptr ? std::string(className) : std::string("")) +
992+
". Native proxies only accept writable native properties; keep "
993+
"JavaScript state in a WeakMap or external object.");
981994
}
982995

983996
std::vector<PropNameID> getPropertyNames(Runtime& runtime) override {
@@ -1131,7 +1144,7 @@ class NativeApiClassHostObject final : public HostObject {
11311144
runtime, "new does not take arguments; use invoke for an "
11321145
"explicit Objective-C selector.");
11331146
}
1134-
result = [cls new];
1147+
result = [[cls alloc] init];
11351148
} else {
11361149
if (count != 0) {
11371150
throw facebook::jsi::JSError(

NativeScript/ffi/shared/jsi/NativeApiJsiInstall.h

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName)
156156
}
157157
if (!force && Object.prototype.hasOwnProperty.call(globalThis, name)) {
158158
try {
159-
cacheGlobal(name, globalThis[name]);
159+
var existingDescriptor = Object.getOwnPropertyDescriptor(globalThis, name);
160+
if (existingDescriptor && Object.prototype.hasOwnProperty.call(existingDescriptor, 'value')) {
161+
cacheGlobal(name, existingDescriptor.value);
162+
}
160163
} catch (_) {
161164
}
162165
return;
@@ -697,6 +700,27 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName)
697700
});
698701
} catch (_) {
699702
}
703+
try {
704+
Object.defineProperty(constructable, 'new', {
705+
configurable: true,
706+
enumerable: false,
707+
writable: false,
708+
value: function() {
709+
if (arguments.length !== 0) {
710+
throw new Error('new does not take arguments; use invoke for an explicit Objective-C selector.');
711+
}
712+
if (typeof nativeClass.alloc !== 'function') {
713+
throw new Error('Native class cannot be allocated');
714+
}
715+
var instance = nativeClass.alloc();
716+
if (instance && typeof instance.init === 'function') {
717+
return rememberInstanceClass(instance.init());
718+
}
719+
return rememberInstanceClass(instance);
720+
}
721+
});
722+
} catch (_) {
723+
}
700724
try {
701725
Object.defineProperty(constructable, 'caller', {
702726
configurable: true,
@@ -716,6 +740,7 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName)
716740
} catch (_) {
717741
}
718742
var basePrototypeTarget = {};
743+
var classMembersInstalled = false;
719744
function installClassMembers(target, members, receiverIsClass) {
720745
if (!target || !members || typeof members.length !== 'number') {
721746
return;
@@ -790,8 +815,23 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName)
790815
}
791816
}
792817
}
793-
installClassMembers(constructable, nativeClass.__staticMembers, true);
794-
installClassMembers(basePrototypeTarget, nativeClass.__instanceMembers, false);
818+
function installNativeClassMembersIfNeeded() {
819+
if (classMembersInstalled) {
820+
return;
821+
}
822+
classMembersInstalled = true;
823+
installClassMembers(constructable, nativeClass.__staticMembers, true);
824+
installClassMembers(basePrototypeTarget, nativeClass.__instanceMembers, false);
825+
}
826+
try {
827+
Object.defineProperty(constructable, '__nativeApiInstallMembers', {
828+
configurable: true,
829+
enumerable: false,
830+
writable: false,
831+
value: installNativeClassMembersIfNeeded
832+
});
833+
} catch (_) {
834+
}
795835
try {
796836
Object.defineProperty(basePrototypeTarget, 'constructor', {
797837
configurable: true,
@@ -905,6 +945,7 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName)
905945
});
906946
} catch (_) {
907947
}
948+
var cachedNativeFunctions = typeof Map === 'function' ? new Map() : null;
908949
var wrapper = typeof Proxy === 'function'
909950
? new Proxy(constructable, {
910951
get: function(target, property, receiver) {
@@ -917,14 +958,36 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName)
917958
property === 'name') {
918959
return Reflect.get(target, property, receiver);
919960
}
961+
if (cachedNativeFunctions && cachedNativeFunctions.has(property)) {
962+
return cachedNativeFunctions.get(property);
963+
}
920964
var nativeValue = nativeClass[property];
921965
if (nativeValue !== undefined) {
966+
if (typeof nativeValue === 'function') {
967+
if (cachedNativeFunctions) {
968+
cachedNativeFunctions.set(property, nativeValue);
969+
}
970+
try {
971+
Object.defineProperty(target, property, {
972+
configurable: true,
973+
enumerable: false,
974+
writable: false,
975+
value: nativeValue
976+
});
977+
} catch (_) {
978+
}
979+
}
922980
return nativeValue;
923981
}
924982
var reflected = Reflect.get(target, property, receiver);
925983
if (reflected !== undefined || property in target) {
926984
return reflected;
927985
}
986+
installNativeClassMembersIfNeeded();
987+
reflected = Reflect.get(target, property, receiver);
988+
if (reflected !== undefined || property in target) {
989+
return reflected;
990+
}
928991
return reflected;
929992
},
930993
set: function(target, property, value, receiver) {
@@ -956,18 +1019,6 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName)
9561019
}
9571020
})
9581021
: constructable;
959-
try {
960-
var nativeSuperclass = nativeClass.__superclass;
961-
if (nativeSuperclass && nativeSuperclass !== nativeClass) {
962-
var superclassWrapper = wrapNativeClass(nativeSuperclass);
963-
if (superclassWrapper &&
964-
superclassWrapper !== wrapper &&
965-
superclassWrapper !== constructable) {
966-
Object.setPrototypeOf(wrapper, superclassWrapper);
967-
}
968-
}
969-
} catch (_) {
970-
}
9711022
if (classWrappers) {
9721023
classWrappers.set(nativeClass, wrapper);
9731024
}
@@ -1000,7 +1051,11 @@ void InstallNativeApiJsiGlobalSymbols(Runtime& runtime, const char* globalName)
10001051
function rememberClassOnInstance(instance, classWrapper) {
10011052
if (instance && typeof instance === 'object' && classWrapper) {
10021053
try {
1003-
instance.__nativeApiClassWrapper = classWrapper;
1054+
if (typeof api.__rememberObjectClassWrapper === 'function') {
1055+
api.__rememberObjectClassWrapper(instance, classWrapper);
1056+
} else {
1057+
instance.__nativeApiClassWrapper = classWrapper;
1058+
}
10041059
} catch (_) {
10051060
}
10061061
}

0 commit comments

Comments
 (0)