前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过 bitcode 分析支付宝 SDK 源码逻辑(1)

通过 bitcode 分析支付宝 SDK 源码逻辑(1)

作者头像
酷酷的哀殿
发布2021-03-18 09:48:31
6140
发布2021-03-18 09:48:31
举报
文章被收录于专栏:酷酷的哀殿酷酷的哀殿

声明

所有分析的源码都来自于支付宝开放平台:App支付客户端 DEMO&SDK[1]

bitcode 版本源码分析

偶然发现支付宝的 SDK 存在一个很有意思的函数 APMutableStringRemoveLastComma。该函数会判断可变字符串尾部是否等于 ,;如果相等,则进行移除。

而且,很奇怪的地方是,当我们通过导出的 bitcode 代码进行分析时,会发现该函数会 重复调用 字符串的 length 方法获取长度,而没有采用调用一次并缓存的方式进行性能优化。

bitcode 源码函数分析:

代码语言:javascript
复制
@"__ir_hidden#1_" = private global [7 x i8] c"length\00", section "__TEXT,__objc_methname,cstring_literals", align 1
@"__ir_hidden#2_" = private externally_initialized global i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"__ir_hidden#1_", i64 0, i64 0), section "__DATA, __objc_selrefs, literal_pointers, no_dead_strip"
@"__ir_hidden#3694_" = private global [20 x i8] c"substringWithRange:\00", section "__TEXT,__objc_methname,cstring_literals", align 1
@"__ir_hidden#3695_" = private externally_initialized global i8* getelementptr inbounds ([20 x i8], [20 x i8]* @"__ir_hidden#3694_", i64 0, i64 0), section "__DATA, __objc_selrefs, literal_pointers, no_dead_strip"
@__CFConstantStringClassReference = external global [0 x i32]
@"__ir_hidden#41_" = private unnamed_addr constant [2 x i8] c",\00", section "__TEXT,__cstring,cstring_literals", align 1
@"__ir_hidden#42_" = private constant %"__ir_hidden#3693_" { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 1992, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"__ir_hidden#41_", i32 0, i32 0), i64 1 }, section "__DATA,__cfstring"
@"__ir_hidden#3696_" = private global [17 x i8] c"isEqualToString:\00", section "__TEXT,__objc_methname,cstring_literals", align 1
@"__ir_hidden#3697_" = private externally_initialized global i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"__ir_hidden#3696_", i64 0, i64 0), section "__DATA, __objc_selrefs, literal_pointers, no_dead_strip"
@"__ir_hidden#3973_" = private unnamed_addr constant [1 x i8] zeroinitializer, section "__TEXT,__cstring,cstring_literals", align 1
@"__ir_hidden#3974_" = private constant %"__ir_hidden#3693_" { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 1992, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @"__ir_hidden#3973_", i32 0, i32 0), i64 0 }, section "__DATA,__cfstring"
@"__ir_hidden#3741_" = private global [37 x i8] c"replaceCharactersInRange:withString:\00", section "__TEXT,__objc_methname,cstring_literals", align 1
@"__ir_hidden#3742_" = private externally_initialized global i8* getelementptr inbounds ([37 x i8], [37 x i8]* @"__ir_hidden#3741_", i64 0, i64 0), section "__DATA, __objc_selrefs, literal_pointers, no_dead_strip"
@llvm.compiler.used = appending global [8 x i8*] [i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"__ir_hidden#1_", i32 0, i32 0), i8* bitcast (i8** @"__ir_hidden#2_" to i8*), i8* getelementptr inbounds ([20 x i8], [20 x i8]* @"__ir_hidden#3694_", i32 0, i32 0), i8* bitcast (i8** @"__ir_hidden#3695_" to i8*), i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"__ir_hidden#3696_", i32 0, i32 0), i8* bitcast (i8** @"__ir_hidden#3697_" to i8*), i8* getelementptr inbounds ([37 x i8], [37 x i8]* @"__ir_hidden#3741_", i32 0, i32 0), i8* bitcast (i8** @"__ir_hidden#3742_" to i8*)], section "llvm.metadata"

; Function Attrs: optsize ssp
define void @APMutableStringRemoveLastComma(%0*) #0 {
  %2 = bitcast %0* %0 to i8*
  ; 先对入参 NSMutableString 进行 retain 操作
  %3 = tail call i8* @llvm.objc.retain(i8* %2)
  ; 这里实际上是获取  @selector(length) 方法,准备进行调用实例方法
  %4 = load i8*, i8** @"__ir_hidden#2_", align 8, !invariant.load !8
  ; 调用  @selector(length) 方法
  %5 = tail call i64 bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i64 (i8*, i8*)*)(i8* %2, i8* %4) #3, !clang.arc.no_objc_arc_exceptions !8
  ; 将返回值与 0 比较
  %6 = icmp eq i64 %5, 0
  ; 如果相等,进入 22: 分支,如果不等,进入 7: 分支
  br i1 %6, label %22, label %7

7:                                                ; preds = %1
  ; 再次获取  @selector(length) 方法,准备进行调用实例方法
  %8 = load i8*, i8** @"__ir_hidden#2_", align 8, !invariant.load !8
  ; 再次调用  @selector(length) 方法
  %9 = tail call i64 bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i64 (i8*, i8*)*)(i8* %2, i8* %8) #3, !clang.arc.no_objc_arc_exceptions !8
  ; 将结果减 1
  %10 = add i64 %9, -1
  ; 获取 @selector(substringWithRange:) 方法
  %11 = load i8*, i8** @"__ir_hidden#3695_", align 8, !invariant.load !8
  ; 准备参数 NSRange ,分别是 {length-1,1}
  %12 = insertvalue [2 x i64] undef, i64 %10, 0
  %13 = insertvalue [2 x i64] %12, i64 1, 1
  ; 获取子串
  %14 = tail call %1* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to %1* (i8*, i8*, [2 x i64])*)(i8* %2, i8* %11, [2 x i64] %13) #3, !clang.arc.no_objc_arc_exceptions !8
  %15 = bitcast %1* %14 to i8*
  ; 讲方法的返回值放入自动释放池
  %16 = tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %15)
  ; 获取 @selector(isEqualToString:) 方法
  %17 = load i8*, i8** @"__ir_hidden#3697_", align 8, !invariant.load !8
  ; 子串与 ',' 判等
  %18 = tail call zeroext i1 bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i1 (i8*, i8*, %1*)*)(i8* %15, i8* %17, %1* bitcast (%"__ir_hidden#3693_"* @"__ir_hidden#42_" to %1*)) #3, !clang.arc.no_objc_arc_exceptions !8
  ; 如果相等,进入 19: 分支,否则进入 21:分支
  br i1 %18, label %19, label %21

19:                                               ; preds = %7
  ; 获取 @selector(replaceCharactersInRange:withString:) 方法
  %20 = load i8*, i8** @"__ir_hidden#3742_", align 8, !invariant.load !8
  ; 将尾部替换为空字符串,和函数名 MutableStringRemoveLastComma 对应
  tail call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, [2 x i64], %1*)*)(i8* %2, i8* %20, [2 x i64] %13, %1* bitcast (%"__ir_hidden#3693_"* @"__ir_hidden#3974_" to %1*)) #3, !clang.arc.no_objc_arc_exceptions !8
  br label %21

21:                                               ; preds = %19, %7
  ; 销毁子串
  tail call void @llvm.objc.release(i8* %15)
  br label %22
22:                                               ; preds = %21, %1
  ; 释放对入参的引用
  tail call void @llvm.objc.release(i8* %2)
  ; return 操作
  ret void
}

OC 版本

下面的代码是根据上面的 bitcode 反推得到,不代表支付宝 SDK 原始的代码

代码语言:javascript
复制
void APMutableStringRemoveLastComma(NSMutableString *input) {
    if ([input length]==0) {
        return;
    } else {
        NSUInteger location = [input length];
        location = location - 1;
        NSRange range = NSMakeRange(location, 1);
        NSString *tail = [input substringWithRange:range];
        BOOL isEqual = [tail isEqualToString:@","];
        if (isEqual) {
            [input replaceCharactersInRange:range withString:@""];
        }
    }
}

性能优化后的版本

代码语言:javascript
复制
void APMutableStringRemoveLastComma(NSMutableString *input) {
    NSUInteger length = [input length];
    if (length==0) {
        return;
    } else {
        NSUInteger location = length - 1;
        NSRange range = NSMakeRange(location, 1);
        NSString *tail = [input substringWithRange:range];
        BOOL isEqual = [tail isEqualToString:@","];
        if (isEqual) {
            [input replaceCharactersInRange:range withString:@""];
        }
    }
}

参考资料

[1]

App支付客户端 DEMO&SDK: https://opendocs.alipay.com/open/54/104509

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 酷酷的哀殿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 声明
  • bitcode 版本源码分析
  • OC 版本
  • 性能优化后的版本
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档