2

the java code snippet

    int x4(int a) {
    if(a>7){
        System.out.println("a>7");
    }


    if(a==0){
        System.out.println("a==0");
    }else if(a>77){
        System.out.println(" a>77");
    }else if(a>44){
        System.out.println(" a>44");
    }else{
        System.out.println("otherwise");
    }

    return 44;
}

the bytecode outline:

// access flags 0x0
x4(I)I
// parameter  a
L0
ILOAD 1
BIPUSH 7
IF_ICMPLE L1  ---- operand stack is empty here , end of a statement
L2 ---- new statement mark
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a>7"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
IFNE L3
L4
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a==0"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
GOTO L6
L3
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
BIPUSH 77
IF_ICMPLE L7
L8
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC " a>77"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L9
GOTO L6
L7
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
BIPUSH 44
IF_ICMPLE L10
L11
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC " a>44"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L12
GOTO L6
L10
FRAME SAME ---- branching place start with empty operand stack
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "otherwise"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
FRAME SAME ---- branching place start with empty operand stack
BIPUSH 44
IRETURN
L13
// access flags 0x0
x4(I)I
// parameter  a
L0
ILOAD 1
BIPUSH 7
IF_ICMPLE L1
L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a>7"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
IFNE L3
L4
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a==0"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
GOTO L6
L3
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
BIPUSH 77
IF_ICMPLE L7
L8
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC " a>77"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L9
GOTO L6
L7
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
BIPUSH 44
IF_ICMPLE L10
L11
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC " a>44"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L12
GOTO L6
L10
FRAME SAME ---- branching place start with empty operand stack
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "otherwise"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
FRAME SAME ---- branching place start with empty operand stack
BIPUSH 44
IRETURN
L13

it seems that whenever the operand stack is emptied by "assignment" "operation+assignment" "follow control" "method invocation" , a new label would mark a new "statement line" .

the "follow control" block is a collection of "statement" , and when a block(marked by "jump instructions") is end, it would also a "statement" ending hence the operand stack would be empty for the block's following code or alternating branches...

so it seems to me that no case the "labeled by ' jump instruction ' code" would not be the "FRAME SAME" for the compiled java source code

but i know it will not be the case . otherwise the STACK MAP FRAME would not be designed into byte-code.

please experts ,thanks very much , give me example of non-FRAME SAME branching , and explain it intuitively .

thanks very much, please help. help please.

Johnny
  • 387
  • 2
  • 12
  • Check this link out regarding stack map frames: http://stackoverflow.com/questions/25109942/is-there-a-better-explanation-of-stack-map-frames – Richard Campbell Sep 06 '16 at 15:36
  • by the dev stage of my java bytecode decompiler, i don't care for extreme speed for now . only the correctness of interpretation is concerned , by the address you inferred, do you mean ,if don't care about decompile speed , i can ignore the stack map frame ? – Johnny Sep 06 '16 at 16:21
  • i mean , in the case which i presented , the stack map is not only FRAM SAME, but also empty too, and i can not think up a case that the operand stack of the "jump target " is not SAME and empty. – Johnny Sep 06 '16 at 16:30

2 Answers2

2

According to the link below, you can ignore the stack map frame completely as the only use of the stack map frame is to verify that a class is secure to run; the stack map frame has no relation to the underlying source code you are trying to recover with your decompiler.

Is there a better explanation of stack map frames?

See also: http://chrononsystems.com/blog/java-7-design-flaw-leads-to-huge-backward-step-for-the-jvm

And, from the standard itself:

The StackMapTable attribute is a variable-length attribute in the attributes table of a Code (§4.7.3) attribute. This attribute is used during the process of verification by type checking (§4.10.1). A method's Code attribute may have at most one StackMapTable attribute. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4

Community
  • 1
  • 1
Richard Campbell
  • 3,591
  • 23
  • 18
1

If you only want to decompile, i.e. print the instruction sequence, you may simply ignore stack frames. They only provide information about the types of items on the operand stack and the local variables. If you don’t print these types, you don’t need to parse them.

If you only use branch statements, there are indeed no operand stack entries on both ends of the branch, but the reason why you don’t encounter local variable changes simply is that you don’t have such changes in your example code.

Consider:

for(int i=0; i<10; i++)
    System.out.println(i);

Here, the variable i has been added to the stack frame at the loop’s branch, so you are likely to encounter an F_APPEND frame there (compilers are encouraged, but not required to use the most compact form). Likewise, when adding another subsequent branch like in

for(int i=0; i<10; i++)
  System.out.println(i);
if(a==0)
  System.out.println();

you are likely to encounter a F_CHOP frame because at the subsequent branch, i is not in scope anymore.

Note that there are also intra-statement branches possible, allowing to have different operand stack entries, e.g.

System.out.println(a==0? "zero": "non-zero");

bears two branches. For the first, a reference to a PrintStream instance is already on the stack, for the second, references to a PrintStream and a String instance are on the stack.

Also, exception handlers form implicit branch targets having a single operand stack entry, the caught exception. This situation can be encoded with the conveniently defined F_SAME1 frame type.


If you plan to use the information contained in stack frames and are using the ASM library, you don’t need to parse all the different frame types. Just pass the flag ClassReader.EXPAND_FRAMES to the ClassReader upon construction and all you will ever encounter, are F_NEW frames containing the complete stack state, eliminating the need to remember previous frames.

Holger
  • 285,553
  • 42
  • 434
  • 765