/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** ** ** $Id$ \* */ //todo check significance of JAVA flag. package scalac.symtab; import scalac.ApplicationError; import scalac.Global; import scalac.PhaseDescriptor; import scalac.util.ArrayApply; import scalac.util.Name; import scalac.util.Names; import scalac.util.NameTransformer; import scalac.util.Position; import scalac.util.Debug; import scalac.symtab.classfile.*; public abstract class Symbol implements Modifiers, Kinds { /** An empty symbol array */ public static final Symbol[] EMPTY_ARRAY = new Symbol[0]; /** An empty array of symbol arrays */ public static final Symbol[][] EMPTY_ARRAY_ARRAY = new Symbol[0][]; /** The error symbol */ public static final ErrorSymbol ERROR = new ErrorSymbol(); /** The absent symbol */ public static final NoSymbol NONE = new NoSymbol(); // Fields ------------------------------------------------------------- /** The kind of the symbol */ public int kind; /** The position of the symbol */ public int pos; /** The name of the symbol */ public Name name; /** The modifiers of the symbol */ public int flags; /** The owner of the symbol */ private Symbol owner; /** The infos of the symbol */ private TypeIntervalList infos = TypeIntervalList.EMPTY; // Constructors ----------------------------------------------------------- /** Generic symbol constructor */ public Symbol(int kind, int pos, Name name, Symbol owner, int flags) { assert (!isTerm() || !name.isTypeName()) && (!isType() || name.isTypeName()); this.kind = kind; this.pos = pos; this.name = name; this.owner = owner; this.flags = flags & ~(INITIALIZED | LOCKED); // safety first } /** Return a fresh symbol with the same fields as this one. */ public abstract Symbol cloneSymbol(); /** copy all fields to `sym' */ public void copyTo(Symbol sym) { sym.kind = kind; sym.pos = pos; sym.name = name; sym.flags = flags; sym.owner = owner; sym.infos = infos; } // Setters --------------------------------------------------------------- /** Set the mangled name of this Symbol */ public Symbol setMangledName(Name name) { throw new ApplicationError("illegal operation on " + getClass()); } /** Set owner */ public Symbol setOwner(Symbol owner) { this.owner = owner; return this; } /** Set information, except if symbol is both initialized and locked. */ public Symbol setInfo(Type info) { return setInfo(info, currentPhaseId()); } public Symbol setInfo(Type info, int limit) { if ((flags & (INITIALIZED | LOCKED)) != (INITIALIZED | LOCKED)) { if (infos == TypeIntervalList.EMPTY) infos = new TypeIntervalList(TypeIntervalList.EMPTY); infos.limit = limit; infos.info = info; } return this; } /** Set type -- this is an alias for setInfo(Type info) */ public Symbol setType(Type info) { return setInfo(info); } public Symbol updateInfo(Type info) { // Global.instance.currentPhase.setInfo(this, info); if (infos.limit <= Global.instance.currentPhase.id) { infos = new TypeIntervalList(infos); infos.limit = Global.instance.currentPhase.id + 1; } else { assert infos.limit == Global.instance.currentPhase.id + 1 : this; } infos.info = info; return this; } // Symbol classification ---------------------------------------------------- /** Does this symbol denote a type? */ public final boolean isType() { return kind == TYPE || kind == CLASS || kind == ALIAS; } /** Does this symbol denote a term? */ public final boolean isTerm() { return kind == VAL; } /** Does this symbol denote a value? */ public final boolean isValue() { return kind == VAL && !(isModule() && isJava()) && !isPackage(); } /** Does this symbol denote a variable? */ public final boolean isVariable() { return kind == VAL && (flags & MUTABLE) != 0; } /** Does this symbol denote a method? */ public final boolean isInitializedMethod() { if (infos.limit < 0) return false; switch (rawInfo()) { case MethodType(_, _): case PolyType(_, _): return true; default: return false; } } public final boolean isMethod() { initialize(); return isInitializedMethod(); } /* Does this symbol denote an anonymous class? */ public final boolean isAnonymousClass() { return kind == CLASS && (name == Names.EMPTY.toTypeName() || name == Names.ANON_CLASS_NAME.toTypeName()); } /** Does this symbol denote the root class or root module? */ public final boolean isRoot() { return this.moduleClass() == Global.instance.definitions.ROOT_CLASS; } /** Does this symbol denote something loaded from a Java class? */ public final boolean isJava() { return (flags & JAVA) != 0; } /** Does this symbol denote a Java package? */ public final boolean isPackage() { return (flags & PACKAGE) != 0; } /** Does this symbol denote a module? */ public final boolean isModule() { return kind == VAL && (flags & MODUL) != 0; } /** Does this symbol denote a module? */ public final boolean isModuleClass() { return kind == CLASS && (flags & MODUL) != 0; } /** Does this symbol denote a module? */ public final boolean isClass() { return kind == CLASS; } /** Does this symbol denote a case class? */ public final boolean isCaseClass() { return kind == CLASS && (flags & CASE) != 0; } /** Does this symbol denote a uniform (i.e. parameterless) class? */ public final boolean isTrait() { return kind == CLASS && (flags & TRAIT) != 0; } /** Does this class symbol denote a compound type symbol? */ public final boolean isCompoundSym() { return name == Names.COMPOUND_NAME.toTypeName(); } /** Does this symbol denote an interface? */ public final boolean isInterface() { return (flags & INTERFACE) != 0; } /** Does this symbol denote a static member? */ public final boolean isStatic() { return (flags & STATIC) != 0; } /** Is this symbol locally defined? I.e. not a member of a class or module */ public final boolean isLocal() { return owner.kind == VAL && !owner.isPrimaryConstructor(); } /** Is this symbol a parameter? Includes type parameters of methods. */ public final boolean isParameter() { return (flags & PARAM) != 0; } /** Is this symbol a def parameter? */ public final boolean isDefParameter() { return (flags & (PARAM | DEF)) == (PARAM | DEF); } /** Is this class locally defined? * A class is local, if * - it is anonymous, or * - its owner is a value * - it is defined within a local class */ public final boolean isLocalClass() { return kind == CLASS && !isPackage() && (name == Names.EMPTY.toTypeName() || owner.isValue() || owner.isLocalClass()); } /** Is this symbol a constructor? */ public final boolean isConstructor() { return name.isConstrName(); } /** Is this symbol the primary constructor of a type? */ public final boolean isPrimaryConstructor() { return isConstructor() && this == primaryConstructorClass().constructor(); } public boolean isGenerated() { return name.pos((byte)'$') < name.length(); } /** Symbol was preloaded from package */ public boolean isPreloaded() { return owner.isPackage() && pos == Position.NOPOS; } // Symbol names ---------------------------------------------------------------- /** Get the fully qualified name of this Symbol * (this is always a normal name, never a type name) */ public Name fullName() { return name.toTermName(); } /** Get the mangled name of this Symbol * (this is always a normal name, never a type name) */ public Name mangledName() { return name.toTermName(); } /** Get the fully qualified mangled name of this Symbol */ public Name mangledFullName() { return fullName().replace((byte)'.', (byte)'$'); } // Acess to related symbols ----------------------------------------------------- /** Get type parameters */ public Symbol[] typeParams() { return EMPTY_ARRAY; } /** Get primary constructor of class */ public Symbol constructor() { return NONE; } /** Get module associated with class */ public Symbol module() { return NONE; } /** Get owner */ public Symbol owner() { return owner; } /** Get owner, but if owner is primary constructor of a class, * get class symbol instead. This is useful for type parameters * and value parameters in classes which have the primary constructor * as owner. */ public Symbol classOwner() { Symbol owner = owner(); Symbol clazz = owner.primaryConstructorClass(); if (clazz.constructor() == owner) return clazz; else return owner; } /** The next enclosing class */ public Symbol enclClass() { return owner().enclClass(); } /** The top-level class enclosing `sym' */ Symbol enclToplevelClass() { Symbol sym = this; while (sym.kind == VAL || (sym.kind == CLASS && !sym.owner().isPackage())) { sym = sym.owner(); } return sym; } /* If this is a primary constructor, return the class it constructs. * Otherwise return the symbol itself. */ public Symbol primaryConstructorClass() { return this; } /* If this is a module, return its class. * Otherwise return the symbol itself. */ public Symbol moduleClass() { return this; } /** The symbol accessed by this accessor function. */ public Symbol accessed() { assert (flags & ACCESSOR) != 0; Name name1 = name; if (name1.endsWith(Names._EQ)) name1 = name1.subName(0, name1.length() - Names._EQ.length()); return owner.info().lookup(Name.fromString(name1 + "$")); } /** The members of this class or module symbol */ public Scope members() { return info().members(); } // Symbol types -------------------------------------------------------------- /** Was symbol's type updated during phase `id'? */ public boolean isUpdated(int id) { return infos.limit >= id; } /** the current phase id, or the id after analysis, whichever is larger. */ int currentPhaseId() { int id = Global.instance.currentPhase.id; if (id > Global.instance.POST_ANALYZER_PHASE_ID) id = Global.instance.POST_ANALYZER_PHASE_ID; return id; } /** Is this symbol initialized? */ public final boolean isInitialized() { return (flags & INITIALIZED) != 0; } /** Initialize the symbol */ public final Symbol initialize() { info(); return this; } /** Get info; This is: * for a term symbol, its type * for a type variable, its bound * for a type alias, its right-hand side * for a class symbol, the compound type consisting of * its baseclasses and members. */ public Type info() { if ((flags & INITIALIZED) == 0) { int id = currentPhaseId(); Type info = rawInfoAt(id); assert info != null : this; if ((flags & LOCKED) != 0) { setInfo(Type.ErrorType); flags |= INITIALIZED; throw new CyclicReference(this, info); } flags |= LOCKED; //System.out.println("completing " + this);//DEBUG info.complete(this); flags = flags & ~LOCKED; if (info instanceof SourceCompleter && (flags & SNDTIME) == 0) { flags |= SNDTIME; return info(); } else { assert !(rawInfoAt(id) instanceof Type.LazyType) : this; flags |= INITIALIZED; } //System.out.println("done: " + this);//DEBUG } return rawInfoAt(Global.instance.currentPhase.id); } /** Get info at phase #id */ public Type infoAt(int id) { info(); return rawInfoAt(id); } /** get info at phase #id, without forcing lazy types. */ private Type rawInfoAt(int id) { int nextid = infos.limit; assert infos != TypeIntervalList.EMPTY : this; if (nextid < id) { do { Type newInfo = Global.instance.phases[nextid].transformInfo(this, infos.info); if (newInfo != infos.info) { infos = new TypeIntervalList(infos); infos.info = newInfo; } nextid++; infos.limit = nextid; } while (nextid < id); return infos.info; } else { TypeIntervalList infos1 = infos; while (infos1.prev.limit >= id) { infos1 = infos1.prev; } return infos1.info; } } public Type rawInfo() { return rawInfoAt(Global.instance.currentPhase.id); } /** The type of a symbol is: * for a type symbol, the type corresponding to the symbol itself * for a term symbol, its usual type */ public Type type() { return info(); } /** The type at phase #id */ public Type typeAt(int id) { return infoAt(id); } /** The types of these symbols as an array. */ static public Type[] type(Symbol[] syms) { Type[] tps = new Type[syms.length]; for (int i = 0; i < syms.length; i++) tps[i] = syms[i].type(); return tps; } /** Get this.type corresponding to this symbol */ public Type thisType() { return Type.localThisType; } public Type typeOfThis() { return Type.localThisType; } /** A total ordering between symbols that refines the class * inheritance graph (i.e. subclass.isLess(superclass) always holds). */ public boolean isLess(Symbol that) { if (this == that) return false; int diff; if (this.isType()) { if (that.isType()) { diff = this.closure().length - that.closure().length; if (diff > 0) return true; if (diff < 0) return false; } else { return true; } } else if (that.isType()) { return false; } diff = that.mangledName().index - this.mangledName().index; if (diff > 0) return true; if (diff < 0) return false; diff = that.mangledFullName().index - this.mangledFullName().index; if (diff > 0) return true; if (diff < 0) return false; throw new ApplicationError( "Giving up: can't order two incarnations of class " + this.mangledFullName()); } /** Return the symbol's type itself followed by all its direct and indirect * base types, sorted by isLess(). Overridden for class symbols. */ public Type[] closure() { return info().closure(); } /** Return position of `c' in the closure of this type; -1 if not there. */ public int closurePos(Symbol c) { if (this == c) return 0; if (c.isCompoundSym()) return -1; Type[] closure = closure(); int lo = 0; int hi = closure.length - 1; while (lo <= hi) { int mid = (lo + hi) / 2; Symbol clsym = closure[mid].symbol(); if (c == clsym) return mid; else if (c.isLess(clsym)) hi = mid - 1; else if (clsym.isLess(c)) lo = mid + 1; else throw new ApplicationError(); } return -1; } Type baseType(Symbol sym) { int i = closurePos(sym); if (i >= 0) return closure()[i]; else return Type.NoType; } /** Is this class a subclass of `c'? I.e. does it have a type instance * of `c' as indirect base class? */ public boolean isSubClass(Symbol c) { return this == c || c.kind == Kinds.ERROR || closurePos(c) >= 0; } // ToString ------------------------------------------------------------------- /** String representation of symbol's simple name. * Translates expansions of operators back to operator symbol. E.g. * $eq => =. */ public String nameString() { return NameTransformer.decode(name).toString(); } /** String representation of symbol's full name. * Translates expansions of operators back to operator symbol. E.g. * $eq => =. */ public String fullNameString() { return NameTransformer.decode(fullName()).toString(); } public String idString() { if (Global.instance.uniqid && (kind == TYPE || Global.instance.debug)) return "#" + Global.instance.uniqueID.id(this); else return ""; } /** String representation, including symbol's kind * e.g., "class Foo", "function Bar". */ public String toString() { if (isRoot()) return ""; if (isAnonymousClass()) return "