Giter Site home page Giter Site logo

ypliang19 / mango Goto Github PK

View Code? Open in Web Editor NEW
1.2K 21.0 213.0 1.33 MB

MangoFix is a DSL which syntax is very similar to Objective-C,MangoFix is also an iOS App hotfix SDK. You can use MangoFix method replace any Objective-C or Swift method.

License: MIT License

Objective-C 63.67% Lex 1.63% Yacc 11.38% C 13.97% Ruby 0.31% Modula-3 6.70% Swift 2.35%
hotfix mangofix jspatch ios swift-hotfix

mango's People

Contributors

ypliang19 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mango's Issues

关于 super 直接使用 objc_msgSendSuper 实现的问题

/* Basic Messaging Primitives
 *
 * On some architectures, use objc_msgSend_stret for some struct return types.
 * On some architectures, use objc_msgSend_fpret for some float return types.
 * On some architectures, use objc_msgSend_fp2ret for some float return types.
 *
 * These functions must be cast to an appropriate function pointer type 
 * before being called. 
 */

根据苹果关于 objc_msgSend 的注释,对于部分返回 struct 的方法,是否应该使用 objc_msgSendSuper_stret ?

关于类型转换的问题

为什么字符串和NSNumber不能转换int, float和double这种基本类型。@"123".intValue()打印出来的是

关于RSA加密的问题

我在升级到1.4的时候发现,MFContext 创建需要传的是私钥,而公钥用来加密,那就是需要把私钥保存到客户端本地,相当于公开,而公钥是私有的。
而据我所知,OpenSSL 生成的私钥是可以直接提取公钥的,客户端就可以同时获取到私钥和公钥,所以这样的加密是基本没有意义的。
而根据我的查询JSPatch 就是使用私钥加密MD5来保证安全性的,https://jspatch.com/Docs/security,也可以看看知乎相关讨论 https://www.zhihu.com/question/25912483
在我看来RSA的算法中私钥公钥其实一样,OpenSSL 区分为公钥私钥后才决定了私钥为什么要私有。所以客户端应该保存的是公钥,用来解密,而私钥是私有的用来加密。
感谢你开发的工具,确实很好用,希望能够完善这个安全问题,谢谢

How to use it?

Hello:
I looked at the documentation and the demo, but I still didn't understand how to perform the hotfix operation. After I looked at the loading script in the delegate, it seemed that the demo example ran without any difference.

How to use it?

有几个问题

  • 是否可以支持static、const修饰符
  • dispatch_after、dispatch_once两个常用方法无法使用
  • 为对象添加属性时,如果属性是基本类型的话会无法赋值,比如下面的代码
class SuperMyController:UIViewController{
@property (assign, nonatomic) int count;
- (void)viewDidLoad {
    super.viewDidLoad();

    self.count = 12;
    NSLog(@"count=" + self.count); //输出 count=0
}

Grammar conflicts

I've just added mango.[yl] to https://mingodad.github.io/parsertl-playground/playground/ an Yacc/Lex compatible online editor/tester (select MangoFix parser from Examples then click Parse to see a parser tree for the content in Input source editor).

I fixed some conflicts there see definition, annotation_list, annotation_list_opt, declare_struct ... , but still there is some conflicts:

state 48 SHIFT (type_specifier -> IDENTIFIER . ASTERISK)/REDUCE (primary_expression -> IDENTIFIER) conflict.
state 192 SHIFT (type_specifier -> IDENTIFIER . ASTERISK)/REDUCE (primary_expression -> IDENTIFIER) conflict.
state 234:COLON REDUCE (selector_1 -> IDENTIFIER)/REDUCE (primary_expression -> primary_expression DOT IDENTIFIER) conflict.
state 234:LP REDUCE (selector_1 -> IDENTIFIER)/REDUCE (primary_expression -> primary_expression DOT IDENTIFIER) conflict.
state 235:COLON REDUCE (selector_1 -> key_work_identifier)/REDUCE (primary_expression -> primary_expression DOT key_work_identifier) conflict.
state 235:LP REDUCE (selector_1 -> key_work_identifier)/REDUCE (primary_expression -> primary_expression DOT key_work_identifier) conflict.

The rule primary_expression is too broad and as it's now accept things like:

    12("str");
    "str"(23.5);
    nil("str");
    NULL("str");

I hope https://mingodad.github.io/parsertl-playground/playground/ can help debug/develop/test/document this project grammar.

The repository is here https://github.com/mingodad/parsertl-playground .

Any feedback is welcome !

和jspatch除了dsl区别在哪里?

看了作者关于原理的介绍,除了一个使用jsbridge一个使用dsl解释器去解析,到oc层面都是用的runtime机制,包括创建函数等好像和jspatch一是一样的,那么核心区别在哪里呢?

之前我们用了jspatch,妥妥地被拒了,jspatch有自己的分发平台,使用mango得自己搭建服务器,接入成本还是很高的,所以希望能够详细了解下,如果被拒的风险依旧很大的话就不适用我们上架的这个情况

super.viewDidLoad() 不加括号会崩溃

@YPLiang19 看文档里有写 OC 无参方法的调用可以省略后面的括号,但是super.viewDidLoad这种写法会崩溃,貌似死循环:

class SuperMyController:UIViewController{

  • (void)viewDidLoad {
    super.viewDidLoad;
    }

image

image

审核能过吗

假如只添加这个库,然后随便写一个修改某个页面的颜色,这样可以通过审核吗

脚本带 if (xx && xx) 或 if (xx || xx) 时可能存在内存泄漏

在脚本里写这样的代码,会导致内存泄漏:

- (void)test {
    if (info && info.xx) {
        //...
    }
}

而换成这样就没问题:

- (void)test {
    if (info) {
        if (info.xx) {
            //...
        }
    }
}

可以在 MangoFixDemo 工程中复现这个问题。原生代码如下:

// ViewController.m
@interface MGTestInfo : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation MGTestInfo

- (void)dealloc {
    NSLog(@"MGTestInfo dealloc, name: %@", self.name);
}

@end

@implementation ViewController

//......

- (void)notificationExample{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onTestChanged:) name:@"kNotificationTestChange" object:nil];
    });
    
    static int index = 0;
    MGTestInfo *info = [MGTestInfo new];
    info.name = [NSString stringWithFormat:@"name_%d", ++ index];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationTestChange" object:info];
}

- (void)onTestChanged:(NSNotification *)notification{
    self.resultView.text = [notification.object name];
}

//......

脚本代码:

// demo.mg
class ViewController:UIViewController {

    - (void)onTestChanged:(NSNotification*)noti {
        MGTestInfo *info = noti.object;
        if (info && info.name.isEqualToString:(@"name_2")) {
            return;
        }
        self.ORGonTestChanged:(noti);
    }
}

运行之后,没法调到 [MGTestInfo dealloc] 方法。如果分开写两个 if 则没问题。

原因可能是这里,eval_logic_and_expression,其实现如下:

static void eval_logic_and_expression(MFInterpreter *inter, MFScopeChain *scope, MFBinaryExpression *expr){
	eval_expression(inter, scope, expr.left);
	MFValue *leftValue = [inter.stack peekStack:0];
	MFValue *resultValue = [MFValue new];
	resultValue.type = mf_create_type_specifier(MF_TYPE_BOOL);
	if (!leftValue.isSubtantial) {
		resultValue.uintValue = NO;
		[inter.stack pop];
	}else{
		eval_expression(inter, scope, expr.right);
		MFValue *rightValue = [inter.stack peekStack:0];
		if (!rightValue.isSubtantial) {
			resultValue.uintValue = NO;
		}else{
			resultValue.uintValue = YES;
		}
		[inter.stack pop];
	}
	[inter.stack push:resultValue];
}

可以看到,第一句 eval_expression(inter, scope, expr.left); 执行之后,可能没有配对地调用 [inter.stack pop];eval_logic_or_expression 也存在同样的问题。

如果在 else 里面最后加一句 [inter.stack pop]; 则问题解决。

请问一下,这里是真的少调用了 pop,还是有其他我不了解的原因故意没加那句 pop?

原IMP丢弃,stackBlock crash , 子类调用不到被修复的父类方法

首先感谢作者实现了一种全新的hotfix方式,点赞!
我在使用过程中发现了几个问题,希望作者能优化下,让mango越来越棒!

问题一:被修复的类会丢弃原来的IMP

execute.m 原来的处理会丢弃旧的IMP,处理有点粗暴。我将旧IMP绑定到ORIGxxx。


static void replace_method(MANInterpreter *interpreter,Class clazz, MANMethodDefinition *method){
   // 省略其他代码
    //保留原来的IMP
    Class c2 = method.classMethod ? objc_getMetaClass(class_getName(clazz)) : clazz;
    class_replaceMethod(c2, @selector(forwardInvocation:), (IMP)mango_forward_invocation,"v@:@");
    IMP originalIMP =class_replaceMethod(c2, sel, _objc_msgForward, typeEncoding);
    class_addMethod(c2, NSSelectorFromString([NSString stringWithFormat:@"ORIG%@",func.name]), originalIMP, typeEncoding);
	if (needFreeTypeEncoding) {
		free((void *)typeEncoding);
	}
}

之后就可以在DSL中使用self.ORIGxxxx带调用到原来的方法。

问题二:父类修复某一方法,子类使用super调用不到

MANMethodMapTable.h
当子类使用[super xxx] ,传递class实际是子类的class,getMethodMapTableItemWith:sel: 中返回nil,类似调用了空函数。

-(MANMethodMapTableItem *)getMethodMapTableItemWith:(Class)clazz classMethod:(BOOL)classMethod sel:(SEL)sel{
    //需要递归找到所有父类的方法,同时产生新的问题:子类好父类hotfix同一个方法,方法内部使用super时会造成死循环。JSPatch取最顶层IMP调用,虽然没有死循环,但是丢掉了继承调用链。
    Class fixClass = clazz;
    do {
        NSString *index = [NSString stringWithFormat:@"%d_%@_%@,",classMethod,NSStringFromClass(fixClass),NSStringFromSelector(sel)];
        MANMethodMapTableItem* item = _dic[index];
        if (item || [NSStringFromClass(fixClass) isEqualToString:@"NSObject"]) {
            return item;
        }
        fixClass =  class_getSuperclass(fixClass);
    } while (fixClass);
    
    return nil;
    
}

问题三:当修复的方法有参数是stack block时,会造成crash

ARC之后有种写法还是会产生StackBlock:

SubModel* model = [[SubModel alloc] initWithTitle:@"Title" image:@"image"];
   [model log];
   int a = 1;
   void(^block)(void) = ^{  //此时block是 Malloc
       NSLog(@"run block %d" , a);
   };
   [model testBlock:^{  // 此时block是 Stack
       NSLog(@"run block %d" , a);
   }];

DSL中的fix代码

class Model : NSObject {
    -(void)log{
        NSLog(@"Fix Model Log");
        self.ORIGlog();
    }
    -(void)testBlock:(Block)block
    {
        NSLog(@"Fix testBlock");
        self.ORIGtestBlock:(block);
    }
}

Crash :
block

crash

在ARC模式下,会自动插入Stack转Malloc的操作,DSL会丢失这个特性,就出现了上述问题。

我在代码中打了一个补丁,暂时满足需求:
MANValue.h

- (instancetype)initWithCValuePointer:(void *)cValuePointer typeEncoding:(const char *)typeEncoding bridgeTransfer:(BOOL)bridgeTransfer  {
	typeEncoding = removeTypeEncodingPrefix((char *)typeEncoding);
	MANValue *retValue = [[MANValue alloc] init];
	
	switch (*typeEncoding) {
			//省略部分代码
        case '@':{
            retValue.type = man_create_type_specifier(MAN_TYPE_OBJECT);
            if (bridgeTransfer) {
                retValue.objectValue = (__bridge_transfer id)(*(void **)cValuePointer);
            } else if (0 == strcmp(typeEncoding, "@?")) { // **如果是block,将stack变为malloc**
                id block = (__bridge id)(*(void **)cValuePointer);
                block = [block copy];
                retValue.objectValue = block;
            }else{
                retValue.objectValue = (__bridge id)(*(void **)cValuePointer);
            }
            
            break;
        }
		//省略部分代码
	}
	
	return retValue;
}

问题四:内存偶尔有点高

MANInterpreter.h

NSString *currentThread = [[NSThread currentThread] description]; 
修改为
 NSString *currentThread = [NSString stringWithFormat:@"%p",[NSThread currentThread]];
完整代码:
- (MANStack *)stack{
    NSString *currentThread = [NSString stringWithFormat:@"%p",[NSThread currentThread]];
    [_lock lock];
    if (!_stacksDic[currentThread]) {
        _stacksDic[(id)currentThread] = [[MANStack alloc] init];
    }
    MANStack* value = _stacksDic[currentThread];
    [_lock unlock];
    return value;
}

这个修改只能解决一点点问题,还需要作者从全局出发找到优化方法。

测试代码

完整DSL:

class Model : NSObject {
    -(void)log{
        NSLog(@"Fix Model Log");
        self.ORIGlog();
    }
    -(void)testBlock:(Block)block
    {
        NSLog(@"Fix testBlock");
        self.ORIGtestBlock:(block);
    }
}

Model.h

@interface Model : NSObject
@property(nonatomic,copy) NSString* title;
@property(nonatomic,copy) NSString* image;
-(instancetype)initWithTitle:(NSString*)t image:(NSString *)i;
-(void)log;
-(void)testBlock:(void(^)(void))block;
@end

Model.m

#import "Model.h"

@implementation Model
-(instancetype)initWithTitle:(NSString*)t image:(NSString *)i
{
    self = [super init];
    if (self) {
        self.title = t;
        self.image = i;
    }
    return self;
}

-(void)log
{
    NSLog(@"title = %@ image = %@" , self.title , self.image);
}
-(void)testBlock:(void(^)(void))block
{
    if(block){
        block();
    }
    
}

@end

我的测试代码是在tag1.0.1上测试的。反馈中虽然给出了自己的修改,但是并不是最好的。希望作者能抽空修复下这些问题,以后有新版本发布也方便我们升级。多谢!!

Terminating app due to uncaught exception 'MFRuntimeErrorNotFoundCFunction'

工程:MangoFixDemo
真机:iPhone X (14.2)

crash1:
*** Terminating app due to uncaught exception 'MFRuntimeErrorNotFoundCFunction', reason: 'error location line number: 223, not found CFunction: NSSearchPathForDirectoriesInDomains'

demo.mg -> line 223

crash2:
*** Terminating app due to uncaught exception 'MFRuntimeErrorNotFoundCFunction', reason: 'error location line number: 268, not found CFunction: testNativeCStringFunc'

demo.mg -> line 268

block属性变量赋值导致内存泄漏

场景:native存在block类型的属性,在hotfix里面如果此block赋值则会导致泄漏

如列子
//native 声明的属性
@Property(nonatomic , copy) void(^testBlock)(void);

//hotfix
{
//此代码运行后 当前对象会内存泄漏
self.testBlock = ^ {
NSLog(@"block go");
};
}

连续解析多个DSL文件问题

如果一个MFInterpreter实例解析过错误的DSL文件,再用该实例解析其他正确的DSL文件,则其他正确DSL文件也不会生效。

我在已下文件方法中使用yyrestart就可以了~

MFInterpreter.h

- (void)compileSoruceWithString:(NSString *)source{
	extern void nac_set_source_string(char const *source);
	nac_set_source_string([source UTF8String]);
	
	extern int yyparse(void);
        extern void yyrestart  (FILE * input_file );
	if (yyparse()) {
        yyrestart(NULL); /** 解析出错时,重置yylex */
        return;
	}
	
}

MangoFix性能如何?

iPhone8 Plus上测试后发现MangoFix的启动速度是JSPatch的10倍,运行速度也达到JSPatch的近2~5倍。
MangFix测试结果:
image

JSPath测试结果:
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.