Groovy实现原理简单研究(2)

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

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

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

1.stageOneCompile

代码都在以下3个包里:
  • org.codehaus.groovy.syntax
  • org.codehaus.groovy.syntax.lexer 词法分析
  • org.codehaus.groovy.syntax.parser 语法分析


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。

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


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

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


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

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

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

3. stageThreeCompile

这里除了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。

3.1  生成抽象语法树(AST)
ASTBuilder.build()

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

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

如图,生成的抽象语法树有如下这些节点,所有的节点都是继承自ASTNode。


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

3.1.1 getter/setter
我们研究下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。

3.2 dumpClass
这里既是用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模式暂时不展开了。后面有机会再深入分析。

猜你喜欢

转载自xpenxpen.iteye.com/blog/2222261
今日推荐