利用javap反编译Scala字节码文件

原创
2012/06/06 00:03
阅读数 1.2K

javap是JDK提供的一个反编译工具。常用的选项有-c -l -s。如果仅仅是想查看编译的得到的字节码文件中函数与变量,不需要带选项。使用javap很容易发现源文件与编译后得到的字节码文件有什么不同,可以加深对编译器的理解。

javap -help
Usage: javap <options> <classes>...

where options include:
   -c                        Disassemble the code
   -classpath <pathlist>     Specify where to find user class files
   -extdirs <dirs>           Override location of installed extensions
   -help                     Print this usage message
   -J<flag>                  Pass <flag> directly to the runtime system
   -l                        Print line number and local variable tables
   -public                   Show only public classes and members
   -protected                Show protected/public classes and members
   -package                  Show package/protected/public classes and members (default)
   -private                  Show all classes and members
   -s                        Print internal type signatures
   -bootclasspath <pathlist> Override location of class files loaded by the bootstrap class loader
   -verbose                  Print stack size, number of locals and args for methods If verifying, print reasons for failure

 Scala是基于JVM的,所有其字节码和Java编译得到的字节码应该是一致的。首先从Hello World开始

object Main {
  def main(args: Array[String]) = {
    println("Hello, " + args(0))
  }
}

使用scalac编译都得到两个字节码文件:Main.class和Main$.class。

在class文件所在的目录分别运行javap Main和javap Main$得到如下结果:

Compiled from "Main.scala"
public final class Main extends java.lang.Object{
  public static final void main(java.lang.String[]);
}

Compiled from "Main.scala"
public final class Main$ extends java.lang.Object implements scala.ScalaObject{
  public static final Main$ MODULE$;
  public static {};
  public void main(java.lang.String[]);
}

Scala的object是单例模式。上面的反编译结果给了我们Scala实现原理的提示。MODULE$指向类的实例,相当于this。而方法也是被声明为静态的。http://rednaxelafx.iteye.com/blog/943036做了一个测试,更加直观。http://stackoverflow.com/questions/2347107/what-is-scala-equivalent-of-javas-static-block说如果不是去发射火箭,Object的代码跟Java的静态代码可以认为是等价的。

把上面的代码稍作修改:

case class Main {
  def main(args: Array[String]) = {
    println("Hello, " + args(0))
  }
}

在class文件所在的目录分别运行javap Main和javap Main$得到如下结果:

Compiled from "Main.scala"
public class Main extends java.lang.Object implements scala.ScalaObject,scala.Product,scala.Serializable{
  public scala.collection.Iterator productIterator();
  public scala.collection.Iterator productElements();
  public void main(java.lang.String[]);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public boolean canEqual(java.lang.Object);
  public Main();
}

Compiled from "Main.scala"
public final class Main$ extends scala.runtime.AbstractFunction0 implements scala.ScalaObject,scala.Serializable{
  public static final Main$ MODULE$;
  public static {};
  public final java.lang.String toString();
  public boolean unapply(Main);
  public Main apply();
  public java.lang.Object readResolve();
  public java.lang.Object apply();
}

与输入的文件相比,Scala添加了许多东西:

1. Scala自动帮助Case类生成toString, equals,hashCode等方法。

2. Scala自动帮助Case类生成apply方法,不需要new就可以直接创建类的实例或引用。

3. 对于类的成员,Scala自动生成成员的Getter和Setter。

4. Case类提供对模式匹配的支持。

下面来看Scala对类和隐式转换的处理,有一个Rational类:

class Rational(n: Int, d: Int) {
  require(d != 0)
  private val g = gcd(n.abs, d.abs)
  val numer = n / g
  val denom = d / g
  
  def this(n: Int) = this(n, 1)
  
  def +(that: Rational): Rational =
    new Rational(numer * that.denom + that.numer * denom, denom * that.denom)
  
  def +(i: Int): Rational = new Rational(numer + i * denom, denom)
  
  def -(that: Rational): Rational =
    new Rational(numer * that.denom - that.numer * denom, denom * that.denom)
  
  def -(i: Int): Rational = new Rational(numer - i * denom, denom)
  
  def *(that: Rational): Rational = new Rational(numer * that.numer, denom * that.denom)
  
  def *(i: Int): Rational = new Rational(numer * i, denom)
  
  def /(that: Rational): Rational = new Rational(numer * that.denom, denom * that.numer)
  
  def /(i: Int): Rational = new Rational(numer, denom * i)
  
  override def toString = numer + "/" + denom
  
  private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
}


object Rational{
  implicit def intToRational(x: Int) = new Rational(x) 
}

javap得到:

Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements scala.ScalaObject{
    public static final Rational intToRational(int);
    public int numer();
    public int denom();
    public Rational(int);
    public Rational $plus(Rational);
    public Rational $plus(int);
    public Rational $minus(Rational);
    public Rational $minus(int);
    public Rational $times(Rational);
    public Rational $times(int);
    public Rational $div(Rational);
    public Rational $div(int);
    public java.lang.String toString();
    public Rational(int, int);
}

Compiled from "Rational.scala"
public final class Rational$ extends java.lang.Object implements scala.ScalaObje
ct{
    public static final Rational$ MODULE$;
    public static {};
    public Rational intToRational(int);
}

对于普通类,Scala自动添加的东西稍微少一些。一直出现的还有一个特别的函数:public static {};通过带参数-c的javap可以看到其汇编代码:

public static {};
  Code:
   0:   new     #9; //class Rational$
   3:   invokespecial   #12; //Method "<init>":()V
   6:   return

new创建一个对象并将其引用值压入栈顶,invokespecial调用父类的构造方法,return从当前方法返回void。该方法实际上就是一个创建自身的静态方法。Java虚拟机指令参考http://blog.csdn.net/noonlyandroid/article/details/6117470

Rational$中的intToRational是我们提供的隐式类型转换,将一个Int转换为Rational。其汇编代码如下:

public Rational intToRational(int);
  Code:
   0:   new     #16; //class Rational
   3:   dup
   4:   iload_1
   5:   invokespecial   #20; //Method Rational."<init>":(I)V
   8:   areturn

new首先创建一个Rational对象并将其引用值压入栈顶,dup复制栈顶数值并将复制值压入栈顶,iload_1将第二个int型本地变量推送至栈顶,invokespecial调用Rational的构造方法,最后areturn从当前方法返回对象引用。

我们用如下代码来测试隐式类型转换:

import Rational._

object RationalTest {
  def main(args: Array[String]) {
	val r = new Rational(2,3)
    println(2 * r)
  }
}

2 * r本身不合法的,因为Int不存在*(Rational)的方法,由于隐式转换的存在,Scala将做一些转换工作。上面程序的汇编代码如下:

Compiled from "RationalTest.scala"
public final class RationalTest$ extends java.lang.Object implements scala.Scala
Object{
public static final RationalTest$ MODULE$;

public static {};
  Code:
   0:   new     #9; //class RationalTest$
   3:   invokespecial   #12; //Method "<init>":()V
   6:   return

public void main(java.lang.String[]);
  Code:
   0:   new     #16; //class Rational
   3:   dup
   4:   iconst_2
   5:   iconst_3
   6:   invokespecial   #20; //Method Rational."<init>":(II)V
   9:   astore_2
   10:  getstatic       #25; //Field Rational$.MODULE$:LRational$;
   13:  iconst_2
   14:  invokevirtual   #29; //Method Rational$.intToRational:(I)LRational;
   17:  aload_2
   18:  invokevirtual   #33; //Method Rational.$times:(LRational;)LRational;
   21:  astore_3
   22:  getstatic       #38; //Field scala/Console$.MODULE$:Lscala/Console$;
   25:  aload_3
   26:  invokevirtual   #42; //Method scala/Console$.println:(Ljava/lang/Object;
)V
   29:  return

}

在做乘法($times)之前调用了 intToRational,返回一个Rational对象, 调用Rational对象的*方法已经合法化了。
展开阅读全文
加载中
点击加入讨论🔥(4) 发布并加入讨论🔥
4 评论
2 收藏
0
分享
返回顶部
顶部