From bd4cd6ea5d0313bcd2e47ce95e18465e019d3f8c Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 19 Sep 2016 04:43:06 -0700 Subject: [PATCH] Fix non-exported module warnings for superclasses Reviewed By: mhorowitz Differential Revision: D3841655 fbshipit-source-id: b855f9bef6c53a0964c59e1977e5eb23452edce3 --- React/Base/RCTBatchedBridge.m | 34 +++--------------------- React/Base/RCTBridge+Private.h | 6 +++++ React/Base/RCTBridge.m | 47 ++++++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index d27576916..952e748c8 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -40,8 +40,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { RCTBridgeFieldCallID, }; -RCT_EXTERN NSArray *RCTGetModuleClasses(void); - @implementation RCTBatchedBridge { BOOL _wasBatchActive; @@ -265,38 +263,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id)dele extraModules = self.moduleProvider(); } +#if RCT_DEBUG static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - if (RCT_DEBUG && !RCTRunningInTestEnvironment()) { - // Check for unexported modules - Class *classes; - unsigned int classCount; - classes = objc_copyClassList(&classCount); - - NSMutableSet *moduleClasses = [NSMutableSet new]; - [moduleClasses addObjectsFromArray:RCTGetModuleClasses()]; - [moduleClasses addObjectsFromArray:[extraModules valueForKeyPath:@"class"]]; - - for (unsigned int i = 0; i < classCount; i++) - { - Class cls = classes[i]; - Class superclass = cls; - while (superclass) - { - if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule))) - { - if (![moduleClasses containsObject:cls] && - ![cls respondsToSelector:@selector(moduleName)]) { - RCTLogWarn(@"Class %@ was not exported. Did you forget to use " - "RCT_EXPORT_MODULE()?", cls); - } - break; - } - superclass = class_getSuperclass(superclass); - } - } - } + RCTVerifyAllModulesExported(extraModules); }); +#endif NSMutableArray *moduleClassesByID = [NSMutableArray new]; NSMutableArray *moduleDataByID = [NSMutableArray new]; diff --git a/React/Base/RCTBridge+Private.h b/React/Base/RCTBridge+Private.h index 249d198dd..6d74f5792 100644 --- a/React/Base/RCTBridge+Private.h +++ b/React/Base/RCTBridge+Private.h @@ -12,6 +12,12 @@ @class RCTModuleData; @protocol RCTJavaScriptExecutor; +RCT_EXTERN NSArray *RCTGetModuleClasses(void); + +#if RCT_DEBUG +RCT_EXTERN void RCTVerifyAllModulesExported(NSArray *extraModules); +#endif + @interface RCTBridge () // Private designated initializer diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index ff2404c9e..35c4fb153 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import "RCTBridge.h" #import "RCTBridge+Private.h" #import @@ -27,7 +28,6 @@ NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailT NSString *const RCTDidInitializeModuleNotification = @"RCTDidInitializeModuleNotification"; static NSMutableArray *RCTModuleClasses; -NSArray *RCTGetModuleClasses(void); NSArray *RCTGetModuleClasses(void) { return RCTModuleClasses; @@ -58,7 +58,7 @@ void RCTRegisterModule(Class moduleClass) */ NSString *RCTBridgeModuleNameForClass(Class cls) { -#if RCT_DEV +#if RCT_DEBUG RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)], @"Bridge module `%@` does not conform to RCTBridgeModule", cls); #endif @@ -73,6 +73,49 @@ NSString *RCTBridgeModuleNameForClass(Class cls) return name; } +#if RCT_DEBUG +void RCTVerifyAllModulesExported(NSArray *extraModules) +{ + // Check for unexported modules + unsigned int classCount; + Class *classes = objc_copyClassList(&classCount); + + NSMutableSet *moduleClasses = [NSMutableSet new]; + [moduleClasses addObjectsFromArray:RCTGetModuleClasses()]; + [moduleClasses addObjectsFromArray:[extraModules valueForKeyPath:@"class"]]; + + for (unsigned int i = 0; i < classCount; i++) { + Class cls = classes[i]; + Class superclass = cls; + while (superclass) { + if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule))) { + if ([moduleClasses containsObject:cls]) { + break; + } + + // Verify it's not a super-class of one of our moduleClasses + BOOL isModuleSuperClass = NO; + for (Class moduleClass in moduleClasses) { + if ([moduleClass isSubclassOfClass:cls]) { + isModuleSuperClass = YES; + break; + } + } + if (isModuleSuperClass) { + break; + } + + RCTLogWarn(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", cls); + break; + } + superclass = class_getSuperclass(superclass); + } + } + + free(classes); +} +#endif + @implementation RCTBridge { NSURL *_delegateBundleURL;