符号的输入过程涉及到两个重要的类,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);
经过如上步骤后,一个完整的语法树就建立了,如下:
可以看到如上的好多其它属性为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属性已经有值了,如下:
调用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符号各个属性如下:
专门为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如下截图:
为这个语法节点建立关连环境,如下:
/**
* 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。