符号的输入过程涉及到两个重要的类,Enter与MemberEnter类。两个类都实现了JCTree.visitor抽象类。首先来看Enter类,

 

举个具体的例子,如下:



package m20170327;

import java.util.ArrayList;

public class Test<T> {

	ArrayList<String> list = new ArrayList<String>();
	
	public void test(){
		T a;
	}
}



首先要在JavaCompiler类中做如下的处理:



initProcessAnnotations(processors); // 初始化注解处理
List<JCCompilationUnit> a = parseFiles(sourceFileObjects); // 词法分析,语法分析
List<JCCompilationUnit> b = stopIfError(CompileState.PARSE,a);



经过如上步骤后,一个完整的语法树就建立了,如下:

useSelector useDispatch 如何在类中 如何在类中输入_sed

  

useSelector useDispatch 如何在类中 如何在类中输入_java_02

可以看到如上的好多其它属性为null,如type和classSymbol、methodSymbol等符号属性,接下来就去填充这些属性。

调用Enter中的classEnter()方法,如下:



<T extends JCTree> List<Type> classEnter(List<T> trees, Environment<AttrContext> environment) { // trees中有个JCCompilationUnit,environment为null
        ListBuffer<Type> ts = new ListBuffer<Type>();
        for (List<T> tree = trees; tree.nonEmpty(); tree = tree.tail) {
            Type type = classEnter(tree.head, environment);
            if (type != null){
                ts.append(type);
            }
        }
        return ts.toList();
    }



 



/** Visitor method: enter all classes in given tree, catching any
     *  completion failure exceptions. Return the tree's type.
     *
     *  @param jcTree    The tree to be visited.
     *  @param environment     The env visitor argument.
     */
    Type classEnter(JCTree jcTree, Environment<AttrContext> environment) {
        Environment<AttrContext> prevEnv = this.environment;
        try {
            this.environment = environment;
            jcTree.accept(this);
            return result;
        }  catch (CompletionFailure ex) {
            return check.completionError(jcTree.position(), ex);
        } finally {
            this.environment = prevEnv;
        }
    }



jcTree.accept(this)方法最终会调用visitTopLevel()方法,首先填充packageSymbol属性,如下:



if (jcCompilationUnit.packageIdentifier != null) {

            Name fullName = TreeInfo.fullName(jcCompilationUnit.packageIdentifier);
            jcCompilationUnit.packageSymbol = classReader.enterPackage(fullName);

            if (jcCompilationUnit.packageAnnotations.nonEmpty() || pkginfoOpt == PkgInfo.ALWAYS) {
                if (isPkgInfo) {
                    addEnvironment = true;
                } else {
                    // 程序包注释应在文件 package-info.java 中
                    log.error(jcCompilationUnit.packageAnnotations.head.position(),"pkg.annotations.sb.in.package-info.java");
                }
            }
} else {
            jcCompilationUnit.packageSymbol = symbolTable.unnamedPackage;
}



此时packageSymbol属性已经有值了,如下:

useSelector useDispatch 如何在类中 如何在类中输入_List_03

调用packageSymbol符号中的complete()方法,为这个符号填充一些信息,如members_field,并且为Scope中的Entry添加符号。

packageSymbol中调用了ClassReader类的complete()方法来完成,这个类中有两个重要的属性,如下:



/** A hashtable containing the encountered top-level and member classes,
     *  indexed by flat names. The table does not contain local classes.
     */
    private Map<Name,ClassSymbol> classes;

    /** A hashtable containing the encountered packages.
     */
    private Map<Name, PackageSymbol> packages;



保存了flatName到符号的映射。

新建了ClassSymbol符号,并且向packageSymbol的members_field属性中输入了这个符号,如下:



classSymbol = enterClass(classname, packageSymbol);

            if (classSymbol.classfile == null) { // only update the file if's it's newly created
                classSymbol.classfile = file;
            }

            if (isPkgInfo) {
                packageSymbol.package_info = classSymbol;
            } else {
                if (classSymbol.ownerSymbol == packageSymbol){ // it might be an inner class
                    packageSymbol.members_field.enter(classSymbol);
                }
            }



其中的ClassSymbol符号各个属性如下:

useSelector useDispatch 如何在类中 如何在类中输入_java_04

 

专门为JCCompilationUnit语法节点创建一个topLevelEnvironment,如下:



/** Create a fresh env for toplevels.
     *  @param jcCompilationUnit     The toplevel tree.
     */
    Environment<AttrContext> topLevelEnvironment(JCCompilationUnit jcCompilationUnit) {
        Environment<AttrContext> localEnvironment = new Environment<AttrContext>(jcCompilationUnit, new AttrContext());
        localEnvironment.toplevel = jcCompilationUnit;
        localEnvironment.enclosingClass = predefinedClassDef;
        
        jcCompilationUnit.namedImportScope = new ImportScope(jcCompilationUnit.packageSymbol);
        jcCompilationUnit.starImportScope = new StarImportScope(jcCompilationUnit.packageSymbol);

        // 注意这个scope是namedImportScope
        localEnvironment.info.scope = jcCompilationUnit.namedImportScope;
        
        localEnvironment.info.lint = lint;
        return localEnvironment;
    }



为JCCompilationUnit语法节点填充了namedImportScope与starImportScope属性。 

接下来要处理JCCompilationUnit中的defs属性中的值了。从上截图中可以看出有JCImport与JCClassDeclaration两个,但是JCImport节点不在这一阶段处理。



classEnter(jcCompilationUnit.defs, topLevelEnvironment);



来看具体的JCClassDeclaration节点,调用visitClassDefinition(JCClassDeclaration jcClassDeclaration)方法,下面就来填充这个语法节点的各个属性吧。

 



jcClassDeclaration.classSymbol = classSymbol;
        classSymbol.completer = memberEnter;
        classSymbol.flags_field = check.checkFlags(jcClassDeclaration.position(), jcClassDeclaration.modifiers.flags, classSymbol, jcClassDeclaration);
        classSymbol.sourcefile = environment.toplevel.sourcefile;
        classSymbol.members_field = new Scope(classSymbol);
        classType.typarams_field = classEnter(jcClassDeclaration.typeParameters, localEnvironment);



 

将这个ClassSymbol放到uncompleted队列中,稍后继续进行处理,如下:



/** The queue of all classes that might still need to be completed;
     *  saved and initialized by main().
     *  
     *  队列中所有的类仍然需要被完成。在main方法中完成存储和初始化
     */
    ListBuffer<ClassSymbol> uncompleted;



完善后的ClassSymbol如下截图:

useSelector useDispatch 如何在类中 如何在类中输入_sed_05

  

为这个语法节点建立关连环境,如下:



/** 
     * Create a fresh env for class bodies.
     *  
     *  This will create a fresh scope for local symbols of a class, referred
     *  to 被引用  by the environments info.scope field.
     *  This scope will contain
     *    - symbols for this and super
     *    - symbols for any type parameters
     *
     *  In addition, it serves as an anchor for scopes of methods and initializers
     *  which are nested in this scope via Scope.duplicate(). ??
     *
     *  This scope should not be confused with the members scope of a class.
     *
     *
     *  @param jcClassDeclaration     The class definition.
     *  @param environment      The env current outside of the class definition.
     */
    public Environment<AttrContext> classEnvironment(JCClassDeclaration jcClassDeclaration, Environment<AttrContext> environment) {

        Scope scope = new Scope(jcClassDeclaration.classSymbol);
        AttrContext attrContext = environment.info.duplicate(scope);
	Environment<AttrContext> localEnvironment = environment.duplicate(jcClassDeclaration, attrContext);

        localEnvironment.enclosingClass = jcClassDeclaration; // The next enclosing class definition.
        localEnvironment.outer = environment;
        localEnvironment.info.isSelfCall = false;
        localEnvironment.info.lint = null; // leave this to be filled in by Attr,when annotations have been processed

        return localEnvironment;
    }



  

下面来继承处理JCClassDeclaration的defs属性,分别有:

(1)JCVariableDeclaration  这个阶段不进行任何处理

(2)JCMethodDeclaration 这个阶段不进行任何处理  

  

 需要补充说明的是ClassReader继承了Completer接口,这个接口定义在Symbol类中,如下:



/** Symbol completer interface.
     */
    public static interface Completer {
        void complete(Symbol sym) throws CompletionFailure;
    }



这个接口的实现类主要有两个,ClassReader和MemberEnter。在MemberEnter中主要就是为了完成classSymbol。