From c5de81c9802b87874bb04cd5927b1b4d999c29da Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Sun, 9 Aug 2026 00:01:23 -0700 Subject: [PATCH] fix(ffi): accept colon-less exposedMethods selectors with declared params The Objective-C runtime allows a method whose selector has no colons to carry argument slots in its type encoding, and callers such as NSNotificationCenter invoke it with the argument. @nativescript/core relies on this (NotificationObserver exposes onReceive with one NSNotification param). Only reject the selector/params count mismatch when the selector explicitly declares argument slots. Co-Authored-By: Claude Fable 5 --- .../ffi/shared/jsi/NativeApiJsiClassBuilder.h | 381 ++++++++---------- 1 file changed, 160 insertions(+), 221 deletions(-) diff --git a/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h b/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h index bf4d3b2fc..8e20000e0 100644 --- a/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h +++ b/NativeScript/ffi/shared/jsi/NativeApiJsiClassBuilder.h @@ -1,5 +1,4 @@ -std::string readOptionalStringProperty(Runtime& runtime, const Object& object, - const char* name) { +std::string readOptionalStringProperty(Runtime& runtime, const Object& object, const char* name) { if (name == nullptr || !object.hasProperty(runtime, name)) { return ""; } @@ -14,19 +13,17 @@ struct NativeApiJsiClassBuilderRegistration { }; std::mutex gNativeApiJsiClassBuilderMutex; -std::unordered_map - gNativeApiJsiClassBuilders; +std::unordered_map gNativeApiJsiClassBuilders; struct NativeApiJsiKnownExposedMethod { std::string selectorName; NativeApiJsiSignature signature; }; std::mutex gNativeApiJsiKnownExposedMethodsMutex; -std::unordered_map - gNativeApiJsiKnownExposedMethods; +std::unordered_map gNativeApiJsiKnownExposedMethods; -void rememberNativeApiJsiClassBuilder( - Runtime& runtime, const std::shared_ptr& bridge, - Class cls) { +void rememberNativeApiJsiClassBuilder(Runtime& runtime, + const std::shared_ptr& bridge, + Class cls) { if (cls == Nil) { return; } @@ -39,8 +36,8 @@ void rememberNativeApiJsiClassBuilder( }; } -void rememberNativeApiJsiKnownExposedMethod( - const std::string& selectorName, const NativeApiJsiSignature& signature) { +void rememberNativeApiJsiKnownExposedMethod(const std::string& selectorName, + const NativeApiJsiSignature& signature) { if (selectorName.empty()) { return; } @@ -50,8 +47,7 @@ void rememberNativeApiJsiKnownExposedMethod( }; std::lock_guard lock(gNativeApiJsiKnownExposedMethodsMutex); gNativeApiJsiKnownExposedMethods[selectorName] = method; - gNativeApiJsiKnownExposedMethods[jsifySelector(selectorName.c_str())] = - std::move(method); + gNativeApiJsiKnownExposedMethods[jsifySelector(selectorName.c_str())] = std::move(method); } std::optional knownNativeApiJsiExposedMethod( @@ -66,8 +62,7 @@ std::optional knownNativeApiJsiExposedMethod( return method; } -std::optional -findNativeApiJsiClassBuilder(id object) { +std::optional findNativeApiJsiClassBuilder(id object) { Class cls = object != nil ? object_getClass(object) : Nil; std::lock_guard lock(gNativeApiJsiClassBuilderMutex); while (cls != Nil) { @@ -84,23 +79,22 @@ const char* nativeApiJsiFastEnumerationEncoding() { static const char* encoding = nullptr; if (encoding == nullptr) { struct objc_method_description desc = protocol_getMethodDescription( - @protocol(NSFastEnumeration), - @selector(countByEnumeratingWithState:objects:count:), YES, YES); + @protocol(NSFastEnumeration), @selector(countByEnumeratingWithState:objects:count:), YES, + YES); encoding = desc.types; } return encoding; } -NSUInteger nativeApiJsiSymbolIteratorCountByEnumerating( - id self, SEL, NSFastEnumerationState* state, - id __unsafe_unretained stackbuf[], NSUInteger len) { +NSUInteger nativeApiJsiSymbolIteratorCountByEnumerating(id self, SEL, NSFastEnumerationState* state, + id __unsafe_unretained stackbuf[], + NSUInteger len) { if (len == 0 || state == nullptr || stackbuf == nullptr) { return 0; } auto registration = findNativeApiJsiClassBuilder(self); - if (!registration || registration->runtime == nullptr || - registration->bridge == nullptr) { + if (!registration || registration->runtime == nullptr || registration->bridge == nullptr) { return 0; } @@ -114,44 +108,36 @@ NSUInteger nativeApiJsiSymbolIteratorCountByEnumerating( } Value iteratorFactoryValue = - runtime.global().getProperty(runtime, - "__nativeScriptCreateNativeApiIterator"); + runtime.global().getProperty(runtime, "__nativeScriptCreateNativeApiIterator"); if (!iteratorFactoryValue.isObject() || !iteratorFactoryValue.asObject(runtime).isFunction(runtime)) { return 0; } - Function iteratorFactory = - iteratorFactoryValue.asObject(runtime).asFunction(runtime); - Value prototype = - bridge->findClassPrototype(runtime, object_getClass(self)); + Function iteratorFactory = iteratorFactoryValue.asObject(runtime).asFunction(runtime); + Value prototype = bridge->findClassPrototype(runtime, object_getClass(self)); Value iteratorValue = prototype.isObject() - ? iteratorFactory.call(runtime, Value(runtime, receiver), - Value(runtime, prototype)) + ? iteratorFactory.call(runtime, Value(runtime, receiver), Value(runtime, prototype)) : iteratorFactory.call(runtime, Value(runtime, receiver)); if (!iteratorValue.isObject()) { return 0; } Object iterator = iteratorValue.asObject(runtime); Value nextValue = iterator.getProperty(runtime, "next"); - if (!nextValue.isObject() || - !nextValue.asObject(runtime).isFunction(runtime)) { + if (!nextValue.isObject() || !nextValue.asObject(runtime).isFunction(runtime)) { return 0; } Function next = nextValue.asObject(runtime).asFunction(runtime); - auto callNext = [&]() -> Value { - return next.callWithThis(runtime, iterator); - }; + auto callNext = [&]() -> Value { return next.callWithThis(runtime, iterator); }; for (unsigned long skipped = 0; skipped < state->state; skipped++) { Value skippedResult = callNext(); if (!skippedResult.isObject()) { return 0; } - Value doneValue = - skippedResult.asObject(runtime).getProperty(runtime, "done"); + Value doneValue = skippedResult.asObject(runtime).getProperty(runtime, "done"); if (doneValue.isBool() && doneValue.getBool()) { return 0; } @@ -189,8 +175,8 @@ NSUInteger nativeApiJsiSymbolIteratorCountByEnumerating( } } -NativeApiSymbol runtimeSymbolForClass( - const std::shared_ptr& bridge, Class cls) { +NativeApiSymbol runtimeSymbolForClass(const std::shared_ptr& bridge, + Class cls) { if (bridge != nullptr) { if (const NativeApiSymbol* symbol = bridge->findClassForRuntimeClass(cls)) { return *symbol; @@ -222,13 +208,12 @@ std::string nextAvailableJsiClassName(const std::string& requestedName) { return candidate; } -std::vector methodOverridesForName( - const std::vector& members, const std::string& name) { +std::vector methodOverridesForName(const std::vector& members, + const std::string& name) { std::vector result; std::unordered_set selectors; for (const auto& member : members) { - if (member.property || member.name != name || - (member.flags & metagen::mdMemberStatic) != 0 || + if (member.property || member.name != name || (member.flags & metagen::mdMemberStatic) != 0 || member.selectorName.empty()) { continue; } @@ -239,12 +224,11 @@ std::vector methodOverridesForName( return result; } -const NativeApiMember* propertyOverrideForName( - const std::vector& members, const std::string& name) { +const NativeApiMember* propertyOverrideForName(const std::vector& members, + const std::string& name) { const NativeApiMember* fallback = nullptr; for (const auto& member : members) { - if (member.property && member.name == name && - (member.flags & metagen::mdMemberStatic) == 0) { + if (member.property && member.name == name && (member.flags & metagen::mdMemberStatic) == 0) { if (fallback == nullptr) { fallback = &member; } @@ -256,32 +240,25 @@ const NativeApiMember* propertyOverrideForName( return fallback; } -void addJsiOverrideMethod(Runtime& runtime, - const std::shared_ptr& bridge, - Class nativeClass, Class baseClass, - const std::string& selectorName, - MDSectionOffset signatureOffset, - bool returnOwned, Function function) { +void addJsiOverrideMethod(Runtime& runtime, const std::shared_ptr& bridge, + Class nativeClass, Class baseClass, const std::string& selectorName, + MDSectionOffset signatureOffset, bool returnOwned, Function function) { if (selectorName.empty() || signatureOffset == MD_SECTION_OFFSET_NULL) { return; } - auto callback = createJsiMethodCallback(runtime, bridge, selectorName, - signatureOffset, std::move(function), - returnOwned); + auto callback = createJsiMethodCallback(runtime, bridge, selectorName, signatureOffset, + std::move(function), returnOwned); SEL selector = sel_registerName(selectorName.c_str()); - std::string metadataEncoding = - objcMethodSignatureForJsiSignature(callback->signature()); - class_replaceMethod(nativeClass, selector, - reinterpret_cast(callback->functionPointer()), + std::string metadataEncoding = objcMethodSignatureForJsiSignature(callback->signature()); + class_replaceMethod(nativeClass, selector, reinterpret_cast(callback->functionPointer()), metadataEncoding.c_str()); } Value getObjectPropertyOrUndefined(Runtime& runtime, const Object& object, const std::string& name) { - return object.hasProperty(runtime, name.c_str()) - ? object.getProperty(runtime, name.c_str()) - : Value::undefined(); + return object.hasProperty(runtime, name.c_str()) ? object.getProperty(runtime, name.c_str()) + : Value::undefined(); } Class dispatchSuperclassForJsiDerivedReceiver(id receiver, Class fallback) { @@ -291,8 +268,7 @@ Class dispatchSuperclassForJsiDerivedReceiver(id receiver, Class fallback) { Class receiverClass = object_getClass(receiver); if (receiverClass == Nil || - !class_conformsToProtocol(receiverClass, - @protocol(NativeApiJsiClassBuilderProtocol))) { + !class_conformsToProtocol(receiverClass, @protocol(NativeApiJsiClassBuilderProtocol))) { return Nil; } @@ -300,8 +276,7 @@ Class dispatchSuperclassForJsiDerivedReceiver(id receiver, Class fallback) { return superclass != Nil ? superclass : fallback; } -std::optional functionForSelector(Runtime& runtime, - const Object& methods, +std::optional functionForSelector(Runtime& runtime, const Object& methods, const std::string& selectorName) { Value value = getObjectPropertyOrUndefined(runtime, methods, selectorName); if (!value.isObject() || !value.asObject(runtime).isFunction(runtime)) { @@ -316,14 +291,14 @@ std::optional functionForSelector(Runtime& runtime, return value.asObject(runtime).asFunction(runtime); } -std::optional readExposedType( - Runtime& runtime, const std::shared_ptr& bridge, - const Object& descriptor, const char* propertyName) { +std::optional readExposedType(Runtime& runtime, + const std::shared_ptr& bridge, + const Object& descriptor, + const char* propertyName) { if (!descriptor.hasProperty(runtime, propertyName)) { return std::nullopt; } - return interopTypeFromValue(runtime, bridge, - descriptor.getProperty(runtime, propertyName)); + return interopTypeFromValue(runtime, bridge, descriptor.getProperty(runtime, propertyName)); } std::optional exposedMethodSignature( @@ -339,46 +314,48 @@ std::optional exposedMethodSignature( Value paramsValue = getObjectPropertyOrUndefined(runtime, descriptor, "params"); if (!paramsValue.isUndefined() && !paramsValue.isNull()) { if (!paramsValue.isObject() || !paramsValue.asObject(runtime).isArray(runtime)) { - throw facebook::jsi::JSError( - runtime, "exposedMethods params must be an array."); + throw facebook::jsi::JSError(runtime, "exposedMethods params must be an array."); } Array params = paramsValue.asObject(runtime).getArray(runtime); for (size_t i = 0; i < params.size(runtime); i++) { Value typeValue = params.getValueAtIndex(runtime, i); auto type = interopTypeFromValue(runtime, bridge, typeValue); if (!type) { - throw facebook::jsi::JSError( - runtime, "exposedMethods contains an unsupported parameter type."); + throw facebook::jsi::JSError(runtime, + "exposedMethods contains an unsupported parameter type."); } signature.argumentTypes.push_back(*type); } } - if (selectorArgumentCount(selectorName) != signature.argumentTypes.size()) { - throw facebook::jsi::JSError( - runtime, "exposedMethods selector argument count does not match params."); + // A colon-less selector may still declare params (@nativescript/core + // exposes onReceive with one NSNotification param); the Objective-C runtime + // accepts such methods and callers like NSNotificationCenter invoke them + // with the argument. Only reject a mismatch when the selector explicitly + // declares argument slots. + size_t selectorArguments = selectorArgumentCount(selectorName); + if (selectorArguments != signature.argumentTypes.size() && selectorArguments != 0) { + throw facebook::jsi::JSError(runtime, + "exposedMethods selector argument count does not match params."); } prepareJsiMethodSignature(&signature); return signature; } -std::optional runtimeProtocolMethodSignature( - const char* types) { +std::optional runtimeProtocolMethodSignature(const char* types) { if (types == nullptr) { return std::nullopt; } - NSMethodSignature* methodSignature = - [NSMethodSignature signatureWithObjCTypes:types]; + NSMethodSignature* methodSignature = [NSMethodSignature signatureWithObjCTypes:types]; if (methodSignature == nil || methodSignature.numberOfArguments < 2) { return std::nullopt; } NativeApiJsiSignature signature; signature.implicitArgumentCount = 2; - signature.returnType = - parseObjCEncodedJsiType(methodSignature.methodReturnType); + signature.returnType = parseObjCEncodedJsiType(methodSignature.methodReturnType); for (NSUInteger i = 2; i < methodSignature.numberOfArguments; i++) { signature.argumentTypes.push_back( parseObjCEncodedJsiType([methodSignature getArgumentTypeAtIndex:i])); @@ -395,8 +372,7 @@ std::optional runtimeProtocolMethodSignature( } std::optional protocolSymbolFromJsiValue( - Runtime& runtime, const std::shared_ptr& bridge, - const Value& value) { + Runtime& runtime, const std::shared_ptr& bridge, const Value& value) { if (value.isString()) { std::string name = value.asString(runtime).utf8(runtime); if (const NativeApiSymbol* symbol = bridge->findProtocol(name)) { @@ -434,25 +410,23 @@ std::optional protocolSymbolFromJsiValue( return std::nullopt; } -void addJsiExposedMethod(Runtime& runtime, - const std::shared_ptr& bridge, +void addJsiExposedMethod(Runtime& runtime, const std::shared_ptr& bridge, Class nativeClass, const std::string& selectorName, NativeApiJsiSignature signature, Function function) { if (selectorName.empty()) { return; } - auto callback = createJsiMethodCallback(runtime, bridge, selectorName, - std::move(signature), std::move(function)); + auto callback = createJsiMethodCallback(runtime, bridge, selectorName, std::move(signature), + std::move(function)); std::string encoding = objcMethodSignatureForJsiSignature(callback->signature()); class_replaceMethod(nativeClass, sel_registerName(selectorName.c_str()), - reinterpret_cast(callback->functionPointer()), - encoding.c_str()); + reinterpret_cast(callback->functionPointer()), encoding.c_str()); } -bool addRuntimeProtocolOverrideForName( - Runtime& runtime, const std::shared_ptr& bridge, - Class nativeClass, const std::vector& protocols, - const std::string& propertyName, Function function) { +bool addRuntimeProtocolOverrideForName(Runtime& runtime, + const std::shared_ptr& bridge, + Class nativeClass, const std::vector& protocols, + const std::string& propertyName, Function function) { std::unordered_set visited; std::function visit = [&](Protocol* protocol) -> bool { if (protocol == nullptr || !visited.insert(protocol).second) { @@ -479,16 +453,14 @@ bool addRuntimeProtocolOverrideForName( protocol_copyMethodDescriptionList(protocol, required, YES, &count); for (unsigned int i = 0; i < count; i++) { SEL selector = descriptions[i].name; - const char* selectorName = - selector != nullptr ? sel_getName(selector) : nullptr; - if (selectorName == nullptr || - jsifySelector(selectorName) != propertyName) { + const char* selectorName = selector != nullptr ? sel_getName(selector) : nullptr; + if (selectorName == nullptr || jsifySelector(selectorName) != propertyName) { continue; } auto signature = runtimeProtocolMethodSignature(descriptions[i].types); if (signature) { - addJsiExposedMethod(runtime, bridge, nativeClass, selectorName, - std::move(*signature), std::move(function)); + addJsiExposedMethod(runtime, bridge, nativeClass, selectorName, std::move(*signature), + std::move(function)); free(descriptions); return true; } @@ -506,47 +478,37 @@ bool addRuntimeProtocolOverrideForName( return false; } -Object getOwnPropertyDescriptor(Runtime& runtime, const Object& object, - const std::string& name) { +Object getOwnPropertyDescriptor(Runtime& runtime, const Object& object, const std::string& name) { Object objectCtor = runtime.global().getPropertyAsObject(runtime, "Object"); Function getOwnPropertyDescriptor = objectCtor.getPropertyAsFunction(runtime, "getOwnPropertyDescriptor"); Value args[] = {Value(runtime, object), makeString(runtime, name)}; - Value descriptorValue = - getOwnPropertyDescriptor.call(runtime, static_cast(args), - static_cast(2)); - return descriptorValue.isObject() ? descriptorValue.asObject(runtime) - : Object(runtime); + Value descriptorValue = getOwnPropertyDescriptor.call(runtime, static_cast(args), + static_cast(2)); + return descriptorValue.isObject() ? descriptorValue.asObject(runtime) : Object(runtime); } -Value extendNativeApiJsiClass( - Runtime& runtime, const std::shared_ptr& bridge, - const Value* args, size_t count) { +Value extendNativeApiJsiClass(Runtime& runtime, const std::shared_ptr& bridge, + const Value* args, size_t count) { if (count < 2 || !args[0].isObject() || !args[1].isObject()) { - throw facebook::jsi::JSError( - runtime, "extendClass expects a native class and method object."); + throw facebook::jsi::JSError(runtime, "extendClass expects a native class and method object."); } Class baseClass = classFromJsiValue(runtime, args[0]); if (baseClass == Nil) { - throw facebook::jsi::JSError( - runtime, "extendClass can only extend native class constructors."); + throw facebook::jsi::JSError(runtime, "extendClass can only extend native class constructors."); } - if (class_conformsToProtocol(baseClass, - @protocol(NativeApiJsiClassBuilderProtocol))) { - throw facebook::jsi::JSError(runtime, - "Cannot extend an already extended class."); + if (class_conformsToProtocol(baseClass, @protocol(NativeApiJsiClassBuilderProtocol))) { + throw facebook::jsi::JSError(runtime, "Cannot extend an already extended class."); } Object methods = args[1].asObject(runtime); - Object options = count >= 3 && args[2].isObject() - ? args[2].asObject(runtime) - : Object(runtime); + Object options = count >= 3 && args[2].isObject() ? args[2].asObject(runtime) : Object(runtime); std::string requestedName = readOptionalStringProperty(runtime, options, "name"); if (requestedName.empty()) { const char* baseName = class_getName(baseClass); - requestedName = std::string(baseName != nullptr ? baseName : "NSObject") + - "_Extended_" + std::to_string(rand()); + requestedName = std::string(baseName != nullptr ? baseName : "NSObject") + "_Extended_" + + std::to_string(rand()); } std::string className = nextAvailableJsiClassName(requestedName); @@ -560,12 +522,10 @@ Value extendNativeApiJsiClass( rememberNativeApiJsiClassBuilder(runtime, bridge, nativeClass); NativeApiSymbol baseSymbol = runtimeSymbolForClass(bridge, baseClass); - std::vector extensionMembers = - bridge->membersForClass(baseSymbol); + std::vector extensionMembers = bridge->membersForClass(baseSymbol); std::vector optionProtocols; Value protocolsValue = getObjectPropertyOrUndefined(runtime, options, "protocols"); - if (protocolsValue.isObject() && - protocolsValue.asObject(runtime).isArray(runtime)) { + if (protocolsValue.isObject() && protocolsValue.asObject(runtime).isArray(runtime)) { Array protocols = protocolsValue.asObject(runtime).getArray(runtime); for (size_t i = 0; i < protocols.size(runtime); i++) { Value protocolValue = protocols.getValueAtIndex(runtime, i); @@ -584,8 +544,7 @@ Value extendNativeApiJsiClass( } if (protocolSymbol) { const auto& protocolMembers = bridge->membersForProtocol(*protocolSymbol); - extensionMembers.insert(extensionMembers.begin(), - protocolMembers.begin(), + extensionMembers.insert(extensionMembers.begin(), protocolMembers.begin(), protocolMembers.end()); } } @@ -606,44 +565,39 @@ Value extendNativeApiJsiClass( auto overrides = methodOverridesForName(members, propertyName); bool addedOverride = false; for (const auto& member : overrides) { - if (member.selectorName.empty() || - member.signatureOffset == MD_SECTION_OFFSET_NULL || + if (member.selectorName.empty() || member.signatureOffset == MD_SECTION_OFFSET_NULL || member.signatureOffset == 0) { continue; } - addJsiOverrideMethod( - runtime, bridge, nativeClass, baseClass, member.selectorName, - member.signatureOffset, - (member.flags & metagen::mdMemberReturnOwned) != 0, - value.asObject(runtime).asFunction(runtime)); + addJsiOverrideMethod(runtime, bridge, nativeClass, baseClass, member.selectorName, + member.signatureOffset, + (member.flags & metagen::mdMemberReturnOwned) != 0, + value.asObject(runtime).asFunction(runtime)); addedOverride = true; } - if (!addedOverride) { - bool addedRuntimeProtocolOverride = addRuntimeProtocolOverrideForName( - runtime, bridge, nativeClass, optionProtocols, propertyName, - value.asObject(runtime).asFunction(runtime)); - if (!addedRuntimeProtocolOverride) { - if (auto known = knownNativeApiJsiExposedMethod(propertyName)) { - addJsiExposedMethod(runtime, bridge, nativeClass, - known->selectorName, - std::move(known->signature), - value.asObject(runtime).asFunction(runtime)); - } - } - } - } - - const NativeApiMember* propertyMember = - propertyOverrideForName(members, propertyName); + if (!addedOverride) { + bool addedRuntimeProtocolOverride = addRuntimeProtocolOverrideForName( + runtime, bridge, nativeClass, optionProtocols, propertyName, + value.asObject(runtime).asFunction(runtime)); + if (!addedRuntimeProtocolOverride) { + if (auto known = knownNativeApiJsiExposedMethod(propertyName)) { + addJsiExposedMethod(runtime, bridge, nativeClass, known->selectorName, + std::move(known->signature), + value.asObject(runtime).asFunction(runtime)); + } + } + } + } + + const NativeApiMember* propertyMember = propertyOverrideForName(members, propertyName); Value getter = descriptor.getProperty(runtime, "get"); if (propertyMember != nullptr && getter.isObject() && getter.asObject(runtime).isFunction(runtime)) { - addJsiOverrideMethod( - runtime, bridge, nativeClass, baseClass, - propertyMember->selectorName, propertyMember->signatureOffset, - (propertyMember->flags & metagen::mdMemberReturnOwned) != 0, - getter.asObject(runtime).asFunction(runtime)); + addJsiOverrideMethod(runtime, bridge, nativeClass, baseClass, propertyMember->selectorName, + propertyMember->signatureOffset, + (propertyMember->flags & metagen::mdMemberReturnOwned) != 0, + getter.asObject(runtime).asFunction(runtime)); } else if (propertyMember == nullptr && getter.isObject() && getter.asObject(runtime).isFunction(runtime)) { auto overrides = methodOverridesForName(members, propertyName); @@ -651,17 +605,16 @@ Value extendNativeApiJsiClass( if (selectorArgumentCount(member.selectorName) != 0) { continue; } - addJsiOverrideMethod( - runtime, bridge, nativeClass, baseClass, member.selectorName, - member.signatureOffset, - (member.flags & metagen::mdMemberReturnOwned) != 0, - getter.asObject(runtime).asFunction(runtime)); + addJsiOverrideMethod(runtime, bridge, nativeClass, baseClass, member.selectorName, + member.signatureOffset, + (member.flags & metagen::mdMemberReturnOwned) != 0, + getter.asObject(runtime).asFunction(runtime)); } } Value setter = descriptor.getProperty(runtime, "set"); - if (propertyMember != nullptr && - setter.isObject() && setter.asObject(runtime).isFunction(runtime) && + if (propertyMember != nullptr && setter.isObject() && + setter.asObject(runtime).isFunction(runtime) && !propertyMember->setterSelectorName.empty()) { addJsiOverrideMethod(runtime, bridge, nativeClass, baseClass, propertyMember->setterSelectorName, @@ -670,11 +623,9 @@ Value extendNativeApiJsiClass( } } - Value exposedMethodsValue = - getObjectPropertyOrUndefined(runtime, options, "exposedMethods"); + Value exposedMethodsValue = getObjectPropertyOrUndefined(runtime, options, "exposedMethods"); if (!exposedMethodsValue.isObject()) { - exposedMethodsValue = - getObjectPropertyOrUndefined(runtime, methods, "ObjCExposedMethods"); + exposedMethodsValue = getObjectPropertyOrUndefined(runtime, methods, "ObjCExposedMethods"); } if (exposedMethodsValue.isObject()) { Object exposedMethods = exposedMethodsValue.asObject(runtime); @@ -685,8 +636,7 @@ Value extendNativeApiJsiClass( continue; } std::string selectorName = selectorValue.asString(runtime).utf8(runtime); - Value descriptorValue = - getObjectPropertyOrUndefined(runtime, exposedMethods, selectorName); + Value descriptorValue = getObjectPropertyOrUndefined(runtime, exposedMethods, selectorName); if (!descriptorValue.isObject()) { continue; } @@ -694,45 +644,41 @@ Value extendNativeApiJsiClass( if (!function) { continue; } - auto signature = exposedMethodSignature( - runtime, bridge, selectorName, descriptorValue.asObject(runtime)); - if (signature) { - rememberNativeApiJsiKnownExposedMethod(selectorName, *signature); - addJsiExposedMethod(runtime, bridge, nativeClass, selectorName, - std::move(*signature), std::move(*function)); - } + auto signature = + exposedMethodSignature(runtime, bridge, selectorName, descriptorValue.asObject(runtime)); + if (signature) { + rememberNativeApiJsiKnownExposedMethod(selectorName, *signature); + addJsiExposedMethod(runtime, bridge, nativeClass, selectorName, std::move(*signature), + std::move(*function)); + } } } - Value hasIteratorValue = - getObjectPropertyOrUndefined(runtime, options, "__hasIterator"); + Value hasIteratorValue = getObjectPropertyOrUndefined(runtime, options, "__hasIterator"); if (hasIteratorValue.isBool() && hasIteratorValue.getBool()) { class_addProtocol(nativeClass, @protocol(NSFastEnumeration)); if (const char* encoding = nativeApiJsiFastEnumerationEncoding()) { - class_replaceMethod( - nativeClass, - @selector(countByEnumeratingWithState:objects:count:), - reinterpret_cast(nativeApiJsiSymbolIteratorCountByEnumerating), - encoding); + class_replaceMethod(nativeClass, @selector(countByEnumeratingWithState:objects:count:), + reinterpret_cast(nativeApiJsiSymbolIteratorCountByEnumerating), + encoding); } } objc_registerClassPair(nativeClass); - NativeApiSymbol newSymbol = baseSymbol; - newSymbol.name = className; - newSymbol.runtimeName = className; - newSymbol.superclassOffset = baseSymbol.offset; - return makeNativeClassValue(runtime, bridge, std::move(newSymbol)); - } + NativeApiSymbol newSymbol = baseSymbol; + newSymbol.name = className; + newSymbol.runtimeName = className; + newSymbol.superclassOffset = baseSymbol.offset; + return makeNativeClassValue(runtime, bridge, std::move(newSymbol)); +} -Value invokeNativeApiJsiBaseMethod( - Runtime& runtime, const std::shared_ptr& bridge, - const Value* args, size_t count) { - if (count < 3 || !args[0].isObject() || !args[1].isObject() || - !args[2].isString()) { - throw facebook::jsi::JSError( - runtime, "__invokeBase expects base class, receiver, and member name."); +Value invokeNativeApiJsiBaseMethod(Runtime& runtime, + const std::shared_ptr& bridge, + const Value* args, size_t count) { + if (count < 3 || !args[0].isObject() || !args[1].isObject() || !args[2].isString()) { + throw facebook::jsi::JSError(runtime, + "__invokeBase expects base class, receiver, and member name."); } Class baseClass = classFromJsiValue(runtime, args[0]); @@ -745,45 +691,38 @@ Value invokeNativeApiJsiBaseMethod( throw facebook::jsi::JSError(runtime, "__invokeBase receiver is not native."); } - id receiver = - receiverObject.getHostObject(runtime)->object(); + id receiver = receiverObject.getHostObject(runtime)->object(); std::string memberName = args[2].asString(runtime).utf8(runtime); size_t actualArgc = count - 3; NativeApiSymbol baseSymbol = runtimeSymbolForClass(bridge, baseClass); const auto& members = bridge->membersForClass(baseSymbol); - const NativeApiMember* member = - selectMethodMember(members, memberName, false, actualArgc); + const NativeApiMember* member = selectMethodMember(members, memberName, false, actualArgc); if (member == nullptr) { if (const NativeApiMember* propertyMember = selectWritablePropertyMember(members, memberName, false)) { if (actualArgc == 0) { - Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); - return callObjCSelector(runtime, bridge, receiver, false, - propertyMember->selectorName, propertyMember, - nullptr, 0, dispatchClass); + Class dispatchClass = dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); + return callObjCSelector(runtime, bridge, receiver, false, propertyMember->selectorName, + propertyMember, nullptr, 0, dispatchClass); } if (actualArgc == 1 && !propertyMember->setterSelectorName.empty() && !propertyMember->readonly) { - Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); + Class dispatchClass = dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); NativeApiMember setterMember = *propertyMember; setterMember.selectorName = propertyMember->setterSelectorName; setterMember.signatureOffset = propertyMember->setterSignatureOffset; - return callObjCSelector(runtime, bridge, receiver, false, - setterMember.selectorName, &setterMember, - args + 3, actualArgc, dispatchClass); + return callObjCSelector(runtime, bridge, receiver, false, setterMember.selectorName, + &setterMember, args + 3, actualArgc, dispatchClass); } } } if (member == nullptr) { - throw facebook::jsi::JSError( - runtime, "Objective-C base selector is not available: " + memberName); + throw facebook::jsi::JSError(runtime, + "Objective-C base selector is not available: " + memberName); } - Class dispatchClass = - dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); - return callObjCSelector(runtime, bridge, receiver, false, member->selectorName, - member, args + 3, actualArgc, dispatchClass); + Class dispatchClass = dispatchSuperclassForJsiDerivedReceiver(receiver, baseClass); + return callObjCSelector(runtime, bridge, receiver, false, member->selectorName, member, args + 3, + actualArgc, dispatchClass); }