duke@1: /*
duke@1: * Copyright 1999-2006 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;
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);
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: }
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: }