|
| 1 | +// |
| 2 | +// HotFix.m |
| 3 | +// FIX |
| 4 | +// |
| 5 | +// Created by xgf on 2018/6/20. |
| 6 | +// Copyright © 2018年 xgf. All rights reserved. |
| 7 | +// |
| 8 | + |
| 9 | +#import "HotFix.h" |
| 10 | +#import <objc/runtime.h> |
| 11 | +#import <JavaScriptCore/JavaScriptCore.h> |
| 12 | +#import <Aspects/Aspects.h> |
| 13 | + |
| 14 | +static HotFix *fixManager = nil; |
| 15 | + |
| 16 | +@interface HotFix() |
| 17 | + |
| 18 | +@property(nonatomic, copy)JSContext *context; |
| 19 | + |
| 20 | +@end |
| 21 | + |
| 22 | +@implementation HotFix |
| 23 | + |
| 24 | ++ (instancetype)shared { |
| 25 | + static dispatch_once_t onceToken; |
| 26 | + dispatch_once(&onceToken, ^{ |
| 27 | + fixManager = [[HotFix alloc] init]; |
| 28 | + }); |
| 29 | + return fixManager; |
| 30 | +} |
| 31 | +- (instancetype)init { |
| 32 | + if(self = [super init]) { |
| 33 | + [self setup]; |
| 34 | + } |
| 35 | + return self; |
| 36 | +} |
| 37 | +- (JSContext *)context { |
| 38 | + if(!_context) { |
| 39 | + _context = [[JSContext alloc] init]; |
| 40 | + _context.exceptionHandler = ^(JSContext *context, JSValue *exception) { |
| 41 | + NSLog(@"Ohooo: %@",exception); |
| 42 | + }; |
| 43 | + } |
| 44 | + return _context; |
| 45 | +} |
| 46 | +- (void)fix:(NSString *)js { |
| 47 | + [self.context evaluateScript:js]; |
| 48 | +} |
| 49 | +- (void)fixWithMethod:(BOOL)isClassMethod aspectionOptions:(AspectOptions)option instanceName:(NSString *)instanceName selectorName:(NSString *)selectorName fixImpl:(JSValue *)fixImpl { |
| 50 | + Class klass = NSClassFromString(instanceName); |
| 51 | + if (isClassMethod) { |
| 52 | + klass = object_getClass(klass); |
| 53 | + } |
| 54 | + SEL sel = NSSelectorFromString(selectorName); |
| 55 | + [klass aspect_hookSelector:sel withOptions:option usingBlock:^(id<AspectInfo> aspectInfo){ |
| 56 | + [fixImpl callWithArguments:@[aspectInfo.instance, aspectInfo.originalInvocation, aspectInfo.arguments]]; |
| 57 | + } error:nil]; |
| 58 | +} |
| 59 | + |
| 60 | +- (id)runClassWithClassName:(NSString *)className selector:(NSString *)selector obj1:(id)obj1 obj2:(id)obj2 { |
| 61 | + Class klass = NSClassFromString(className); |
| 62 | + #pragma clang diagnostic push |
| 63 | + #pragma clang diagnostic ignored "-Warc-performSelector-leaks" |
| 64 | + return [klass performSelector:NSSelectorFromString(selector) withObject:obj1 withObject:obj2]; |
| 65 | + #pragma clang diagnostic pop |
| 66 | +} |
| 67 | + |
| 68 | +- (id)runInstanceWithInstance:(id)instance selector:(NSString *)selector obj1:(id)obj1 obj2:(id)obj2 { |
| 69 | + #pragma clang diagnostic push |
| 70 | + #pragma clang diagnostic ignored "-Warc-performSelector-leaks" |
| 71 | + return [instance performSelector:NSSelectorFromString(selector) withObject:obj1 withObject:obj2]; |
| 72 | + #pragma clang diagnostic pop |
| 73 | +} |
| 74 | +- (void)setup { |
| 75 | + __weak typeof(self) wkself = self; |
| 76 | + self.context[@"fixInstanceMethodBefore"] = ^(NSString *instanceName, NSString *selectorName, JSValue *fixImpl) { |
| 77 | + [wkself fixWithMethod:NO aspectionOptions:AspectPositionBefore instanceName:instanceName selectorName:selectorName fixImpl:fixImpl]; |
| 78 | + }; |
| 79 | + [self context][@"fixInstanceMethodReplace"] = ^(NSString *instanceName, NSString *selectorName, JSValue *fixImpl) { |
| 80 | + [wkself fixWithMethod:NO aspectionOptions:AspectPositionInstead instanceName:instanceName selectorName:selectorName fixImpl:fixImpl]; |
| 81 | + }; |
| 82 | + [self context][@"fixInstanceMethodAfter"] = ^(NSString *instanceName, NSString *selectorName, JSValue *fixImpl) { |
| 83 | + [wkself fixWithMethod:NO aspectionOptions:AspectPositionAfter instanceName:instanceName selectorName:selectorName fixImpl:fixImpl]; |
| 84 | + }; |
| 85 | + [self context][@"fixClassMethodBefore"] = ^(NSString *instanceName, NSString *selectorName, JSValue *fixImpl) { |
| 86 | + [wkself fixWithMethod:YES aspectionOptions:AspectPositionBefore instanceName:instanceName selectorName:selectorName fixImpl:fixImpl]; |
| 87 | + }; |
| 88 | + [self context][@"fixClassMethodReplace"] = ^(NSString *instanceName, NSString *selectorName, JSValue *fixImpl) { |
| 89 | + [wkself fixWithMethod:YES aspectionOptions:AspectPositionInstead instanceName:instanceName selectorName:selectorName fixImpl:fixImpl]; |
| 90 | + }; |
| 91 | + [self context][@"fixClassMethodAfter"] = ^(NSString *instanceName, NSString *selectorName, JSValue *fixImpl) { |
| 92 | + [wkself fixWithMethod:YES aspectionOptions:AspectPositionAfter instanceName:instanceName selectorName:selectorName fixImpl:fixImpl]; |
| 93 | + }; |
| 94 | + [self context][@"runClassWithNoParamter"] = ^id(NSString *className, NSString *selectorName) { |
| 95 | + return [wkself runClassWithClassName:className selector:selectorName obj1:nil obj2:nil]; |
| 96 | + }; |
| 97 | + [self context][@"runClassWith1Paramter"] = ^id(NSString *className, NSString *selectorName, id obj1) { |
| 98 | + return [wkself runClassWithClassName:className selector:selectorName obj1:obj1 obj2:nil]; |
| 99 | + }; |
| 100 | + [self context][@"runClassWith2Paramters"] = ^id(NSString *className, NSString *selectorName, id obj1, id obj2) { |
| 101 | + return [wkself runClassWithClassName:className selector:selectorName obj1:obj1 obj2:obj2]; |
| 102 | + }; |
| 103 | + [self context][@"runVoidClassWithNoParamter"] = ^(NSString *className, NSString *selectorName) { |
| 104 | + [wkself runClassWithClassName:className selector:selectorName obj1:nil obj2:nil]; |
| 105 | + }; |
| 106 | + [self context][@"runVoidClassWith1Paramter"] = ^(NSString *className, NSString *selectorName, id obj1) { |
| 107 | + [wkself runClassWithClassName:className selector:selectorName obj1:obj1 obj2:nil]; |
| 108 | + }; |
| 109 | + [self context][@"runVoidClassWith2Paramters"] = ^(NSString *className, NSString *selectorName, id obj1, id obj2) { |
| 110 | + [wkself runClassWithClassName:className selector:selectorName obj1:obj1 obj2:obj2]; |
| 111 | + }; |
| 112 | + [self context][@"runInstanceWithNoParamter"] = ^id(id instance, NSString *selectorName) { |
| 113 | + return [wkself runInstanceWithInstance:instance selector:selectorName obj1:nil obj2:nil]; |
| 114 | + }; |
| 115 | + [self context][@"runInstanceWith1Paramter"] = ^id(id instance, NSString *selectorName, id obj1) { |
| 116 | + return [wkself runInstanceWithInstance:instance selector:selectorName obj1:obj1 obj2:nil]; |
| 117 | + }; |
| 118 | + [self context][@"runInstanceWith2Paramters"] = ^id(id instance, NSString *selectorName, id obj1, id obj2) { |
| 119 | + return [wkself runInstanceWithInstance:instance selector:selectorName obj1:obj1 obj2:obj2]; |
| 120 | + }; |
| 121 | + [self context][@"runVoidInstanceWithNoParamter"] = ^(id instance, NSString *selectorName) { |
| 122 | + [wkself runInstanceWithInstance:instance selector:selectorName obj1:nil obj2:nil]; |
| 123 | + }; |
| 124 | + [self context][@"runVoidInstanceWith1Paramter"] = ^(id instance, NSString *selectorName, id obj1) { |
| 125 | + [wkself runInstanceWithInstance:instance selector:selectorName obj1:obj1 obj2:nil]; |
| 126 | + }; |
| 127 | + [self context][@"runVoidInstanceWith2Paramters"] = ^(id instance, NSString *selectorName, id obj1, id obj2) { |
| 128 | + [wkself runInstanceWithInstance:instance selector:selectorName obj1:obj1 obj2:obj2]; |
| 129 | + }; |
| 130 | + [self context][@"runInvocation"] = ^(NSInvocation *invocation) { |
| 131 | + [invocation invoke]; |
| 132 | + }; |
| 133 | + [[self context] evaluateScript:@"var console = {}"]; |
| 134 | + [self context][@"console"][@"log"] = ^(id message) { |
| 135 | + NSLog(@"Javascript log: %@",message); |
| 136 | + }; |
| 137 | +} |
| 138 | + |
| 139 | +@end |
0 commit comments