ByteX icon indicating copy to clipboard operation
ByteX copied to clipboard

修复access-inline插件遇到access$方法中只有LDC指令时的NPE问题

Open Xiaojuanmao opened this issue 4 years ago • 4 comments

复现步骤

  1. 先写一个类,如下:
class TestClass {
  fun func1(): Int {
    return ARG_1 + ARG_2
  }
}

private val ARG_1 = 1
private val ARG_2 = 0
  1. 将Bytex的access-inline与const-inline插件集成,顺序为先执行const,再执行access
    apply plugin: 'bytex.const_inline'
    const_inline {
        enable true
        enableInDebug true
        logLevel "INFO"
        autoFilterReflectionField = true  //使用插件内置的反射检查过滤掉可能的反射常量,建议为true
        //supposesReflectionWithString = false //使用插件内置字符串匹配可能反射常量,建议为false
        skipWithRuntimeAnnotation true //过滤掉带有运行时注解的常量,推荐true
        skipWithAnnotations = [
                //过滤掉被注解注释过的常量,包含class
                "android/support/annotation/Keep",
        ]
        whiteList = [
                //跳过优化的名单
                "com/meizu/cloud/*",
        ]
    }

    apply plugin: 'bytex.access_inline'
    access_inline {
        enable true
        enableInDebug true
        logLevel "DEBUG"
    }

  1. 编译一遍就能看到有如下错误栈:
Caused by: java.lang.NullPointerException
        at com.ss.android.ugc.bytex.access_inline.Context.prepare(Context.java:96)
        at com.ss.android.ugc.bytex.access_inline.AccessInlinePlugin.beforeTransform(AccessInlinePlugin.java:40)
        at com.ss.android.ugc.bytex.common.flow.main.MainTransformFlow.lambda$beforeTransform$5(MainTransformFlow.java:144)
        at com.ss.android.ugc.bytex.transformer.concurrent.Worker.lambda$null$0(Worker.java:59)

原因分析

  1. access-inline插件崩溃在Context.prepare()方法,由于target为null导致
  2. target在PreProcessClassVisitor.refine()方法执行时设置,但在处理access$方法中只包含LDC指令时,会出现没有设置target的情况。(经过const-inline插件优化过后,ARG_2 参数access$方法中的GET STATIC -> LDC)
  3. 如果将ARG_2的值从0改为非0(例如2),变成了运行期常量,上述问题不会有,因为const-inline插件不对其处理。kotlin里private val本不应该被const-inline识别并处理,目前看来由于赋值的是默认值0,ARG_2成了编译期常量

处理 跳过包含LDC指令的access$方法内联

Xiaojuanmao avatar Jul 02 '20 08:07 Xiaojuanmao

感谢反馈。我本地已经复现了,const_inline插件破坏了标准了access方法指令,这里access方法判断不严谨。 这里的编译器常量和运行期常量在kotlin代码中也有些特殊,val有时候是编译器常量有时候不是,对插件之间的冲突有些干扰。 看到你的mr中只有判断ldc指令的判断,是不是应该判断target,而不仅仅是ldc?同时这里应该使用MethodNode更合适,可以顺带提一下。

yangzhiqian avatar Jul 03 '20 12:07 yangzhiqian

  1. 之前只判断LDC会导致方法中包含LDC指令的方法内联执行被跳过,已更正为在refine()方法最后对target进行判断
  2. MethodVisitor已替换为MethodNode

Xiaojuanmao avatar Jul 06 '20 02:07 Xiaojuanmao

大佬,pull request 求看一眼 Orz

Xiaojuanmao avatar Jul 27 '20 03:07 Xiaojuanmao

还有一个对插件的思考,const-inline不会对带有PUT_STATIC指令操作的进行优化,(上面例子中的ARG_1理论上是可以优化的,也被忽略了),从字节码的角度过滤可能成本比较大,可以把这些情况做一个扫描上报,如果优化空间较大,可以从代码规范的角度来做(把ARG_1的写法换成 private val const ARG_1 = 1)

Xiaojuanmao avatar Jul 27 '20 03:07 Xiaojuanmao