Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 113 lines (94 sloc) 5.257 kb
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
#import <objc/runtime.h>
#import "JSObjectionUtils.h"

static NSString *const JSObjectionException = @"JSObjectionException";

NSString *const JSObjectionInitializerKey = @"initializer";
NSString *const JSObjectionDefaultArgumentsKey = @"arguments";

static JSObjectionPropertyInfo FindClassOrProtocolForProperty(objc_property_t property) {
    NSString *attributes = [NSString stringWithCString: property_getAttributes(property) encoding: NSASCIIStringEncoding];
    NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding];

    NSRange startRange = [attributes rangeOfString:@"T@\""];
    if (startRange.location == NSNotFound) {
        @throw [NSException exceptionWithName:JSObjectionException reason:[NSString stringWithFormat:@"Unable to determine class type for property declaration: '%@'", propertyName] userInfo:nil];
    }

    NSString *startOfClassName = [attributes substringFromIndex:startRange.length];
    NSRange endRange = [startOfClassName rangeOfString:@"\""];

    if (endRange.location == NSNotFound) {
        @throw [NSException exceptionWithName:JSObjectionException reason:[NSString stringWithFormat:@"Unable to determine class type for property declaration: '%@'", propertyName] userInfo:nil];
    }

    NSString *classOrProtocolName = [startOfClassName substringToIndex:endRange.location];
    id classOrProtocol = nil;
    JSObjectionPropertyInfo propertyInfo;

    if ([classOrProtocolName hasPrefix:@"<"] && [classOrProtocolName hasSuffix:@">"]) {
        classOrProtocolName = [classOrProtocolName stringByReplacingOccurrencesOfString:@"<" withString:@""];
        classOrProtocolName = [classOrProtocolName stringByReplacingOccurrencesOfString:@">" withString:@""];
        classOrProtocol = objc_getProtocol([classOrProtocolName UTF8String]);
        propertyInfo.type = JSObjectionTypeProtocol;
    } else {
        classOrProtocol = NSClassFromString(classOrProtocolName);
        propertyInfo.type = JSObjectionTypeClass;
    }

    if(!classOrProtocol) {
        @throw [NSException exceptionWithName:JSObjectionException reason:[NSString stringWithFormat:@"Unable get class for name '%@' for property '%@'", classOrProtocolName, propertyName] userInfo:nil];
    }
    propertyInfo.value = classOrProtocol;

    return propertyInfo;
}

static NSSet* BuildDependenciesForClass(Class klass, NSSet *requirements) {
    Class superClass = class_getSuperclass([klass class]);
    if([superClass respondsToSelector:@selector(objectionRequires)]) {
        NSSet *parentsRequirements = [superClass performSelector:@selector(objectionRequires)];
        NSMutableSet *dependencies = [NSMutableSet setWithSet:parentsRequirements];
        [dependencies unionSet:requirements];
        requirements = dependencies;
    }
    return requirements;
}

static NSDictionary* BuildInitializer(SEL selector, NSArray *defaultArguments) {
    return [NSDictionary dictionaryWithObjectsAndKeys:
                NSStringFromSelector(selector), JSObjectionInitializerKey,
                defaultArguments, JSObjectionDefaultArgumentsKey
            , nil];
}

static NSArray* TransformVariadicArgsToArray(va_list va_arguments) {
    NSMutableArray *arguments = [NSMutableArray array];
    
    id object;
    while ((object = va_arg( va_arguments, id ))) {
        [arguments addObject:object];
    }
    
    return [[arguments copy] autorelease];
}

static objc_property_t GetProperty(Class klass, NSString *propertyName) {
    objc_property_t property = class_getProperty(klass, (const char *)[propertyName UTF8String]);
    if (property == NULL) {
        @throw [NSException exceptionWithName:JSObjectionException reason:[NSString stringWithFormat:@"Unable to find property declaration: '%@'", propertyName] userInfo:nil];
    }
    return property;
}


static id BuildObjectWithInitializer(Class klass, SEL initializer, NSArray *arguments) {
    id instance = [klass alloc];
    NSMethodSignature *signature = [klass instanceMethodSignatureForSelector:initializer];
    if (signature) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:instance];
        [invocation setSelector:initializer];
        for (int i = 0; i < arguments.count; i++) {
            id argument = [arguments objectAtIndex:i];
            [invocation setArgument:&argument atIndex:i + 2];
        }
        [invocation invoke];
        [invocation getReturnValue:&instance];
        return [instance autorelease];
    } else {
        [instance release];
        @throw [NSException exceptionWithName:JSObjectionException reason:[NSString stringWithFormat:@"Could not find initializer '%@' on %@", NSStringFromSelector(initializer), NSStringFromClass(klass)] userInfo:nil];
    }
    return nil;
}

const struct JSObjectionUtils JSObjectionUtils = {
    .findClassOrProtocolForProperty = FindClassOrProtocolForProperty,
    .propertyForClass = GetProperty,
    .buildDependenciesForClass = BuildDependenciesForClass,
    .buildInitializer = BuildInitializer,
    .transformVariadicArgsToArray = TransformVariadicArgsToArray,
    .buildObjectWithInitializer = BuildObjectWithInitializer
};
Something went wrong with that request. Please try again.