Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

插件App编译失败:inconsistent stack height Index 12 out of bounds for length 12 #1196

Closed
cocomikes opened this issue Jun 7, 2023 · 4 comments

Comments

@cocomikes
Copy link

cocomikes commented Jun 7, 2023

执行':sample-app:transformClassesWithShadowTransformForPluginDebug'任务时,编译失败
报错日志如下:
`
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: javassist.CannotCompileException: by javassist.bytecode.BadBytecode: (Lorg/webrtc/NetworkMonitorAutoDetect$Observer;Landroid/content/Context;)V in org.webrtc.NetworkMonitorAutoDetect: inconsistent stack height Index 12 out of bounds for length 12

Caused by: java.lang.RuntimeException: javassist.CannotCompileException: by javassist.bytecode.BadBytecode: (Lorg/webrtc/NetworkMonitorAutoDetect$Observer;Landroid/content/Context;)V in org.webrtc.NetworkMonitorAutoDetect: inconsistent stack height Index 12 out of bounds for length 12
at com.tencent.shadow.core.transform_kit.AbstractTransform.debugWriteJar(AbstractTransform.kt:93)
at com.tencent.shadow.core.transform_kit.AbstractTransform.onOutputClass(AbstractTransform.kt:79)
at com.tencent.shadow.core.transform_kit.ClassTransform.output(ClassTransform.kt:144)
at com.tencent.shadow.core.transform_kit.ClassTransform.transform(ClassTransform.kt:186)
at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:284)
at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:69)
... 131 more
Caused by: javassist.CannotCompileException: by javassist.bytecode.BadBytecode: (Lorg/webrtc/NetworkMonitorAutoDetect$Observer;Landroid/content/Context;)V in org.webrtc.NetworkMonitorAutoDetect: inconsistent stack height Index 12 out of bounds for length 12
at javassist.CtClassType.modifyConstructors(CtClassType.java:1742)
at javassist.CtClassType.toBytecode(CtClassType.java:1588)
at com.tencent.shadow.core.transform_kit.AbstractTransform.debugWriteJar(AbstractTransform.kt:88)
... 136 more
Caused by: javassist.bytecode.BadBytecode: (Lorg/webrtc/NetworkMonitorAutoDetect$Observer;Landroid/content/Context;)V in org.webrtc.NetworkMonitorAutoDetect: inconsistent stack height Index 12 out of bounds for length 12
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:119)
at javassist.bytecode.MethodInfo.rebuildStackMap(MethodInfo.java:458)
at javassist.bytecode.MethodInfo.rebuildStackMapIf6(MethodInfo.java:440)
at javassist.CtClassType.modifyConstructors(CtClassType.java:1739)
... 138 more
Caused by: javassist.bytecode.BadBytecode: inconsistent stack height Index 12 out of bounds for length 12
at javassist.bytecode.stackmap.Tracer.doOpcode(Tracer.java:81)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:195)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:172)
at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:116)
... 141 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 12 out of bounds for length 12
at javassist.bytecode.stackmap.Tracer.doOpcode148_201(Tracer.java:606)
at javassist.bytecode.stackmap.Tracer.doOpcode(Tracer.java:78)
`
请问出现此问题的原因会是什么呢?shadow-gradle-plugin内javassist转换问题?抑或是NetworkMonitorAutoDetect本身类损坏的问题? @shifujun

附测试demo :
地址: https://github.com/cocomikes/Shadow/tree/issue-inconsistent-stack-height
提交: 31ffdd9

@shifujun
Copy link
Collaborator

shifujun commented Jun 7, 2023

首先这个issue反馈的方式特别好,交流开源项目就应该这样基于简单改动可复现的代码。

我分析这个问题应该是javassist的bug。它在addField时,如果有field初始化代码,它需要在构造器里添加几个初始化field的代码。这时:
https://github.com/jboss-javassist/javassist/blob/700be6f6f9546e8af049b1a763ce27f1fde5955d/src/main/javassist/CtClassType.java#L1765-L1767
它这里的代码似乎是在考虑自己添加的代码是否会超出原本栈高度,如果不够用就设置为新增代码所需栈高度。
但这明显是不合理的,具体到这个复现问题的例子,原本栈高12,新增代码所需3,看起来是够的。但执行到新增代码处时栈正好满了。所以这个bug能否复现也要看新增代码处栈是不是正好满的,如果新增代码在方法最前面,一般栈是空的,那可能原本的最大栈高正好够用,它就不能复现bug。

基于以上分析,我做了兼容修复:
https://github.com/shifujun/Shadow/tree/1196

你可以先帮我验证下能否正常工作了。

另外修改后出现了如下的warning,让我不能确定这个兼容是否正确。我在修改后的类中找不到哪里有aload 5。然后也怀疑是不是原本的class就有问题,因为它看起来是经过proguard优化过的。

> Task :sample-app:dexBuilderPluginDebug
WARNING:Shadow/projects/sample/source/sample-plugin/sample-app/build/intermediates/transforms/ShadowTransform/plugin/debug/1.jar: D8: Invalid stack map table at 154: aload 5, error: No local at index 5.
WARNING:Shadow/projects/sample/source/sample-plugin/sample-app/build/intermediates/transforms/ShadowTransform/plugin/debug/1.jar: D8: Invalid stack map table at 570: aload 1, error: The expected type top is not assignable to java.lang.Object.

shifujun added a commit to shifujun/Shadow that referenced this issue Jun 7, 2023
@shifujun
Copy link
Collaborator

shifujun commented Jun 8, 2023

又继续学习了一下。这个问题看起来主要原因还是这个有问题的NetworkMonitorAutoDetect的class不是普通javac的产物。因为它这段字节码:

public org.webrtc.NetworkMonitorAutoDetect(org.webrtc.NetworkMonitorAutoDetect$Observer, android.content.Context);
    descriptor: (Lorg/webrtc/NetworkMonitorAutoDetect$Observer;Landroid/content/Context;)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=12, locals=4, args_size=3
         0: aload_0
         1: dup
         2: dup2
         3: dup2
         4: dup2
         5: aload_2
         6: aload_0
         7: aload_1
         8: aload_0
         9: invokespecial #85                 // Method android/content/BroadcastReceiver."<init>":()V
        12: putfield      #87                 // Field observer:Lorg/webrtc/NetworkMonitorAutoDetect$Observer;
        15: putfield      #89                 // Field context:Landroid/content/Context;
        18: new           #19                 // class org/webrtc/NetworkMonitorAutoDetect$ConnectivityManagerDelegate
        21: dup
        22: aload_2

通常javac生成的域变量初始化代码是这样的:

javassist.bytecode.Gap0Example(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    descriptor: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
    flags: (0x0000)
    Code:
      stack=3, locals=6, args_size=6
         0: aload_0
         1: invokespecial #13                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: getstatic     #38                 // Field counter:I
         8: iconst_1
         9: iadd
        10: dup
        11: putstatic     #38                 // Field counter:I
        14: putfield      #170                // Field i:I
        17: aload_0
        18: aload_1
        19: putfield      #22                 // Field o1:Ljava/lang/Object;
        22: aload_0
        23: aload_2
        24: putfield      #24                 // Field o2:Ljava/lang/Object;
        27: aload_0
        28: aload_3
        29: putfield      #26                 // Field o3:Ljava/lang/Object;

可以注意到都是一个变量生成一组aload和putfield的指令。所以它不需要很大的栈。读一个释放一个的概念。

但是NetworkMonitorAutoDetect的构造器在9: invokespecial #85也就是super()调用之前几乎把所有后面putfield等需要从栈中读参数的值都先aload或者dup到栈中了。所以它才有高达12的栈容量。

所以说javassist虽然有问题,但可能它就是只针对javac输出的字节码设计的。感觉这段逻辑假设了每条java语句对应的jvm字节码都能清空栈。

#1155 也是这种类不是javac生成的导致的问题。

这么看来javassist对于我们这种需要编辑任意合法JVM字节码的需求来说不是能完全满足的。可能应该考虑在编辑前将class先反编译回java再重新用javac生成较为标准的字节码再编辑。

@cocomikes
Copy link
Author

参考https://github.com/shifujun/Shadow/tree/1196,做了兼容,可以正常编译打包了。

修复后,我在https://github.com/cocomikes/Shadow/tree/issue-inconsistent-stack-height这个分支下 :sample-app:dexBuilderPluginDebug有一模一样的WARNING。等我把这个修复发布到内网maven上后,在项目里编译dexBuilderPluginDebug下面就没有以上2条WARNING了。😈

抽空细细研究下javaassist

谢谢大佬,祝好。

@uchiaitachipro
Copy link

感谢大佬的分析思路,接入shadow的也遇到类似问题。是我们项目中的transform asm将一个类的stack 改小了,shadow transform的时候就报错了。
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants