我是靠谱客的博主 大力烧鹅,最近开发中收集的这篇文章主要介绍Groovy实现原理简单研究(2),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

上回说到:
[b]groovy负责词法、语法分析groovy文件,然后用asm生成普通的class文件,供jvm使用[/b]

这回稍许详细的分析一下源码。
我这里还是选用的最早期的groovy的初版。
整理好的代码可在附件下载,经过改装,可在maven2, maven3下编译通过,并导入到eclipse中。我这里就叫他为groovy0.1吧。

主程序在Compiler里,编译流程为3阶段。stageOne,stageTwo,stageThree。

[b]1.stageOneCompile[/b]

代码都在以下3个包里:
[list]
[*]org.codehaus.groovy.syntax
[*]org.codehaus.groovy.syntax.lexer 词法分析
[*]org.codehaus.groovy.syntax.parser 语法分析
[/list]

Compiler.java


protected CSTNode stageOneCompile(File file)
throws Exception
{
LOG.info( "stage-1 compiling: " + file );

FileInputStream fileIn = new FileInputStream( file );
BufferedInputStream bufferedIn = new BufferedInputStream( fileIn );
InputStreamCharStream charStream = new InputStreamCharStream( bufferedIn );

try
{
Lexer lexer = new Lexer( charStream );
Parser parser = new Parser( new LexerTokenStream( lexer ) );

return parser.compilationUnit();
}
finally
{
charStream.close();
}
}

大致就是Lexer负责词法分析,Parser负责语法分析,生成CST。

[b]1.1 词法分析[/b]
也就是拆词,或者有点lucene里的分词的意思。
比如 "package cheese.toast"
Lexer.nextToken每次调用返回的Token依次是
package
cheese
.
toast


[b]1.2 语法分析。生成具体语法树(CST)[/b]
可参考ParserTest.testPackageDeclaration_OneDot()
比如 "package abc.def.ghi"

调用以下代码,
CSTNode root = parser.packageDeclaration();


解析以后变成一棵树,
package
|--- .(dot)
|--- .(dot)
| |----abc
| |----def
|----ghi


以上2步都属于Compiler.stageOneCompile
stageOneCompile会去调用Parser.compilationUnit(),得到一个解析好的语法树。

[b]2.stageTwoCompile[/b]
语义合法验证,内容暂时是空的,先略过。

[b]3. stageThreeCompile[/b]

这里除了ASTBuilder类在org.codehaus.groovy.syntax.parser以外,
还有代码集中在org.codehaus.groovy.ast包中。

Compiler.java

protected void stageThreeCompile(CSTNode compilationUnit,
File file )
throws Exception
{
ASTBuilder astBuilder = new ASTBuilder( getClassLoader() );

ClassNode[] classNodes = astBuilder.build( compilationUnit );

for ( int i = 0 ; i < classNodes.length ; ++i )
{
dumpClass( classNodes[ i ],
file );
}
}

可以看到2个步骤,先build AST,然后dump class。

[b]3.1 生成抽象语法树(AST)[/b]
ASTBuilder.build()

关于CST和AST的区别可以参考
[url]http://eli.thegreenplace.net/2009/02/16/abstract-vs-concrete-syntax-trees[/url]
简单来说,AST就是将CST简化了,去掉以及合并了很多没用的节点,比如去掉分号啦,合并abc.def.ghi这种带有好多点的包名等等。

比如包名,CST转成AST以后,大致像下面这般模样:
package
|--- abc.def.ghi

如图,生成的抽象语法树有如下这些节点,所有的节点都是继承自ASTNode。
[img]http://dl2.iteye.com/upload/attachment/0109/7834/11d9fcad-38a7-3de4-8618-b814458de9da.png[/img]

比如最基本的,一个类的父亲节点是ClassNode,它的儿子有FieldNode和MethodNode分别代表属性和方法。

[b]3.1.1 getter/setter[/b]
我们研究下groovy在哪里自动生成getter/setter的。
查看代码ClassNode.addProperty()

public void addProperty(PropertyNode node) {
FieldNode field =
new FieldNode(node.getName(), ACC_PRIVATE, node.getType(), getName(), node.getInitialValueExpression());
addField(field);

String name = node.getName();
String getterName = "get" + capitalize(name);
String setterName = "set" + capitalize(name);

Statement getterBlock = node.getGetterBlock();
if (getterBlock == null) {
getterBlock = createGetterBlock(node, field);
}
Statement setterBlock = node.getGetterBlock();
if (setterBlock == null) {
setterBlock = createSetterBlock(node, field);
}

MethodNode getter =
new MethodNode(
getterName,
node.getModifiers(),
node.getType(),
Parameter.EMPTY_ARRAY,
getterBlock);

addMethod(getter);

Parameter[] setterParameterTypes = { new Parameter(node.getType(), "value")};
MethodNode setter =
new MethodNode(setterName, node.getModifiers(), "void", setterParameterTypes, setterBlock);
addMethod(setter);


properties.add(node);
}

可以发现在增加属性节点的同时,就给自动生成了2个方法节点,getter和setter。

[b]3.2 dumpClass[/b]
这里既是用ASM生成java字节码了,毋庸置疑,用到了有名的visitor模式。

protected void dumpClass(ClassNode classNode,
File file)
throws Exception
{
ClassWriter classWriter = new ClassWriter( true );
ClassGenerator classGenerator = new ClassGenerator( classWriter,
getClassLoader(),
file.getName() );

classGenerator.visitClass( classNode );

byte[] code = classWriter.toByteArray();

File outputFile = createOutputFile( classNode.getName() );

if ( ! outputFile.getParentFile().exists() )
{
outputFile.getParentFile().mkdirs();
}

LOG.info( "generating class to: " + outputFile );

FileOutputStream out = new FileOutputStream( outputFile );

try
{
out.write( code );
}
finally
{
out.close();
}
}

ASM和visitor模式暂时不展开了。后面有机会再深入分析。

最后

以上就是大力烧鹅为你收集整理的Groovy实现原理简单研究(2)的全部内容,希望文章能够帮你解决Groovy实现原理简单研究(2)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部