1

I am studying class file's StackMapTable attribute, and i have some questions, how does the stack map frames computed?

Here I have two examples:
Example 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 ]  

Example 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

Can anybody introduce how to calculate stack map frames base this two examples?
This problem has been bothering me for a long time,any help will be appreciated! Thanks!

user5549139
  • 157
  • 9
  • what do you mean by "compute"? what is the meaning of it? – Eugene Jun 15 '21 at 15:43
  • you can start from [here](https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-4.html#jvms-4.7.4). – Eugene Jun 15 '21 at 15:48
  • I mean, how the StackMapTable attribute generated – user5549139 Jun 16 '21 at 06:05
  • 1
    How much do you know about the stack map table? What’s the actual point you don’t understand? – Holger Jun 16 '21 at 07:08
  • After i use javap command show the bytecode info of a class file, when i meet the StackMapTable attribute info of a method, i can know why it like that. for example, the `chop` method, why the number_of_entries is 2 and why the item's frame type of stamp_map_frame is `append` & `chop`. Sorry for my poor english, i can not express my words exactly – user5549139 Jun 17 '21 at 03:06
  • I know StackMapTable attribute, include it's structure, but i do not know why it like that of a method. Can i deduce the `stack_map_frame` data from the bytecode of a method by myself? And How? – user5549139 Jun 17 '21 at 03:18
  • 1
    Maybe you should first read [What is a stack map frame](https://stackoverflow.com/q/25109942/2711488). Then, you should understand the number of entries. As the linked answer says, “they are usually expressed as a difference from the previous frame in order to reduce data size”, so when you understand its structure you should understand the meaning of append or chop types. You can calculate stack frames from the bytecode, that’s what the old verifier did and what the ASM library supports, but as explained in [this answer](https://stackoverflow.com/a/49262105/2711488), it has limitations. – Holger Jun 17 '21 at 08:45
  • `In order to make the metadata smaller and more efficient, they decided to have it only list the types at positions which are targets of jumps` - from [what is stack map fram](https://stackoverflow.com/a/25110513/5549139). Is this mean when meet `ifne`、`ifle` and `goto` instruction's jump target, there must have a stack map frame? For example, the `public static void chop()` method from my question, the bytecode offset at 26 and 23, there must have a frame, is it right? Is the stack map frame does not base on the basic block? – user5549139 Jun 17 '21 at 15:48
  • About basic block, ASM developer guider [describe it](https://asm.ow2.io/developer-guide.html#controlflow). – user5549139 Jun 17 '21 at 15:54
  • 1
    Branch targets, the beginning of exception handlers, and the beginning of unreachable instruction sequences will require an entry in the stackmap table. You may call these points the beginning of a basic block if it makes you feel better but it makes no difference. Yes, for your second method these are the bytecode locations 23 and 26. – Holger Jun 18 '21 at 08:49
  • I have a question about `StackMapTable`, it present `the current state of the local variables and operand stack elements`, what does the `local variables` means? What is the difference with `local variables table`? – user5549139 Sep 18 '21 at 01:51
  • @Holger Do you have time to check my previous comment? Thanks. – user5549139 Sep 27 '21 at 02:12
  • 1
    Do you mean the [`LocalVariableTable`](https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.7.13)? That’s a debugging feature. It provides information about variables, as they appear in the source code. The `StackMapTable` is an attribute for the verifier, which is concerned with variables as they are used in the bytecode. The former is optional and contains names, the latter is mandatory and doesn’t care about names, for example. – Holger Sep 27 '21 at 07:43
  • "The StackMapTable is an attribute for the verifier, which is concerned with variables as they are used in the bytecode." ---------------- I want to know the `variables` in the bytecode, what this means? – user5549139 Sep 29 '21 at 07:00

1 Answers1

0

Example 1:

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

The opcodes and frames of the foo method:

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                   // {} | {}

The initial frame is computed automatically from the method access and method descriptor:

{this} | {}

The second frame is the jump target of ifle opcode(0005):

{this, int, int} | {}

Compare the second frame with the initial frame: two int values appended.

Example 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;
        }
    }
} 

The opcodes and frames of the chop method:

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                   // {} | {}

Compare the second frame with the initial frame: two ints and a long appended.

Compare the third frame with the second frame: a long chopped.

lsieun
  • 11
  • 2