学习笔记想法简友广场

Jimple 学习

2021-08-10  本文已影响0人  佩玖吟

jimple 是一种中间代码,用来简化分析,以及简化向 java 字节码的过渡。

jimple 是一种类型化的、三地址的、基于语句的中间代码

int i;

将上面代码转化为 jimple 之后,类型依旧保留,并没有丢失

int i;

Jimple 只需要 15 条语句指令即可,而 Java 字节码所需的指令多达 200 条,使代码的操作更加方便。

Jimple语句类型

Jimple 转 Java

public class Lab1 extends java.lang.Object
{

    public void <init>()
    {
        Lab1 r0;

        r0 := @this: Lab1;

        specialinvoke r0.<java.lang.Object: void <init>()>();

        return;
    }
    ...

这里类代码的Jimple不仅要将类名写完整,还要将继承的类显式写出来,以保全语义信息。

源程序中没有显式给出构造函数,Jimple中的<init>是默认生成的构造函数,然后$r0指向this,再用specialinvoke调用其父类(这里是Object,见方法签名)的构造函数。

JVM 里四种主要方法调用:

在上面的 Jimple 代码中方法调用(invoke)的地方,尖括号<>之间的内容是方法签名(Method Signature),它一般会包含方法所在的类名、方法的返回值类型、形参列表中各个参数的类型,有些还会包含方法名。

 public static void main(java.lang.String[])
    {
        Lab1 $r0;
        java.lang.String[] r2;

        r2 := @parameter0: java.lang.String[]; //IdentityStmt

        $r0 = new Lab1;

        specialinvoke $r0.<Lab1: void <init>()>();

        virtualinvoke $r0.<Lab1: void foo(int,int)>(5, 8);

        return;
    }

main 函数中 r2 := @parameter0: java.lang.String[]; 表示变量 r2 是该方法的第一个参数,其类型为 java.lang.String[],其中带 $ 的变量表示其在栈内位置,并不是源程序中的局部变量,源程序中的局部变量名与 Jimple 中的变量名相同。

specialinvoke 对 Lab1 类进行初始化,virtualinvke 调用 foo 函数,传参为 5、8。

所以可以得到源程序为

public static void main(String[] args){
    new Lab1().foot(5, 8);
}
public void foo(int, int)
    {
        int i0, i1;
        Lab1 r0;
        boolean $z0;
        java.io.PrintStream $r1, $r2, $r3;

        r0 := @this: Lab1;
        i0 := @parameter0: int;
        i1 := @parameter1: int;
        if i0 != i1 goto label1;
        $r3 = <java.lang.System: java.io.PrintStream out>;
    
        virtualinvoke $r3.<java.io.PrintStream: void println(java.lang.String)>("a == b");
        goto label3;
    
     label1:
        $z0 = virtualinvoke r0.<Lab1: boolean lt(int,int)>(i0, i1);
        if $z0 == 0 goto label2;
        $r2 = <java.lang.System: java.io.PrintStream out>;
        virtualinvoke $r2.<java.io.PrintStream: void println(java.lang.String)>("a > b");
        goto label3;
    
     label2:
        $r1 = <java.lang.System: java.io.PrintStream out>;
        virtualinvoke $r1.<java.io.PrintStream: void println(java.lang.String)>("a < b");
     label3:
        return;
    }

i0 和 i1 分别对应于传进来的两个参数,其中语句 if i0 != i1 goto label1; 判断两个参数是否相等。

virtualinvoke $r3.<java.io.PrintStream: void println(java.lang.String)>("a == b"); 对应于 System.out.println 函数。

对于 label1 中情况:

首先调用 lt 函数,然后判断返回值是否为 0,然后调用 System.out.println 函数。

对于 label2 中情况:

直接调用 System.out.println 函数。

所以可以得到源程序为

public void foo(int a, int b) {
    if (a == b) {
        System.out.println("a == b");
    } else if (lt(a, b)) {
        System.out.println("a > b");
    } else {
        System.out.println("a < b");
    }
}
public boolean lt(int, int)
    {
        int i0, i1;
        Lab1 r0;

        r0 := @this: Lab1;

        i0 := @parameter0: int;

        i1 := @parameter1: int;

        if i0 <= i1 goto label1;

        return 1;

     label1:
        return 0;
    }

ro 对应 Lab1 类,i0 和 i1 对应两个输入的参数,然后判断两个数值大小来进行返回,比较简单的大小判断,源程序为

public boolean lt(int a, int b) {
    if (a > b) {
        return true;
    }
    return false;
}
上一篇下一篇

猜你喜欢

热点阅读