duke@1: /* xdono@117: * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javac.comp; duke@1: duke@1: import java.util.*; duke@1: import javax.tools.JavaFileObject; duke@1: import javax.tools.JavaFileManager; duke@1: duke@1: import com.sun.tools.javac.code.*; duke@1: import com.sun.tools.javac.jvm.*; duke@1: import com.sun.tools.javac.tree.*; duke@1: import com.sun.tools.javac.util.*; duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; duke@1: import com.sun.tools.javac.util.List; duke@1: duke@1: import com.sun.tools.javac.code.Type.*; duke@1: import com.sun.tools.javac.code.Symbol.*; duke@1: import com.sun.tools.javac.tree.JCTree.*; duke@1: duke@1: import static com.sun.tools.javac.code.Flags.*; duke@1: import static com.sun.tools.javac.code.Kinds.*; duke@1: duke@1: /** This class enters symbols for all encountered definitions into duke@1: * the symbol table. The pass consists of two phases, organized as duke@1: * follows: duke@1: * duke@1: *

In the first phase, all class symbols are intered into their duke@1: * enclosing scope, descending recursively down the tree for classes duke@1: * which are members of other classes. The class symbols are given a duke@1: * MemberEnter object as completer. duke@1: * duke@1: *

In the second phase classes are completed using duke@1: * MemberEnter.complete(). Completion might occur on demand, but duke@1: * any classes that are not completed that way will be eventually duke@1: * completed by processing the `uncompleted' queue. Completion duke@1: * entails (1) determination of a class's parameters, supertype and duke@1: * interfaces, as well as (2) entering all symbols defined in the duke@1: * class into its scope, with the exception of class symbols which duke@1: * have been entered in phase 1. (2) depends on (1) having been duke@1: * completed for a class and all its superclasses and enclosing duke@1: * classes. That's why, after doing (1), we put classes in a duke@1: * `halfcompleted' queue. Only when we have performed (1) for a class duke@1: * and all it's superclasses and enclosing classes, we proceed to duke@1: * (2). duke@1: * duke@1: *

Whereas the first phase is organized as a sweep through all duke@1: * compiled syntax trees, the second phase is demand. Members of a duke@1: * class are entered when the contents of a class are first duke@1: * accessed. This is accomplished by installing completer objects in duke@1: * class symbols for compiled classes which invoke the member-enter duke@1: * phase for the corresponding class tree. duke@1: * duke@1: *

Classes migrate from one phase to the next via queues: duke@1: * duke@1: *

duke@1:  *  class enter -> (Enter.uncompleted)         --> member enter (1)
duke@1:  *              -> (MemberEnter.halfcompleted) --> member enter (2)
duke@1:  *              -> (Todo)                      --> attribute
duke@1:  *                                              (only for toplevel classes)
duke@1:  *  
duke@1: * duke@1: *

This is NOT part of any API supported by Sun Microsystems. If duke@1: * you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ duke@1: public class Enter extends JCTree.Visitor { duke@1: protected static final Context.Key enterKey = duke@1: new Context.Key(); duke@1: duke@1: Log log; duke@1: Symtab syms; duke@1: Check chk; duke@1: TreeMaker make; duke@1: ClassReader reader; duke@1: Annotate annotate; duke@1: MemberEnter memberEnter; jjg@110: Types types; duke@1: Lint lint; jjg@483: Names names; duke@1: JavaFileManager fileManager; duke@1: duke@1: private final Todo todo; duke@1: duke@1: public static Enter instance(Context context) { duke@1: Enter instance = context.get(enterKey); duke@1: if (instance == null) duke@1: instance = new Enter(context); duke@1: return instance; duke@1: } duke@1: duke@1: protected Enter(Context context) { duke@1: context.put(enterKey, this); duke@1: duke@1: log = Log.instance(context); duke@1: reader = ClassReader.instance(context); duke@1: make = TreeMaker.instance(context); duke@1: syms = Symtab.instance(context); duke@1: chk = Check.instance(context); duke@1: memberEnter = MemberEnter.instance(context); jjg@110: types = Types.instance(context); duke@1: annotate = Annotate.instance(context); duke@1: lint = Lint.instance(context); jjg@483: names = Names.instance(context); duke@1: duke@1: predefClassDef = make.ClassDef( duke@1: make.Modifiers(PUBLIC), duke@1: syms.predefClass.name, null, null, null, null); duke@1: predefClassDef.sym = syms.predefClass; duke@1: todo = Todo.instance(context); duke@1: fileManager = context.get(JavaFileManager.class); duke@1: } duke@1: duke@1: /** A hashtable mapping classes and packages to the environments current duke@1: * at the points of their definitions. duke@1: */ duke@1: Map> typeEnvs = duke@1: new HashMap>(); duke@1: duke@1: /** Accessor for typeEnvs duke@1: */ duke@1: public Env getEnv(TypeSymbol sym) { duke@1: return typeEnvs.get(sym); duke@1: } duke@1: duke@1: public Env getClassEnv(TypeSymbol sym) { duke@1: Env localEnv = getEnv(sym); duke@1: Env lintEnv = localEnv; duke@1: while (lintEnv.info.lint == null) duke@1: lintEnv = lintEnv.next; duke@1: localEnv.info.lint = lintEnv.info.lint.augment(sym.attributes_field, sym.flags()); duke@1: return localEnv; duke@1: } duke@1: duke@1: /** The queue of all classes that might still need to be completed; duke@1: * saved and initialized by main(). duke@1: */ duke@1: ListBuffer uncompleted; duke@1: duke@1: /** A dummy class to serve as enclClass for toplevel environments. duke@1: */ duke@1: private JCClassDecl predefClassDef; duke@1: duke@1: /* ************************************************************************ duke@1: * environment construction duke@1: *************************************************************************/ duke@1: duke@1: duke@1: /** Create a fresh environment for class bodies. duke@1: * This will create a fresh scope for local symbols of a class, referred duke@1: * to by the environments info.scope field. duke@1: * This scope will contain duke@1: * - symbols for this and super duke@1: * - symbols for any type parameters duke@1: * In addition, it serves as an anchor for scopes of methods and initializers duke@1: * which are nested in this scope via Scope.dup(). duke@1: * This scope should not be confused with the members scope of a class. duke@1: * duke@1: * @param tree The class definition. duke@1: * @param env The environment current outside of the class definition. duke@1: */ duke@1: public Env classEnv(JCClassDecl tree, Env env) { duke@1: Env localEnv = duke@1: env.dup(tree, env.info.dup(new Scope(tree.sym))); duke@1: localEnv.enclClass = tree; duke@1: localEnv.outer = env; duke@1: localEnv.info.isSelfCall = false; duke@1: localEnv.info.lint = null; // leave this to be filled in by Attr, duke@1: // when annotations have been processed duke@1: return localEnv; duke@1: } duke@1: duke@1: /** Create a fresh environment for toplevels. duke@1: * @param tree The toplevel tree. duke@1: */ duke@1: Env topLevelEnv(JCCompilationUnit tree) { duke@1: Env localEnv = new Env(tree, new AttrContext()); duke@1: localEnv.toplevel = tree; duke@1: localEnv.enclClass = predefClassDef; duke@1: tree.namedImportScope = new Scope.ImportScope(tree.packge); duke@1: tree.starImportScope = new Scope.ImportScope(tree.packge); duke@1: localEnv.info.scope = tree.namedImportScope; duke@1: localEnv.info.lint = lint; duke@1: return localEnv; duke@1: } duke@1: duke@1: public Env getTopLevelEnv(JCCompilationUnit tree) { duke@1: Env localEnv = new Env(tree, new AttrContext()); duke@1: localEnv.toplevel = tree; duke@1: localEnv.enclClass = predefClassDef; duke@1: localEnv.info.scope = tree.namedImportScope; duke@1: localEnv.info.lint = lint; duke@1: return localEnv; duke@1: } duke@1: duke@1: /** The scope in which a member definition in environment env is to be entered duke@1: * This is usually the environment's scope, except for class environments, duke@1: * where the local scope is for type variables, and the this and super symbol duke@1: * only, and members go into the class member scope. duke@1: */ duke@1: Scope enterScope(Env env) { duke@1: return (env.tree.getTag() == JCTree.CLASSDEF) duke@1: ? ((JCClassDecl) env.tree).sym.members_field duke@1: : env.info.scope; duke@1: } duke@1: duke@1: /* ************************************************************************ duke@1: * Visitor methods for phase 1: class enter duke@1: *************************************************************************/ duke@1: duke@1: /** Visitor argument: the current environment. duke@1: */ duke@1: protected Env env; duke@1: duke@1: /** Visitor result: the computed type. duke@1: */ duke@1: Type result; duke@1: duke@1: /** Visitor method: enter all classes in given tree, catching any duke@1: * completion failure exceptions. Return the tree's type. duke@1: * duke@1: * @param tree The tree to be visited. duke@1: * @param env The environment visitor argument. duke@1: */ duke@1: Type classEnter(JCTree tree, Env env) { duke@1: Env prevEnv = this.env; duke@1: try { duke@1: this.env = env; duke@1: tree.accept(this); duke@1: return result; duke@1: } catch (CompletionFailure ex) { duke@1: return chk.completionError(tree.pos(), ex); duke@1: } finally { duke@1: this.env = prevEnv; duke@1: } duke@1: } duke@1: duke@1: /** Visitor method: enter classes of a list of trees, returning a list of types. duke@1: */ duke@1: List classEnter(List trees, Env env) { duke@1: ListBuffer ts = new ListBuffer(); jjg@70: for (List l = trees; l.nonEmpty(); l = l.tail) { jjg@70: Type t = classEnter(l.head, env); jjg@70: if (t != null) jjg@70: ts.append(t); jjg@70: } duke@1: return ts.toList(); duke@1: } duke@1: duke@1: public void visitTopLevel(JCCompilationUnit tree) { duke@1: JavaFileObject prev = log.useSource(tree.sourcefile); duke@1: boolean addEnv = false; duke@1: boolean isPkgInfo = tree.sourcefile.isNameCompatible("package-info", duke@1: JavaFileObject.Kind.SOURCE); duke@1: if (tree.pid != null) { duke@1: tree.packge = reader.enterPackage(TreeInfo.fullName(tree.pid)); duke@1: if (tree.packageAnnotations.nonEmpty()) { duke@1: if (isPkgInfo) { duke@1: addEnv = true; duke@1: } else { duke@1: log.error(tree.packageAnnotations.head.pos(), duke@1: "pkg.annotations.sb.in.package-info.java"); duke@1: } duke@1: } duke@1: } else { duke@1: tree.packge = syms.unnamedPackage; duke@1: } duke@1: tree.packge.complete(); // Find all classes in package. duke@1: Env env = topLevelEnv(tree); duke@1: duke@1: // Save environment of package-info.java file. duke@1: if (isPkgInfo) { duke@1: Env env0 = typeEnvs.get(tree.packge); duke@1: if (env0 == null) { duke@1: typeEnvs.put(tree.packge, env); duke@1: } else { duke@1: JCCompilationUnit tree0 = env0.toplevel; duke@1: if (!fileManager.isSameFile(tree.sourcefile, tree0.sourcefile)) { duke@1: log.warning(tree.pid != null ? tree.pid.pos() duke@1: : null, duke@1: "pkg-info.already.seen", duke@1: tree.packge); duke@1: if (addEnv || (tree0.packageAnnotations.isEmpty() && duke@1: tree.docComments != null && duke@1: tree.docComments.get(tree) != null)) { duke@1: typeEnvs.put(tree.packge, env); duke@1: } duke@1: } duke@1: } jjg@483: jjg@483: for (Symbol q = tree.packge; q != null && q.kind == PCK; q = q.owner) jjg@483: q.flags_field |= EXISTS; jjg@483: jjg@483: Name name = names.package_info; jjg@483: ClassSymbol c = reader.enterClass(name, tree.packge); jjg@483: c.flatname = names.fromString(tree.packge + "." + name); jjg@483: c.sourcefile = tree.sourcefile; jjg@483: c.completer = null; jjg@483: c.members_field = new Scope(c); jjg@483: tree.packge.package_info = c; duke@1: } duke@1: classEnter(tree.defs, env); duke@1: if (addEnv) { duke@1: todo.append(env); duke@1: } duke@1: log.useSource(prev); duke@1: result = null; duke@1: } duke@1: duke@1: public void visitClassDef(JCClassDecl tree) { duke@1: Symbol owner = env.info.scope.owner; duke@1: Scope enclScope = enterScope(env); duke@1: ClassSymbol c; duke@1: if (owner.kind == PCK) { duke@1: // We are seeing a toplevel class. duke@1: PackageSymbol packge = (PackageSymbol)owner; duke@1: for (Symbol q = packge; q != null && q.kind == PCK; q = q.owner) duke@1: q.flags_field |= EXISTS; duke@1: c = reader.enterClass(tree.name, packge); duke@1: packge.members().enterIfAbsent(c); duke@1: if ((tree.mods.flags & PUBLIC) != 0 && !classNameMatchesFileName(c, env)) { duke@1: log.error(tree.pos(), duke@1: "class.public.should.be.in.file", tree.name); duke@1: } duke@1: } else { jjg@113: if (!tree.name.isEmpty() && duke@1: !chk.checkUniqueClassName(tree.pos(), tree.name, enclScope)) { duke@1: result = null; duke@1: return; duke@1: } duke@1: if (owner.kind == TYP) { duke@1: // We are seeing a member class. duke@1: c = reader.enterClass(tree.name, (TypeSymbol)owner); duke@1: if ((owner.flags_field & INTERFACE) != 0) { duke@1: tree.mods.flags |= PUBLIC | STATIC; duke@1: } duke@1: } else { duke@1: // We are seeing a local class. duke@1: c = reader.defineClass(tree.name, owner); duke@1: c.flatname = chk.localClassName(c); jjg@113: if (!c.name.isEmpty()) duke@1: chk.checkTransparentClass(tree.pos(), c, env.info.scope); duke@1: } duke@1: } duke@1: tree.sym = c; duke@1: duke@1: // Enter class into `compiled' table and enclosing scope. duke@1: if (chk.compiled.get(c.flatname) != null) { duke@1: duplicateClass(tree.pos(), c); jjg@110: result = types.createErrorType(tree.name, (TypeSymbol)owner, Type.noType); duke@1: tree.sym = (ClassSymbol)result.tsym; duke@1: return; duke@1: } duke@1: chk.compiled.put(c.flatname, c); duke@1: enclScope.enter(c); duke@1: duke@1: // Set up an environment for class block and store in `typeEnvs' duke@1: // table, to be retrieved later in memberEnter and attribution. duke@1: Env localEnv = classEnv(tree, env); duke@1: typeEnvs.put(c, localEnv); duke@1: duke@1: // Fill out class fields. duke@1: c.completer = memberEnter; duke@1: c.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, c, tree); duke@1: c.sourcefile = env.toplevel.sourcefile; duke@1: c.members_field = new Scope(c); duke@1: duke@1: ClassType ct = (ClassType)c.type; duke@1: if (owner.kind != PCK && (c.flags_field & STATIC) == 0) { duke@1: // We are seeing a local or inner class. duke@1: // Set outer_field of this class to closest enclosing class duke@1: // which contains this class in a non-static context duke@1: // (its "enclosing instance class"), provided such a class exists. duke@1: Symbol owner1 = owner; duke@1: while ((owner1.kind & (VAR | MTH)) != 0 && duke@1: (owner1.flags_field & STATIC) == 0) { duke@1: owner1 = owner1.owner; duke@1: } duke@1: if (owner1.kind == TYP) { duke@1: ct.setEnclosingType(owner1.type); duke@1: } duke@1: } duke@1: duke@1: // Enter type parameters. duke@1: ct.typarams_field = classEnter(tree.typarams, localEnv); duke@1: duke@1: // Add non-local class to uncompleted, to make sure it will be duke@1: // completed later. duke@1: if (!c.isLocal() && uncompleted != null) uncompleted.append(c); duke@1: // System.err.println("entering " + c.fullname + " in " + c.owner);//DEBUG duke@1: duke@1: // Recursively enter all member classes. duke@1: classEnter(tree.defs, localEnv); duke@1: duke@1: result = c.type; duke@1: } duke@1: //where duke@1: /** Does class have the same name as the file it appears in? duke@1: */ duke@1: private static boolean classNameMatchesFileName(ClassSymbol c, duke@1: Env env) { duke@1: return env.toplevel.sourcefile.isNameCompatible(c.name.toString(), duke@1: JavaFileObject.Kind.SOURCE); duke@1: } duke@1: duke@1: /** Complain about a duplicate class. */ duke@1: protected void duplicateClass(DiagnosticPosition pos, ClassSymbol c) { duke@1: log.error(pos, "duplicate.class", c.fullname); duke@1: } duke@1: duke@1: /** Class enter visitor method for type parameters. duke@1: * Enter a symbol for type parameter in local scope, after checking that it duke@1: * is unique. duke@1: */ duke@1: public void visitTypeParameter(JCTypeParameter tree) { duke@1: TypeVar a = (tree.type != null) duke@1: ? (TypeVar)tree.type duke@1: : new TypeVar(tree.name, env.info.scope.owner, syms.botType); duke@1: tree.type = a; duke@1: if (chk.checkUnique(tree.pos(), a.tsym, env.info.scope)) { duke@1: env.info.scope.enter(a.tsym); duke@1: } duke@1: result = a; duke@1: } duke@1: duke@1: /** Default class enter visitor method: do nothing. duke@1: */ duke@1: public void visitTree(JCTree tree) { duke@1: result = null; duke@1: } duke@1: duke@1: /** Main method: enter all classes in a list of toplevel trees. duke@1: * @param trees The list of trees to be processed. duke@1: */ duke@1: public void main(List trees) { duke@1: complete(trees, null); duke@1: } duke@1: duke@1: /** Main method: enter one class from a list of toplevel trees and duke@1: * place the rest on uncompleted for later processing. duke@1: * @param trees The list of trees to be processed. duke@1: * @param c The class symbol to be processed. duke@1: */ duke@1: public void complete(List trees, ClassSymbol c) { duke@1: annotate.enterStart(); duke@1: ListBuffer prevUncompleted = uncompleted; duke@1: if (memberEnter.completionEnabled) uncompleted = new ListBuffer(); duke@1: duke@1: try { duke@1: // enter all classes, and construct uncompleted list duke@1: classEnter(trees, null); duke@1: duke@1: // complete all uncompleted classes in memberEnter duke@1: if (memberEnter.completionEnabled) { duke@1: while (uncompleted.nonEmpty()) { duke@1: ClassSymbol clazz = uncompleted.next(); duke@1: if (c == null || c == clazz || prevUncompleted == null) duke@1: clazz.complete(); duke@1: else duke@1: // defer duke@1: prevUncompleted.append(clazz); duke@1: } duke@1: duke@1: // if there remain any unimported toplevels (these must have duke@1: // no classes at all), process their import statements as well. duke@1: for (JCCompilationUnit tree : trees) { duke@1: if (tree.starImportScope.elems == null) { duke@1: JavaFileObject prev = log.useSource(tree.sourcefile); duke@1: Env env = typeEnvs.get(tree); duke@1: if (env == null) duke@1: env = topLevelEnv(tree); duke@1: memberEnter.memberEnter(tree, env); duke@1: log.useSource(prev); duke@1: } duke@1: } duke@1: } duke@1: } finally { duke@1: uncompleted = prevUncompleted; duke@1: annotate.enterDone(); duke@1: } duke@1: } duke@1: }