(openJDK)Javac1.7编译器源码分析_001_简介与安装


网站: JavaEye 作者: zhh2007 发表时间: 2007-05-30 23:51 此文章来自于 http://www.iteye.com
声明:本文系JavaEye网站原创文章,未经JavaEye网站或者作者本人书面许可,任何其他网站严禁擅自发表本文,否则必将追究法律责任!
原文链接: http://www.iteye.com/topic/84833

(openJDK)Javac1.7编译器源码分析_001_简介与安装

(第一次写Blog,此文只是本人的一点点学习记录,不经本人事先同意请勿随意转载,谢谢)


1.1:简介:


1.1.1 javac版本


SUN公司在2006年11月份已将java语言编译器(javac)源码通过GPLv2的方式开源,

还建立了“the Open-Source JDK Community”,网址是:http://openjdk.java.net/





截止到写这篇文章时,最新的javac Source Release已到了b13,

一般是半个月出一个beta版,从b04到b13变化都不大,javac类文件没有添加一个。

本文以及后续文章都是基于以下版本:

compiler-7-ea-src-b10-21_mar_2007 (为了方便起见,以后都简写成javac1.7)




1.1.2 我最初想到要分析javac源码的原因


a 看编译原理的书理论太多,想找个实际的编译器验证一下理论

b 之前在JDK1.4下写过一些java程序

c 对javac感兴趣,想要了解所有细节

d 要是还想分析HotSpot VM,先看javac有利

e 偶尔想到要反编译别人的代码




1.1.3 分析javac源码前最好具备的条件


a 编译原理的知识基本上忘记了不要紧,

  但还记得有LL(1)文法、递归下降算法、运算符优先这几个名词


b 知道基本的java语言语法,

  要是像我一样起初不懂Java 5之后的语言新特性也不要紧


c 越有耐心越好




1.1.4 java语言编译器是用什么语言编写的?


javac1.7的源代码本身是用java语言写的(用编译原理的术语叫“自举”),

源代码总行数不到8万行。


在James Gosling的blog上有一篇文章:

“Compiler fun” (http://blogs.sun.com/jag/entry/compiler_fun)

里面有提到最初的java语言编译器是用C语言写成的,不过我不能准确的知道从哪个

JDK发行版开始正式用java语言重写编译器,我尝试在JDK1.4到JDK1.6的tools.jar文件中

寻找javac,只能确定在JDK1.4到JDK1.6内嵌的java语言编译器都是java语言写的。


JDK1.6中的javac与开源的javac1.7生成的字节码几乎没有差别,这也肯定了一点:

开源的javac1.7并不是SUN公司为开源社区单独开发的一个版本,

javac1.7最终也会纳入未来的JDK1.7一起发行。


在“the Open-Source JDK Community”的邮件列表上也有一封邮件证实了这一点:

主题:OpenJDk7 opensource compiler final Java 7 compiler?

URL :https://openjdk.dev.java.net/servlets/ReadMsg?list=compiler-dev&msgNo=270




1.1.5 javac1.7编译流程概述


javac1.7的编译流程可以简单分为以下主要阶段:

(只大概说了一下,还有无数的细节问题会在以后详述,

有看到很讨厌的名词可以直接跳过这一节)


1) javac命令行选项及选项参数处理


   这一阶段主要是识别javac命令行选项是否是合法的,选项参数是否正确




2) 词法分析


   从头到尾分析源文件的字符流,形成关键字、标识符、数字、运算符等等

   具有独立意义的token序列,并去掉源文件中的空白与注释。




3) 语法分析


   词法分析在javac1.7中其实是做为语法分析阶段的一个过程(或方法)来

   驱动的,整个语法分析阶段完成的任务就是按照java语言的LL(1)文法

   采用递归下降算法并结合运算符优先规则,对token序列构造一棵

   抽象语法树(abstract syntax tree)




4) 符号识别(符号表)


   一个包、一个类、一个方法、一个字段都可以抽象成一个符号(symbol),

   不同种类的符号之间可以有包含嵌套关系:

   一个包符号可以包含多个类符号,

   一个类符号可以包含多个方法符号与多个字段符号。


   这一阶段的任务就是识别出各类符号,并对不同种类的符号按

   照包含嵌套关系进行归类,并挂接到抽象语法树对应的结点域。




5) 注释处理(可选阶段)




6) 属性分析(或称语义分析)

   这一阶段包含了一个复杂的类型系统,语义分析阶段检查语言规范中

   定义的各类规则




7) 数据流分析


   这一阶段主要是检查final类型的字段与方法中定义的局部变量是否被赋值




8) Desugar


   在Java 5之后增加了很多语言新特性,但是JVM规范却没有什么变动,

   JVM指令集也没有增加,所以在这一阶段的任务主要是把采用新语言

   特性写成的源代码自动翻译转换成未使用新特性写成的代码。

   如:泛型类到普通类的转换,enhanced for loop到普通for loop的转换。

 


9) 代码生成


   构造常量池、筛选指令、生成class文件。




9) 错误处理:


   错误处理贯穿编译流程的所有阶段




其他:

javac1.7中没有使用像Lex、YACC这样的生成器工具,

词法、语法分析与代码生成全都是手工实现的,具有简单、灵活、高效的

特点,传统编译原理课本上讲授的知识更具通用性,但过于复杂,效率也是

个问题。

另外,可惜的是javac1.7(包括sun公司以往发行的JDK中内置的javac)不是

一个优化编译器,javac1.7并没有独立的优化阶段,

散落在其他阶段的“优化”可以省略不计。


比如:(例子不考虑人的因素,只考虑编译器的行为):


java 代码




  1. package my.test;   
  2. public class Test {   
  3.     Test() {   
  4.         int v1=1;   
  5.         while(v1<10000) {   
  6.             int v2=5;   
  7.             v1=v1+v2*2;   
  8.         }   
  9.     }   
  10. }  


用javac1.7或JDK1.6.0生成的字节码如下(部分内容)

(用命令行“javap -verbose my.test.Test”查看):

---------------------------------------------------

my.test.Test();

  Code:

   Stack=3, Locals=3, Args_size=1

   0: aload_0

   1: invokespecial #1; //Method java/lang/Object."":()V

   4: iconst_1  //将常量1压入堆栈

   5: istore_1  //将常量1弹出堆栈并存入局部变量v1,

   6: iload_1   //将局部变量v1的值压入堆栈

   7: sipush 10000   //将常量10000压入堆栈

   10: if_icmpge 24  //弹出常量10000,弹出局部变量v1的值,局部变量v1的值>=10000结束循环

   13: iconst_5  //将常量5压入堆栈

   14: istore_2  //将常量5弹出堆栈并存入局部变量v2,

   15: iload_1   //将局部变量v1的值压入堆栈

   16: iload_2   //将局部变量v2的值压入堆栈

   17: iconst_2  //将常量2压入堆栈

   18: imul      //弹出常量2,弹出局部变量v2的值,相乘后将结果压入堆栈

   19: iadd      //弹出结果,弹出局部变量v1的值,相加后将结果压入堆栈

   20: istore_1  //弹出结果并存入局部变量v1

   21: goto 6 //转到6: iload_1

   24: return

---------------------------------------------------


理想的优化编译器应该能生成如下类似的代码:


java 代码




  1. package my.test;   
  2. public class Test {   
  3.     Test() {   
  4.         int v1=1;   
  5.         while(v1<10000) {   
  6.             v1=v1+10;//v2=5与v2*2总是不变的,可以合并成10   
  7.         }   
  8.     }   
  9. }  




更理想的优化编译器应该能生成如下类似的代码:


java 代码




  1. package my.test;   
  2. public class Test {   
  3.     Test() {}//局部变量v1没有任何用处,完全可以删除   
  4. }  


关于编译流程简短说明的文档也可参考

Peter von der Ahé(javac编译器的主要开发者,前段时间已离开SUN公司)

在“the Open-Source JDK Community”的邮件列表上回复的一封邮件

主题:A set ot tutorials about the compiler

URL :https://openjdk.dev.java.net/servlets/ReadMsg?list=compiler-dev&msgNo=89




1.1.6 在没有任何设计文档的前提下如何分析javac1.7源码?


我只说说我采用的方法(我是第一次分析别人的源代码):

1) 找到一种最简单的办法完成源代码的第一次编译

2) 找到第一个开始运行的类文件(也就是找到切入点)

3) 粗略看一下这个类文件定义了哪些字段,有构造方法的话,

   粗略看一下构造方法中做了哪些初始化工作

4) 要是定义的字段、构造方法太多,把它们都copy一份,单独放到一个文件


5) 找到第一个被运行的方法,

   在方法开头和末尾打上Debug(包装System.out.println()后写成的一个类),

   觉得关键的字段(或局部变量),用自己喜欢的方式也打上Debug,

   如果类文件源码超过200行(javac1.7源码中有许多核心类文件大多超过1000行),

   为了切换方便,把这方法copy一份,单独放到一个文件


6) 当在一个方法中调用了另一个类的方法时,转到3)

7) 当一个类文件中定义的方法有85%都已Debug过了,从头到尾细细分析一遍类文件

8) Debug的输出信息最好重定向到一个自定义的文件

9) 方法中有复杂算法时最好用笔画在纸上


10)有很多个方法同时来回调用时,把每个方法按调用的顺序单独打开,对照Debug

   信息一起看(我经常打开5、6个EditPlus窗口实例同时看20几个类文件)


11)记住随心所欲地想把一个个类文件不打Debug、不按流程顺序分析,是非常低效

   的一种方法,除非这个类文件很独立,只是简单的字段值存取功能。


 


1.2:安装


1.2.1 运行环境


我的OS是Windows XP


建议安装JDK1.6,我的JDK版本是

-------------------------------

java version "1.6.0-beta2"

Java(TM) SE Runtime Environment (build 1.6.0-beta2-b86)

Java HotSpot(TM) Client VM (build 1.6.0-beta2-b86, mixed mode, sharing)

-------------------------------


安装JDK后,请在系统变量Path中加入 %JAVA_HOME%\bin,

其中%JAVA_HOME%是JDK安装目录。




1.2.2 下载javac1.7源码


下载地址:

http://www.java.net/download/openjdk/jdk7/promoted/b10/compiler-7-ea-src-b10-21_mar_2007.zip

解压后会有一个“compiler”目录,

如果你习惯使用Ant、NetBeans或其他IDE工具编译源码,请直

接参考“compiler\README.html”文件,然后跳到“第1.3节”;

如果你像我一样不会用(或不想用)IDE或想操控一切,

请按下面的简单步骤操作:




1.2.3 下载附件中的"Javac.jar"文件,解压到一个目录(这里以“F:\Javac”为例)

(注:"Javac.jar"文件只包含了“compiler\src\share\classes”目录下的

两个子目录“com”与“javax”,其他子目录或文件都是我自建的,javac1.7源码中

还包含了无数的测试用例,我觉得它太繁琐了,就自己一边看源码,一边写自己的

测试例子,所以我把它省略掉了)




1.2.4 编译javac1.7源码


打开一个Dos命令行窗口,切换到“F:\Javac”目录

输入“com”按回车,稍等片刻就可以完成编译




1.2.5 用javac1.7编译其他java源文件


打开一个Dos命令行窗口,切换到“F:\Javac”目录

输入“run”按回车,打开“F:\Javac\myout.txt”文件,就可以看到Debug信息。

在F:\Javac\run.bat文件中默认是编译F:\Javac\bin\mysrc\my\test\Test.java文件

你可以随意替换成你自己的java源文件


1.2.6 javac1.7源码的切入点是F:\Javac\com\sun\tools\javac\Main.java文件


 


1.3:几个有帮助的网址


James Gosling 的blog:

http://blogs.sun.com/jag/


Peter von der Ahé 的blog:

http://blogs.sun.com/ahe/


the Open-Source JDK Community:

http://openjdk.java.net/


The Java Language Specification, Third Edition:

http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html


The JavaTM Virtual Machine Specification Second Edition

http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html




<<深入Java虚拟机(原书第2版)>>

作者:Bill Venners

译者:曹晓钢,蒋靖 


书译得还可以,不过还是有很多印刷错误,

第6章和附录对理解代码生成有一定价值。


中文版地址:

http://www.china-pub.com/computers/common/info.asp?id=14719


Bill Venners维护的网站:

http://www.artima.com/insidejvm/resources/




JDK6的文档(特别是语言新特性部分)也有参考价值





《 (openJDK)Javac1.7编译器源码分析_001_简介与安装 》 的评论也很精彩,欢迎您也添加评论。查看详细 >>

推荐相关文章:
   正确认识memcached的缓存失效
   ThreadLocalizer




JavaEye推荐
上海乐福狗信息技术有限公司:诚聘技术经理和开发工程师
免费下载IBM社区版软件--它基于开放的标准,支持广泛的开发类型,让您的开发高效自主!
京沪穗蓉四地免费注册,SOA技术高手汇聚交锋.
上海:优秀公司德比:高薪诚聘 资深Java工程师
广州:优易公司:诚聘Java工程师,开发经理
上海:尤恩斯国际集团:诚聘开发工程师
北京:优秀公司NHNChina招聘:WEB开发,系统管理,JAVA开发, DBA


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值