【问题标题】:How to compute StackMapTable's frame map frames?如何计算 StackMapTable 的框架图框架?
【发布时间】:2021-06-15 15:39:32
【问题描述】:

我正在研究类文件的 StackMapTable 属性,我有一些问题,堆栈映射框架是如何计算的?

这里有两个例子:
例子1

public class Foo {  
    public void foo() {  
        int i = 0;  
        int j = 0;  
        if (i > 0) {  
          int k = 0;  
        }  
        int l = 0;  
    }  
}  

//byte code:
public void foo();  
  Code:  
   Stack=1, Locals=4, Args_size=1  
   0:   iconst_0  
   1:   istore_1  
   2:   iconst_0  
   3:   istore_2  
   4:   iload_1  
   5:   ifle    10  
   8:   iconst_0  
   9:   istore_3  
   10:  iconst_0 
   11:  istore_3  
   12:  return  

  
  LocalVariableTable:  
   Start  Length  Slot  Name   Signature  
   10      0      3    k       I  
   0      13      0    this       LFoo;  
   2      11      1    i       I  
   4      9      2    j       I  
   12      1      3    l       I  
  
  StackMapTable: number_of_entries = 1  
   frame_type = 253 /* append */  
     offset_delta = 10  
     locals = [ int, int ]  

示例 2

public static void chop() {
        int i = 0;
        int j = 0;
        if (i > 0) {
            long k = 0;
            if (j == 0) {
                k++;
                int s=1111;
            }
            int t = 0;
        }
    }


 //bytecode
 public static void chop();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=5, args_size=0
         0: iconst_0
         1: istore_0
         2: iconst_0
         3: istore_1
         4: iload_0
         5: ifle          26
         8: lconst_0
         9: lstore_2
        10: iload_1
        11: ifne          23
        14: lload_2
        15: lconst_1
        16: ladd
        17: lstore_2
        18: sipush        1111
        21: istore        4
        23: iconst_0
        24: istore        4
        26: return
      LineNumberTable:
        line 34: 0
        line 35: 2
        line 36: 4
        line 37: 8
        line 38: 10
        line 39: 14
        line 40: 18
        line 42: 23
        line 44: 26
      StackMapTable: number_of_entries = 2
        frame_type = 254 /* append */
          offset_delta = 23
          locals = [ int, int, long ]
        frame_type = 250 /* chop */
          offset_delta = 2

谁能根据这两个例子介绍如何计算堆栈图框架?
这个问题困扰了我很久,任何帮助将不胜感激!谢谢!

【问题讨论】:

  • “计算”是什么意思?这是什么意思?
  • 栈映射表你了解多少?你不明白的实际点是什么?
  • 也许你应该先阅读What is a stack map frame。然后,您应该了解条目的数量。正如链接的答案所说,“它们通常表示为与前一帧的差异以减少数据大小”,因此当您了解其结构时,您应该了解附加或切分类型的含义。您可以从字节码计算堆栈帧,这是旧验证器所做的,也是 ASM 库支持的,但正如 this answer 中所述,它有局限性。
  • 分支目标、异常处理程序的开始以及无法访问的指令序列的开始都需要堆栈映射表中的条目。如果这些点让你感觉更好,你可以把这些点称为一个基本块的开始,但这并没有什么区别。是的,对于您的第二种方法,这些是字节码位置 23 和 26。
  • 您是指LocalVariableTable 吗?这是一个调试功能。它提供有关变量的信息,因为它们出现在源代码中。 StackMapTable 是验证者的一个属性,它与字节码中使用的变量有关。例如,前者是可选的并且包含名称,后者是强制性的并且不关心名称。

标签: java class bytecode java-bytecode-asm


【解决方案1】:

示例 1

public class Foo {
    public void foo() {
        int i = 0;
        int j = 0;
        if (i > 0) {
            int k = 0;
        }
        int l = 0;
    }
}

foo方法的操作码和帧:

foo:()V
                               // {this} | {} <--- initial frame
0000: iconst_0                 // {this} | {int}
0001: istore_1                 // {this, int} | {}
0002: iconst_0                 // {this, int} | {int}
0003: istore_2                 // {this, int, int} | {}
0004: iload_1                  // {this, int, int} | {int}
0005: ifle            5        // {this, int, int} | {}
0008: iconst_0                 // {this, int, int} | {int}
0009: istore_3                 // {this, int, int, int} | {}
                               // {this, int, int} | {} <--- second frame
0010: iconst_0                 // {this, int, int} | {int}
0011: istore_3                 // {this, int, int, int} | {}
0012: return                   // {} | {}

初始帧是根据方法访问方法描述符自动计算的:

{this} | {}

第二帧ifle操作码(0005)的跳转目标:

{this, int, int} | {}

比较第二帧初始帧:附加了两个int值。

示例 2

public class Foo {
    public static void chop() {
        int i = 0;
        int j = 0;
        if (i > 0) {
            long k = 0;
            if (j == 0) {
                k++;
                int s = 1111;
            }
            int t = 0;
        }
    }
} 

chop 方法的操作码和帧:

chop:()V
                               // {} | {} <--- initial frame
0000: iconst_0                 // {} | {int}
0001: istore_0                 // {int} | {}
0002: iconst_0                 // {int} | {int}
0003: istore_1                 // {int, int} | {}
0004: iload_0                  // {int, int} | {int}
0005: ifle            21       // {int, int} | {}
0008: lconst_0                 // {int, int} | {long, top}
0009: lstore_2                 // {int, int, long, top} | {}
0010: iload_1                  // {int, int, long, top} | {int}
0011: ifne            12       // {int, int, long, top} | {}
0014: lload_2                  // {int, int, long, top} | {long, top}
0015: lconst_1                 // {int, int, long, top} | {long, top, long, top}
0016: ladd                     // {int, int, long, top} | {long, top}
0017: lstore_2                 // {int, int, long, top} | {}
0018: sipush          1111     // {int, int, long, top} | {int}
0021: istore          4        // {int, int, long, top, int} | {}
                               // {int, int, long, top} | {} <--- second frame
0023: iconst_0                 // {int, int, long, top} | {int}
0024: istore          4        // {int, int, long, top, int} | {}
                               // {int, int} | {} <--- third frame
0026: return                   // {} | {}

比较第二帧初始帧:两个ints和一个long附加。

比较第三帧第二帧long 被砍掉了。

【讨论】:

    猜你喜欢
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 2022-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    • 1970-01-01
    相关资源
    最近更新 更多