466
技術社區[雲棲]
Javac源碼簡單分析之解析和填充符號表
一、說明
符號表是由一組符號地址和符號信息構成的表格。符號表中所登記的信息在編譯的不同階段都要用到,在語義分析(後麵的步驟)中,符號表所登記的內容將用於語義檢查和產生中間代碼,在目標代碼生成階段,黨對符號名進行地址分配時,符號表是地址分配的依據。二、主要的類與方法
解析和填充符號表這個過程主要由com.sun.tools.javac.comp.Entry及com.sun.tools.javac.comp.MemberEnter兩個類來實現的。
com.sun.tools.javac.comp.Entry 主要的方法如下:
/** * 訪問類聲明 */ public void visitClassDef(JCClassDecl tree) { Symbol owner = env.info.scope.owner; Scope enclScope = enterScope(env); ClassSymbol c; if (owner.kind == PCK) { // We are seeing a toplevel class. PackageSymbol packge = (PackageSymbol)owner; for (Symbol q = packge; q != null && q.kind == PCK; q = q.owner) q.flags_field |= EXISTS; c = reader.enterClass(tree.name, packge); packge.members().enterIfAbsent(c); if ((tree.mods.flags & PUBLIC) != 0 && !classNameMatchesFileName(c, env)) { log.error(tree.pos(), "class.public.should.be.in.file", tree.name); } } else { if (tree.name.len != 0 && !chk.checkUniqueClassName(tree.pos(), tree.name, enclScope)) { result = null; return; } if (owner.kind == TYP) { // We are seeing a member class. c = reader.enterClass(tree.name, (TypeSymbol)owner); if ((owner.flags_field & INTERFACE) != 0) { tree.mods.flags |= PUBLIC | STATIC; } } else { // We are seeing a local class. c = reader.defineClass(tree.name, owner); c.flatname = chk.localClassName(c); if (c.name.len != 0) chk.checkTransparentClass(tree.pos(), c, env.info.scope); } } tree.sym = c; // Enter class into `compiled' table and enclosing scope. if (chk.compiled.get(c.flatname) != null) { duplicateClass(tree.pos(), c); result = new ErrorType(tree.name, (TypeSymbol)owner); tree.sym = (ClassSymbol)result.tsym; return; } chk.compiled.put(c.flatname, c); enclScope.enter(c); // Set up an environment for class block and store in `typeEnvs' // table, to be retrieved later in memberEnter and attribution. Env<AttrContext> localEnv = classEnv(tree, env); typeEnvs.put(c, localEnv); // Fill out class fields. c.completer = memberEnter; c.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, c, tree); c.sourcefile = env.toplevel.sourcefile; c.members_field = new Scope(c); ClassType ct = (ClassType)c.type; if (owner.kind != PCK && (c.flags_field & STATIC) == 0) { // We are seeing a local or inner class. // Set outer_field of this class to closest enclosing class // which contains this class in a non-static context // (its "enclosing instance class"), provided such a class exists. Symbol owner1 = owner; while ((owner1.kind & (VAR | MTH)) != 0 && (owner1.flags_field & STATIC) == 0) { owner1 = owner1.owner; } if (owner1.kind == TYP) { ct.setEnclosingType(owner1.type); } } // Enter type parameters. ct.typarams_field = classEnter(tree.typarams, localEnv); // Add non-local class to uncompleted, to make sure it will be // completed later. if (!c.isLocal() && uncompleted != null) uncompleted.append(c); // System.err.println("entering " + c.fullname + " in " + c.owner);//DEBUG // Recursively enter all member classes. classEnter(tree.defs, localEnv); result = c.type; } /** Main method: enter all classes in a list of toplevel trees. * @param trees The list of trees to be processed. */ public void main(List<JCCompilationUnit> trees) { complete(trees, null); } /** * Main method: enter one class from a list of toplevel trees and * place the rest on uncompleted for later processing. * @param trees The list of trees to be processed. * @param c The class symbol to be processed. */ public void complete(List<JCCompilationUnit> trees, ClassSymbol c) { annotate.enterStart(); ListBuffer<ClassSymbol> prevUncompleted = uncompleted; if (memberEnter.completionEnabled) uncompleted = new ListBuffer<ClassSymbol>(); try { // enter all classes, and construct uncompleted list classEnter(trees, null); // complete all uncompleted classes in memberEnter if (memberEnter.completionEnabled) { while (uncompleted.nonEmpty()) { ClassSymbol clazz = uncompleted.next(); if (c == null || c == clazz || prevUncompleted == null) clazz.complete(); else //將類符號放入prevUncompleted列表(uncompleted列表) prevUncompleted.append(clazz); } // if there remain any unimported toplevels (these must have // no classes at all), process their import statements as well. /** * uncompleted列表沒有的符號(除類符號外),根據improt聲明,給頂級抽象樹都添加了一個MemberEnter對象 * 這些符號(包括類的參數類型符號也就是泛型、父類符號、接口類型符等) */ for (JCCompilationUnit tree : trees) { if (tree.starImportScope.elems == null) { JavaFileObject prev = log.useSource(tree.sourcefile); Env<AttrContext> env = typeEnvs.get(tree); if (env == null) env = topLevelEnv(tree); memberEnter.memberEnter(tree, env); log.useSource(prev); } } } } finally { //prevUncompleted列表賦值給uncompleted列表 uncompleted = prevUncompleted; annotate.enterDone(); } }
com.sun.tools.javac.comp.MemberEnter 主要的方法如下:
/** Complete entering a class. * 將未處理列表中的所有符號都解析到各自的類符號表中 * @param sym The symbol of the class to be completed. */ public void complete(Symbol sym) throws CompletionFailure { // Suppress some (recursive) MemberEnter invocations if (!completionEnabled) { // Re-install same completer for next time around and return. assert (sym.flags() & Flags.COMPOUND) == 0; sym.completer = this; return; } ClassSymbol c = (ClassSymbol)sym; ClassType ct = (ClassType)c.type; Env<AttrContext> env = enter.typeEnvs.get(c); JCClassDecl tree = (JCClassDecl)env.tree; boolean wasFirst = isFirst; isFirst = false; JavaFileObject prev = log.useSource(env.toplevel.sourcefile); try { // Save class environment for later member enter (2) processing. halfcompleted.append(env); // If this is a toplevel-class, make sure any preceding import // clauses have been seen. if (c.owner.kind == PCK) { memberEnter(env.toplevel, env.enclosing(JCTree.TOPLEVEL)); todo.append(env); } // Mark class as not yet attributed. c.flags_field |= UNATTRIBUTED; if (c.owner.kind == TYP) c.owner.complete(); // create an environment for evaluating the base clauses Env<AttrContext> baseEnv = baseEnv(tree, env); // Determine supertype. Type supertype = (tree.extending != null) ? attr.attribBase(tree.extending, baseEnv, true, false, true) : ((tree.mods.flags & Flags.ENUM) != 0 && !target.compilerBootstrap(c)) ? attr.attribBase(enumBase(tree.pos, c), baseEnv, true, false, false) : (c.fullname == names.java_lang_Object) ? Type.noType : syms.objectType; ct.supertype_field = supertype; // Determine interfaces. ListBuffer<Type> interfaces = new ListBuffer<Type>(); Set<Type> interfaceSet = new HashSet<Type>(); List<JCExpression> interfaceTrees = tree.implementing; if ((tree.mods.flags & Flags.ENUM) != 0 && target.compilerBootstrap(c)) { // add interface Comparable<T> interfaceTrees = interfaceTrees.prepend(make.Type(new ClassType(syms.comparableType.getEnclosingType(), List.of(c.type), syms.comparableType.tsym))); // add interface Serializable interfaceTrees = interfaceTrees.prepend(make.Type(syms.serializableType)); } for (JCExpression iface : interfaceTrees) { Type i = attr.attribBase(iface, baseEnv, false, true, true); if (i.tag == CLASS) { interfaces.append(i); chk.checkNotRepeated(iface.pos(), types.erasure(i), interfaceSet); } } if ((c.flags_field & ANNOTATION) != 0) ct.interfaces_field = List.of(syms.annotationType); else ct.interfaces_field = interfaces.toList(); if (c.fullname == names.java_lang_Object) { if (tree.extending != null) { chk.checkNonCyclic(tree.extending.pos(), supertype); ct.supertype_field = Type.noType; } else if (tree.implementing.nonEmpty()) { chk.checkNonCyclic(tree.implementing.head.pos(), ct.interfaces_field.head); ct.interfaces_field = List.nil(); } } // Annotations. // In general, we cannot fully process annotations yet, but we // can attribute the annotation types and then check to see if the // @Deprecated annotation is present. attr.attribAnnotationTypes(tree.mods.annotations, baseEnv); if (hasDeprecatedAnnotation(tree.mods.annotations)) c.flags_field |= DEPRECATED; annotateLater(tree.mods.annotations, baseEnv, c); attr.attribTypeVariables(tree.typarams, baseEnv); chk.checkNonCyclic(tree.pos(), c.type); /** * 增加一個默認的構造方法(當類沒有構造方法時) */ if ((c.flags() & INTERFACE) == 0 && !TreeInfo.hasConstructors(tree.defs)) { List<Type> argtypes = List.nil(); List<Type> typarams = List.nil(); List<Type> thrown = List.nil(); long ctorFlags = 0; boolean based = false; if (c.name.len == 0) { JCNewClass nc = (JCNewClass)env.next.tree; if (nc.constructor != null) { Type superConstrType = types.memberType(c.type, nc.constructor); argtypes = superConstrType.getParameterTypes(); typarams = superConstrType.getTypeArguments(); ctorFlags = nc.constructor.flags() & VARARGS; if (nc.encl != null) { argtypes = argtypes.prepend(nc.encl.type); based = true; } thrown = superConstrType.getThrownTypes(); } } JCTree constrDef = DefaultConstructor(make.at(tree.pos), c, typarams, argtypes, thrown, ctorFlags, based); tree.defs = tree.defs.prepend(constrDef); } // If this is a class, enter symbols for this and super into // current scope. if ((c.flags_field & INTERFACE) == 0) { VarSymbol thisSym = new VarSymbol(FINAL | HASINIT, names._this, c.type, c); thisSym.pos = Position.FIRSTPOS; env.info.scope.enter(thisSym); if (ct.supertype_field.tag == CLASS) { VarSymbol superSym = new VarSymbol(FINAL | HASINIT, names._super, ct.supertype_field, c); superSym.pos = Position.FIRSTPOS; env.info.scope.enter(superSym); } } // check that no package exists with same fully qualified name, // but admit classes in the unnamed package which have the same // name as a top-level package. if (checkClash && c.owner.kind == PCK && c.owner != syms.unnamedPackage && reader.packageExists(c.fullname)) { log.error(tree.pos, "clash.with.pkg.of.same.name", c); } } catch (CompletionFailure ex) { chk.completionError(tree.pos(), ex); } finally { log.useSource(prev); } // Enter all member fields and methods of a set of half completed // classes in a second phase. if (wasFirst) { try { while (halfcompleted.nonEmpty()) { finish(halfcompleted.next()); } } finally { isFirst = true; } // commit pending annotations annotate.flush(); } }
三、過程及簡單源碼解析
Enter過程中,編譯器會找到當前範圍(enclosing scope)中發現的所有的定義(definitions),並且把這些定義注冊成符號(symbols)。Enter又分為以下兩個階段:
第一個階段:

編譯器會注冊所有類的符號,並且把這寫符號和相應的範圍(scope)聯係在一起。實現方法是使用一個Visitor(訪問者)類,由上而下的遍曆AST(抽象語法樹),訪問所有的類,包括類裏麵的內部類。Enter給每一個類的符號都添加了一個MemberEnter對象,這個對象是由第二個階段來調用的。
整個操作的方法調用過程如下:
上麵這個過程是訪問者模式的一種實現。
Enter是一個JCTree.Visitor.Enter.classEnter(l.head, env)調用JCTree.accept(Visitor v),而accept方法又是調用的Visitor類裏麵的visitXXX()方法,而這些方法的實現又是在Enter類中。也就是Enter.visitClassDef(JCClassDecl tree)方法,在這個方法中,會將類符號放入uncompleted列表;
visitClassDef(JCClassDecl tree)方法主要做三件事:
1、將類符號(當前類)填入類自身的符號表,添加了一個MemberEnter對象
// Enter class into `compiled' table and enclosing scope. if (chk.compiled.get(c.flatname) != null) { duplicateClass(tree.pos(), c); result = new ErrorType(tree.name, (TypeSymbol)owner); tree.sym = (ClassSymbol)result.tsym; return; } chk.compiled.put(c.flatname, c); enclScope.enter(c); // Set up an environment for class block and store in `typeEnvs' // table, to be retrieved later in memberEnter and attribution. Env<AttrContext> localEnv = classEnv(tree, env); typeEnvs.put(c, localEnv); // Fill out class fields. c.completer = memberEnter; c.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, c, tree); c.sourcefile = env.toplevel.sourcefile; c.members_field = new Scope(c);
2、解析填寫其它的類符號,包括當前類中使用到的內部類、枚舉、變量等抽象樹的類符號。
// Enter type parameters. ct.typarams_field = classEnter(tree.typarams, localEnv);
3、將類符號放入uncompleted列表
if (!c.isLocal() && uncompleted != null) uncompleted.append(c);
Enter給每一個類的符號都添加了一個MemberEnter對象,這個對象是由第二個階段來調用的。
memberEnter.memberEnter(tree, env);
第二個階段:
這些類被MemberEnter對象所完成(completed,即完成類的成員變量的Enter)。首先,MemberEnter決定一個類的參數,父類和接口。然後這些符號被添加進了類的範圍中。不像前一個步驟,這個步驟是懶惰執行的。類的成員隻有在被訪問時,才加入類的定義中的。這裏的實現,是通過安裝一個完成對象(member object)到類的符號中。這些對象可以在需要時調用memberEnter。
整個操作的方法調用過程如下:
Enter是一個JCTree.Visitor.Enter.classEnter(l.head, env)調用JCTree.accept(Visitor v),而accept方法又是調用的Visitor類裏麵的visitXXX()方法,而這些方法的實現又是在Enter類中。也就是Enter.visitClassDef(JCClassDecl tree)方法,在這個方法中,會將類符號解析和填入類自身的符號表。
最後,enter把所有的頂層類(top-level classes)放到一個todo-queue中。
最後更新:2017-04-03 05:39:09