From 4be0bb3a308bcdd7f41ad4f1ceeda525a10f7e36 Mon Sep 17 00:00:00 2001 From: Aparajita Fishman Date: Sat, 4 Jun 2011 18:03:53 -0700 Subject: [PATCH] Load userland NS classes from the main bundle as well as frameworks, and temporarily change the class of NSSwapper classes to the target class when calling KVC methods. --- Tools/nib2cib/Converter.j | 12 +++++----- Tools/nib2cib/NSClassSwapper.j | 33 ++++++++++++++++++++++++---- Tools/nib2cib/Nib2Cib.j | 40 +++++++++++++++++++++------------- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/Tools/nib2cib/Converter.j b/Tools/nib2cib/Converter.j index 61fdc6f08..654c5dd0a 100644 --- a/Tools/nib2cib/Converter.j +++ b/Tools/nib2cib/Converter.j @@ -40,12 +40,12 @@ ConverterConversionException = @"ConverterConversionException"; @implementation Converter : CPObject { - CPString inputPath @accessors(readonly); - CPString outputPath @accessors; - CPString resourcesPath @accessors; - NibFormat format @accessors(readonly); - CPArray themes @accessors(readonly); - CPArray frameworkNSClasses @accessors; + CPString inputPath @accessors(readonly); + CPString outputPath @accessors; + CPString resourcesPath @accessors; + NibFormat format @accessors(readonly); + CPArray themes @accessors(readonly); + CPArray userNSClasses @accessors; } + (Converter)sharedConverter diff --git a/Tools/nib2cib/NSClassSwapper.j b/Tools/nib2cib/NSClassSwapper.j index 4eebb9a8f..91e26c765 100644 --- a/Tools/nib2cib/NSClassSwapper.j +++ b/Tools/nib2cib/NSClassSwapper.j @@ -39,10 +39,10 @@ var _CPCibClassSwapperClassNameKey = @"_CPCibClassSwapperClassNameKey", if (!swapperClass) { - // If this is a framework NS class, call its KVC methods directly + // If this is a userland NS class, call its KVC methods directly var nsClass = nil; - if ([[[Converter sharedConverter] frameworkNSClasses] containsObject:aClassName]) + if ([[[Converter sharedConverter] userNSClasses] containsObject:aClassName]) nsClass = objj_lookUpClass("NS_" + aClassName); var originalClass = nsClass || objj_lookUpClass(anOriginalClassName); @@ -51,9 +51,26 @@ var _CPCibClassSwapperClassNameKey = @"_CPCibClassSwapperClassNameKey", objj_registerClassPair(swapperClass); + /* + When calling userland KVC methods, they should think that the class is + the NS class (not the swapper class) so that they are in their userland space, + not in AppKit space. For example, this ensures that bundleForClass:[self class] will work correctly. + We can accomplish this safely by changing the class of self temporarily and sending directly + to self instead of to super. This swizzle is safe because NSClassSwapper and _CPCibClassSwapper + do not add any ivars. + */ + class_addMethod(swapperClass, @selector(initWithCoder:), function(self, _cmd, aCoder) { - self = objj_msgSendSuper({super_class:originalClass, receiver:self}, _cmd, aCoder); + if (nsClass) + { + // Switch to userland temporarily + self.isa = nsClass; + self = objj_msgSend(self, _cmd, aCoder); + self.isa = swapperClass; + } + else + self = objj_msgSendSuper({super_class:originalClass, receiver:self}, _cmd, aCoder); if (self) { @@ -73,7 +90,15 @@ var _CPCibClassSwapperClassNameKey = @"_CPCibClassSwapperClassNameKey", class_addMethod(swapperClass, @selector(encodeWithCoder:), function(self, _cmd, aCoder) { - objj_msgSendSuper({super_class:originalClass, receiver:self}, _cmd, aCoder); + if (nsClass) + { + // Switch to userland temporarily + self.isa = nsClass; + objj_msgSend(self, _cmd, aCoder); + self.isa = swapperClass; + } + else + objj_msgSendSuper({super_class:originalClass, receiver:self}, _cmd, aCoder); // If this is a custom NS class, lookup its archiver class so that // the correct class is swapped during unarchiving. diff --git a/Tools/nib2cib/Nib2Cib.j b/Tools/nib2cib/Nib2Cib.j index afcd94a52..b3e2604d8 100644 --- a/Tools/nib2cib/Nib2Cib.j +++ b/Tools/nib2cib/Nib2Cib.j @@ -52,7 +52,7 @@ var FILE = require("file"), CPString appDirectory; CPString resourcesDirectory; CPDictionary infoPlist; - CPArray frameworkNSClasses; + CPArray userNSClasses; } - (id)initWithArgs:(CPArray)theArgs @@ -67,7 +67,7 @@ var FILE = require("file"), appDirectory = @""; resourcesDirectory = @""; infoPlist = [CPDictionary dictionary]; - frameworkNSClasses = []; + userNSClasses = []; } return self; @@ -153,7 +153,8 @@ var FILE = require("file"), var loadFrameworksCallback = function() { - [converter setFrameworkNSClasses:frameworkNSClasses]; + [self loadNSClassesFromBundle:[CPBundle mainBundle]]; + [converter setUserNSClasses:userNSClasses]; [converter convert]; }; @@ -573,18 +574,7 @@ var FILE = require("file"), [frameworkBundle loadWithDelegate:nil]; [self setLogLevel:verbosity]; - // See if the framework defines NS classes - var nsClasses = [frameworkBundle objectForInfoDictionaryKey:@"IBClasses"] || []; - - for (var i = 0; i < nsClasses.length; ++i) - { - var filename = "NS_" + nsClasses[i] + ".j"; - - objj_importFile(FILE.join(frameworkPath, filename), YES); - CPLog.debug("Imported framework NS class: %s", filename); - - frameworkNSClasses.push(nsClasses[i]); - } + [self loadNSClassesFromBundle:frameworkBundle]; } finally { @@ -597,6 +587,26 @@ var FILE = require("file"), aCallback(); } +- (void)loadNSClassesFromBundle:(CPBundle)aBundle +{ + // See if the framework defines NS classes + var nsClasses = [aBundle objectForInfoDictionaryKey:@"IBClasses"] || [], + bundlePath = [aBundle bundlePath]; + + for (var i = 0; i < nsClasses.length; ++i) + { + if (userNSClasses.indexOf(nsClasses[i]) >= 0) + continue; + + var path = FILE.join(bundlePath, "NS_" + nsClasses[i] + ".j"); + + objj_importFile(path, YES); + CPLog.debug("Imported NS class: %s", path); + + userNSClasses.push(nsClasses[i]); + } +} + - (CPArray)getThemeList:(JSObject)options { var defaultTheme = options.defaultTheme;