diff options
Diffstat (limited to 'sources/scalac/symtab')
19 files changed, 6146 insertions, 0 deletions
diff --git a/sources/scalac/symtab/Definitions.java b/sources/scalac/symtab/Definitions.java new file mode 100644 index 0000000000..6d67edac42 --- /dev/null +++ b/sources/scalac/symtab/Definitions.java @@ -0,0 +1,421 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +import scalac.*; +import scalac.util.*; +import scalac.symtab.classfile.*; +import Type.*; + +public class Definitions { + + /** the root module + */ + public final Symbol ROOT; + public final Symbol ROOT_CLASS; + public final Type ROOT_TYPE; + + /** the scala module + */ + public final Symbol SCALA; + public final Symbol SCALA_CLASS; + public final Type SCALA_TYPE; + + /** the java module + */ + public final Symbol JAVA; + public final Symbol JAVA_CLASS; + public final Type JAVA_TYPE; + + /** the java.lang module + */ + public final Symbol JAVALANG; + public final Symbol JAVALANG_CLASS; + public final Type JAVALANG_TYPE; + + /** the scala.runtime module + */ + public final Symbol SCALARUNTIME; + public final Symbol SCALARUNTIME_CLASS; + public final Type SCALARUNTIME_TYPE; + + /** the null value + */ + public final Symbol NULL; + + /** the scala.Any class + */ + public final Symbol ANY_CLASS; + public final Type ANY_TYPE; + + /** some methods of the scala.Any class + */ + public final Symbol MATCH; + public final Symbol IS; + public final Symbol AS; + public final Symbol EQEQ; + public final Symbol BANGEQ; + public final Symbol TOSTRING; + public final Symbol HASHCODE; + + /** a method of class java.lang.Throwable + */ + public final Symbol THROW; + + /** the scala.AnyVal class + */ + public final Symbol ANYVAL_CLASS; + public final Type ANYVAL_TYPE; + + /** the scala.AnyRef class + */ + public final Symbol ANYREF_CLASS; + public final Type ANYREF_TYPE; + + /** the java.lang.Object class + */ + public final Symbol JAVA_OBJECT_CLASS; + public final Type JAVA_OBJECT_TYPE; + + /** the scala.Object class + */ + public final Symbol OBJECT_CLASS; + public final Type OBJECT_TYPE; + + /** the primitive types + */ + public final Symbol BYTE_CLASS; + public final Type BYTE_TYPE; + public final Symbol SHORT_CLASS; + public final Type SHORT_TYPE; + public final Symbol CHAR_CLASS; + public final Type CHAR_TYPE; + public final Symbol INT_CLASS; + public final Type INT_TYPE; + public final Symbol LONG_CLASS; + public final Type LONG_TYPE; + public final Symbol FLOAT_CLASS; + public final Type FLOAT_TYPE; + public final Symbol DOUBLE_CLASS; + public final Type DOUBLE_TYPE; + public final Symbol BOOLEAN_CLASS; + public final Type BOOLEAN_TYPE; + public final Symbol UNIT_CLASS; + public final Type UNIT_TYPE; + + /** the array class + */ + public final Symbol ARRAY_CLASS; + + /** types from java.lang + */ + public final Symbol JAVA_STRING_CLASS; + public final Type JAVA_STRING_TYPE; + public final Symbol JAVA_THROWABLE_CLASS; + public final Type JAVA_THROWABLE_TYPE; + + /** types from scala + */ + public final Symbol STRING_CLASS; + public final Type STRING_TYPE; + + /** string concatenation pseudo-methods of classes scala.Any and + * java.lang.String + */ + //public final Symbol ANY_PLUS_STRING; + public final Symbol STRING_PLUS_ANY; + + /** members of class Boolean + */ + private Symbol TRUE; + private Symbol FALSE; + private Symbol BARBAR; + private Symbol AMPAMP; + + public Symbol TRUE () { loadBooleanMembers(); return TRUE ; } + public Symbol FALSE () { loadBooleanMembers(); return FALSE ; } + public Symbol BARBAR() { loadBooleanMembers(); return BARBAR; } + public Symbol AMPAMP() { loadBooleanMembers(); return AMPAMP; } + + public Definitions(Global global) { + // a hack to make definitions accessible earlier to other + // components + global.definitions = this; + PackageParser pparser = new PackageParser(global); + + // this is the root value; all top-level functions, + // modules etc. are a member of this value + ROOT = TermSymbol.newJavaPackageModule( + Names.EMPTY, Symbol.NONE, pparser); + ROOT_CLASS = ROOT.moduleClass(); + // this is a prefix for all types inside of the anonymous package + ROOT_TYPE = ROOT_CLASS.thisType(); + + // the scala module + SCALA = getModule(Names.scala); + // the scala class + SCALA_CLASS = SCALA.moduleClass(); + // the scala package as a prefix + SCALA_TYPE = Type.singleType(ROOT_TYPE, SCALA); + + // the java module + JAVA = getModule(Names.java); + // the java class + JAVA_CLASS = JAVA.moduleClass(); + // the java package as a prefix + JAVA_TYPE = Type.singleType(ROOT_TYPE, JAVA); + + // the java.lang module + JAVALANG = getModule(Names.java_lang); + // the java.lang class + JAVALANG_CLASS = JAVALANG.moduleClass(); + // the java.lang package as a prefix + JAVALANG_TYPE = Type.singleType(JAVA_TYPE, JAVALANG); + + // the scala.runtime module + SCALARUNTIME = getModule(Names.scala_runtime); + // the scala.runtime class + SCALARUNTIME_CLASS = SCALARUNTIME.moduleClass(); + // the scala.runtime package as a prefix + SCALARUNTIME_TYPE = Type.singleType(SCALA_TYPE, SCALARUNTIME); + + // the scala.ANY classs + ANY_CLASS = new ClassSymbol( + Position.NOPOS, Names.Any.toTypeName(), SCALA_CLASS, Modifiers.JAVA); + SCALA_CLASS.members().enter(ANY_CLASS); + ANY_TYPE = monoType(ANY_CLASS); + ANY_CLASS.setInfo(Type.compoundType(Type.EMPTY_ARRAY, new Scope(), ANY_CLASS)); + ANY_CLASS.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, ANY_TYPE)); + + // the java.lang.OBJECT class + JAVA_OBJECT_CLASS = getClass(Names.java_lang_Object); + JAVA_OBJECT_TYPE = monoType(JAVA_OBJECT_CLASS); + JAVA_OBJECT_CLASS.setInfo(pparser.classCompletion); + + // the scala.ANYREF class + ANYREF_CLASS = new TypeSymbol( + Kinds.ALIAS, Position.NOPOS, Names.AnyRef.toTypeName(), + SCALA_CLASS, Modifiers.JAVA) + .setInfo(JAVA_OBJECT_TYPE); + ANYREF_CLASS.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType)); + + ANYREF_TYPE = monoType(ANYREF_CLASS); + SCALA.members().enter(ANYREF_CLASS); + + // the scala.OBJECT class + OBJECT_CLASS = getClass(Names.scala_Object); + OBJECT_TYPE = monoType(OBJECT_CLASS); + + // the scala.ANYVAL class + ANYVAL_CLASS = getClass(Names.scala_AnyVal); + ANYVAL_TYPE = monoType(ANYVAL_CLASS); + + // the primitive types + DOUBLE_CLASS = getClass(Names.scala_Double); + DOUBLE_TYPE = monoType(DOUBLE_CLASS); + FLOAT_CLASS = getClass(Names.scala_Float); + FLOAT_TYPE = monoType(FLOAT_CLASS); + LONG_CLASS = getClass(Names.scala_Long); + LONG_TYPE = monoType(LONG_CLASS); + INT_CLASS = getClass(Names.scala_Int); + INT_TYPE = monoType(INT_CLASS); + CHAR_CLASS = getClass(Names.scala_Char); + CHAR_TYPE = monoType(CHAR_CLASS); + SHORT_CLASS = getClass(Names.scala_Short); + SHORT_TYPE = monoType(SHORT_CLASS); + BYTE_CLASS = getClass(Names.scala_Byte); + BYTE_TYPE = monoType(BYTE_CLASS); + BOOLEAN_CLASS = getClass(Names.scala_Boolean); + BOOLEAN_TYPE = monoType(BOOLEAN_CLASS); + UNIT_CLASS = getClass(Names.scala_Unit); + UNIT_TYPE = monoType(UNIT_CLASS); + + // the array class + ARRAY_CLASS = getClass(Names.scala_Array); + + // add members to java.lang.Throwable + JAVA_THROWABLE_CLASS = getClass(Names.java_lang_Throwable); + JAVA_THROWABLE_TYPE = monoType(JAVA_THROWABLE_CLASS); + THROW = new TermSymbol( + Position.NOPOS, Names.throw_, JAVA_THROWABLE_CLASS, Modifiers.FINAL); + Symbol tvar = newTypeParameter(THROW, ANY_TYPE); + THROW.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type())); + JAVA_THROWABLE_CLASS.members().enter(THROW); + + // add the java.lang.String class to the scala package + JAVA_STRING_CLASS = getClass(Names.java_lang_String); + JAVA_STRING_TYPE = monoType(JAVA_STRING_CLASS); + STRING_CLASS = new TypeSymbol( + Kinds.ALIAS, Position.NOPOS, Names.String.toTypeName(), SCALA_CLASS, 0) + .setInfo(JAVA_STRING_TYPE); + STRING_CLASS.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType)); + STRING_TYPE = monoType(STRING_CLASS); + SCALA.members().enter(STRING_CLASS); + + /* + ANY_PLUS_STRING = new TermSymbol( + Position.NOPOS, Names.PLUS, ANY_CLASS, Modifiers.FINAL); + ANY_PLUS_STRING.setInfo( + Type.MethodType( + new Symbol[]{newParameter(ANY_PLUS_STRING, STRING_TYPE)}, + STRING_TYPE)); + ANY_CLASS.members().enter(ANY_PLUS_STRING); + */ + + STRING_PLUS_ANY = new TermSymbol( + Position.NOPOS, Names.PLUS, STRING_CLASS, Modifiers.FINAL); + STRING_PLUS_ANY.setInfo( + Type.MethodType( + new Symbol[]{newParameter(STRING_PLUS_ANY, ANY_TYPE)}, + STRING_TYPE)); + STRING_CLASS.members().enter(STRING_PLUS_ANY); + + // add members to class scala.Any + MATCH = new TermSymbol( + Position.NOPOS, Names.match, ANY_CLASS, Modifiers.FINAL); + MATCH.setInfo( + Type.MethodType( + new Symbol[]{newParameter(MATCH, OBJECT_TYPE)}, + OBJECT_TYPE)); + ANY_CLASS.members().enter(MATCH); + + AS = new TermSymbol( + Position.NOPOS, Names.as, ANY_CLASS, Modifiers.FINAL); + tvar = newTypeParameter(AS, ANY_TYPE); + AS.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type())); + ANY_CLASS.members().enter(AS); + + IS = new TermSymbol( + Position.NOPOS, Names.is, ANY_CLASS, Modifiers.FINAL); + IS.setInfo(Type.PolyType(new Symbol[]{newTypeParameter(IS, ANY_TYPE)}, + BOOLEAN_TYPE)); + ANY_CLASS.members().enter(IS); + + EQEQ = new TermSymbol( + Position.NOPOS, Names.EQEQ, ANY_CLASS, 0); + EQEQ.setInfo(Type.MethodType(new Symbol[]{newParameter(EQEQ, ANY_TYPE)}, + BOOLEAN_TYPE)); + ANY_CLASS.members().enter(EQEQ); + + BANGEQ = new TermSymbol( + Position.NOPOS, Names.BANGEQ, ANY_CLASS, 0); + BANGEQ.setInfo(Type.MethodType(new Symbol[]{newParameter(BANGEQ, ANY_TYPE)}, + BOOLEAN_TYPE)); + ANY_CLASS.members().enter(BANGEQ); + + TOSTRING = new TermSymbol( + Position.NOPOS, Names.toString, ANY_CLASS, 0); + TOSTRING.setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, STRING_TYPE)); + ANY_CLASS.members().enter(TOSTRING); + + HASHCODE = new TermSymbol( + Position.NOPOS, Names.hashCode, ANY_CLASS, 0); + HASHCODE.setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, INT_TYPE)); + ANY_CLASS.members().enter(HASHCODE); + + // add a null value to the root scope + NULL = new TermSymbol( + Position.NOPOS, Names.null_, ROOT_CLASS, 0); + tvar = newTypeParameter(NULL, ANYREF_TYPE); + NULL.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type())); + ROOT.members().enter(NULL); + } + + private Symbol newParameter(Symbol owner, Type tp) { + return new TermSymbol(Position.NOPOS, Name.fromString("v"), owner, Modifiers.PARAM) + .setInfo(tp); + } + + private Symbol newTypeParameter(Symbol owner, Type bound) { + return new TypeSymbol( + Kinds.TYPE, Position.NOPOS, Name.fromString("T").toTypeName(), owner, Modifiers.PARAM) + .setInfo(bound); + } + + public Symbol getModule(Name fullname) { + Scope scope = ROOT_CLASS.members(); + int i = 0; + int j = fullname.pos((byte)'.', i); + while (j < fullname.length()) { + scope = scope.lookup(fullname.subName(i, j)).members(); + i = j + 1; + j = fullname.pos((byte)'.', i); + } + Symbol sym = scope.lookup(fullname.subName(i, fullname.length())); + if (!sym.isModule()) { + switch (sym.type()) { + case OverloadedType(Symbol[] alts, Type[] alttypes): + for (int k = 0; k < alts.length; k++) + if ((sym = alts[k]).isModule()) break; + } + } + assert sym.isModule() : "no module '" + fullname + "'"; + return sym; + } + + public Symbol getClass(Name fullname) { + Scope scope = ROOT_CLASS.members(); + int i = 0; + int j = fullname.pos((byte)'.', i); + while (j < fullname.length()) { + scope = scope.lookup(fullname.subName(i, j)).members(); + i = j + 1; + j = fullname.pos((byte)'.', i); + } + Symbol sym = scope.lookup(fullname.subName(i, fullname.length()).toTypeName()); + assert sym.kind != Kinds.NONE : "no class '" + fullname + "'"; + return sym; + } + + public Type getType(Name fullname) { + return getClass(fullname).type(); + } + + public Type monoType(Symbol c) { + return Type.TypeRef(c.owner().thisType(), c, Type.EMPTY_ARRAY); + } + + public Type getJavaType(Name fullname) { + return monoType(getClass(fullname)); + } + + private void loadBooleanMembers() { + if (TRUE != null) return; + Symbol booleanStatics = getModule(Names.scala_Boolean); + TRUE = booleanStatics.members().lookup(Names.True); + FALSE = booleanStatics.members().lookup(Names.False); + BARBAR = BOOLEAN_TYPE.lookup(Names.BARBAR); + AMPAMP = BOOLEAN_TYPE.lookup(Names.AMPAMP); + } + + public Type arrayType(Type elemtpe) { + return Type.appliedType(monoType(ARRAY_CLASS), new Type[]{elemtpe}); + } + + public Type functionType(Type[] argtps, Type restp) { + Type[] argtps1 = new Type[argtps.length + 1]; + System.arraycopy(argtps, 0, argtps1, 0, argtps.length); + argtps1[argtps.length] = Type.covarType(restp); + return Type.appliedType( + getType(Name.fromString("scala.Function" + argtps.length)), + argtps1); + } + + public Type tupleType(Type[] args) { + assert args.length > 0; + Type[] args1 = new Type[args.length]; + for (int i = 0; i < args.length; i++) + args1[i] = Type.covarType(args[i]); + return Type.appliedType( + getType(Name.fromString("scala.Tuple" + args.length)), args1); + } +} diff --git a/sources/scalac/symtab/Kinds.java b/sources/scalac/symtab/Kinds.java new file mode 100644 index 0000000000..8902ff4699 --- /dev/null +++ b/sources/scalac/symtab/Kinds.java @@ -0,0 +1,28 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + + +public interface Kinds { + + /** kind of error symbol + */ + int ERROR = 0; + + /** kind of non-existent symbol + */ + int NONE = 1; + + /** definition kinds + */ + int ALIAS = 2; + int CLASS = 3; + int TYPE = 4; + int VAL = 5; +} diff --git a/sources/scalac/symtab/Modifiers.java b/sources/scalac/symtab/Modifiers.java new file mode 100644 index 0000000000..3fc119419e --- /dev/null +++ b/sources/scalac/symtab/Modifiers.java @@ -0,0 +1,135 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +public interface Modifiers { + + // modifiers + int ABSTRACT = 0x00000001; + int FINAL = 0x00000002; + int PRIVATE = 0x00000004; + int PROTECTED = 0x00000008; + + int QUALIFIED = 0x00000010; + int OVERRIDE = 0x00000020; + int CASE = 0x00000040; + int ABSTRACTCLASS = 0x00000080; // abstract class + + int DEF = 0x00000100; // a def parameter + int SYNTHETIC = 0x00000200; + int DEPRECATED = 0x00000400; + int JAVA = 0x00000800; // symbol was defined by a Java class + + int MODUL = 0x00001000; // symbol is module or class implementing a module + int MUTABLE = 0x00002000; // symbol is a mutable variable. + int PARAM = 0x00004000; // symbol is a (type) parameter to a method + + int INITIALIZED = 0x00010000; // symbol's definition is complete + int LOCKED = 0x00020000; // temporary flag to catch cyclic dependencies + int ACCESSED = 0x00040000; // symbol was accessed at least once + int SELECTOR = 0x00080000; // symbol was used as selector in Select + + int PACKAGE = 0x00100000; // symbol is a java packages. + int LABEL = 0x00200000; // symbol is a label symbol + int STATIC = 0x00400000; // "static" inner classes (i.e. after class norm.) + int STABLE = 0x00800000; // functions that are assumed to be stable + // (typically, access methods for valdefs) + + int CAPTURED = 0x01000000; // variables is accessed from nested function. + + int ACCESSOR = 0x04000000; // function is an access function for a + // value or variable + int BRIDGE = 0x0800000; // function is a bridge method. + + int INTERFACE = 0x10000000; // symbol is a Java interface + int TRAIT = 0x20000000; // symbol is a Trait + + int SNDTIME = 0x40000000; //debug + + // masks + int SOURCEFLAGS = 0x00000077 | PARAM | TRAIT; // these modifiers can be set in source programs. + int ACCESSFLAGS = PRIVATE | PROTECTED; + + public static class Helper { + + public static boolean isAbstract(int flags) { + return (flags & (ABSTRACT | ABSTRACTCLASS)) != 0; + } + + public static boolean isFinal(int flags) { + return (flags & FINAL) != 0; + } + + public static boolean isPrivate(int flags) { + return (flags & PRIVATE) != 0; + } + + public static boolean isProtected(int flags) { + return (flags & PROTECTED) != 0; + } + + public static boolean isQualified(int flags) { + return (flags & QUALIFIED) != 0; + } + + public static boolean isOverride(int flags) { + return (flags & OVERRIDE) != 0; + } + + public static boolean isCase(int flags) { + return (flags & CASE) != 0; + } + + public static boolean isInterface(int flags) { + return (flags & INTERFACE) != 0; + } + + public static boolean isDef(int flags) { + return (flags & DEF) != 0; + } + + public static boolean isModClass(int flags) { + return (flags & MODUL) != 0; + } + + public static boolean isStatic(int flags) { + return (flags & STATIC) != 0; + } + + public static boolean isJava(int flags) { + return (flags & JAVA) != 0; + } + + public static boolean isNoVal(int flags) { + return (flags & PACKAGE) != 0; + } + + public static String toString(int flags) { + StringBuffer buffer = new StringBuffer(); + toString(buffer, flags); + return buffer.toString(); + } + + public static void toString(StringBuffer buffer, int flags) { + //buffer.append(flags).append(": ");//debug + int marker = buffer.length(); + if (isPrivate(flags)) buffer.append("private "); + if (isProtected(flags)) buffer.append("protected "); + if (isAbstract(flags)) buffer.append("abstract "); + if (isFinal(flags)) buffer.append("final "); + if (isQualified(flags)) buffer.append("qualified "); + if (isInterface(flags)) buffer.append("interface "); + if (isCase(flags)) buffer.append("case "); + if (isDef(flags)) buffer.append("def "); + if (isOverride(flags)) buffer.append("override "); + int length = buffer.length(); + buffer.setLength(length - (length == marker ? 0 : 1)); + } + } +} diff --git a/sources/scalac/symtab/NameMangler.java b/sources/scalac/symtab/NameMangler.java new file mode 100644 index 0000000000..30de8193d0 --- /dev/null +++ b/sources/scalac/symtab/NameMangler.java @@ -0,0 +1,33 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +package scalac.symtab; + +import scalac.util.Name; +import java.util.HashMap; + +public class NameMangler { + + private HashMap/*<Symbol,HashMap<Symbol,int[]>>*/ mangleMap = new HashMap(); + + public void setMangledName(Symbol innerclazz) { + Symbol topclazz = innerclazz.enclToplevelClass(); + HashMap map = (HashMap) mangleMap.get(topclazz); + if (map == null) { + map = new HashMap(); + mangleMap.put(topclazz, map); + } + int[] ctr = (int[]) map.get(innerclazz); + if (ctr == null) { + ctr = new int[1]; + map.put(innerclazz, ctr); + } + innerclazz.setMangledName( + Name.fromString(topclazz.name + "$" + (ctr[0]++) + innerclazz.name)); + } +} diff --git a/sources/scalac/symtab/Scope.java b/sources/scalac/symtab/Scope.java new file mode 100644 index 0000000000..3bd22f51ed --- /dev/null +++ b/sources/scalac/symtab/Scope.java @@ -0,0 +1,306 @@ + /* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +import scalac.util.*; +import scalac.ApplicationError; + +public class Scope { + + public static abstract class SymbolIterator { + public abstract boolean hasNext(); + public abstract Symbol next(); + } + + /** A symbol iterator that returns all alternatives of an overloaded symbol + * instead of the overloaded symbol itself. + */ + public static class UnloadIterator extends SymbolIterator { + private SymbolIterator iterator; + private Symbol[] alternatives; + private int index; + + public UnloadIterator(SymbolIterator iterator) { + this.iterator = iterator; + this.alternatives = null; + this.index = -1; + } + + public boolean hasNext() { + return index >= 0 || iterator.hasNext(); + } + public Symbol next() { + if (index >= 0) { + Symbol symbol = alternatives[index++]; + if (index == alternatives.length) { + alternatives = null; + index = -1; + } + return symbol; + } else { + Symbol symbol = iterator.next(); + switch (symbol.type()) { + case OverloadedType(Symbol[] alts, _): + alternatives = alts; + index = 0; + return next(); + default: + return symbol; + } + } + } + } + + public static class Entry { + + /** the absent entry + */ + public static final Entry NONE = new Entry(); + + /** the symbol of the entry (this is the symbol containing the name) + */ + public Symbol sym; + + /** the next entry in the hash bucket + */ + Entry tail; + + /** the next entry in this scope + */ + public Entry next; + + /** The owner of the entry; + */ + public Scope owner; + + Entry(Symbol sym, Scope owner) { + if (sym == null) throw new ApplicationError(); + this.sym = sym; + this.owner = owner; + this.next = owner.elems; + owner.elems = this; + } + + private Entry() { + this.sym = Symbol.NONE; + } + + public Entry setSymbol(Symbol sym) { + this.sym = sym; + owner.elemsCache = null; + return this; + } + + public int hashCode() { + return sym.name.index; + } + + public String toString() { + return sym.toString(); + } + } + + /** all elements of this scope + */ + public Entry elems; + + /** the hash table + */ + private Entry[] hashtable; + + /** a cache for all elements, to be used by symbol iterator. + */ + private Symbol[] elemsCache = null; + + /** size and mask of hash tables + * todo: make hashtables grow? + */ + private final int HASHSIZE = 0x80; + private final int HASHMASK = 0x7f; + + /** the threshold number of entries from which a hashtable is constructed. + */ + private final int MIN_HASH = 6; + + /** construct a new name space + */ + public Scope() { + this.elems = Entry.NONE; + } + + public Scope(Entry elems) { + this.elems = elems; + if (size() >= MIN_HASH) createHash(); + } + + public Scope(Scope base) { + this.elems = base.elems; + if (base.hashtable != null) { + this.hashtable = new Entry[HASHSIZE]; + for (int i = 0; i < HASHSIZE; i++) + hashtable[i] = base.hashtable[i]; + } + } + + public Scope(Symbol[] members) { + this(); + for (int i = 0; i < members.length; i++) + enter(members[i]); + } + + /** the number of entries in this scope + */ + int size() { + int s = 0; + for (Entry e = elems; e != Entry.NONE; e = e.next) s++; + return s; + } + + private Scope enter(Entry e) { + elems = e; + elemsCache = null; + if (hashtable != null) { + int i = e.sym.name.index & HASHMASK; + elems.tail = hashtable[i]; + hashtable[i] = elems; + } else if (size() >= MIN_HASH) { + createHash(); + } + return this; + } + + /** enter a symbol + */ + public Scope enter(Symbol sym) { + return enter(new Entry(sym, this)); + } + + public Scope enterOrOverload(Symbol sym) { + Entry e = lookupEntry(sym.name); + if (e.owner == this && (sym.flags & Modifiers.PRIVATE) == 0) { + e.setSymbol(e.sym.overloadWith(sym)); + return this; + } else { + return enter(sym); + } + } + + private void createHash() { + hashtable = new Entry[HASHSIZE]; + for (int i = 0; i < HASHSIZE; i++) + hashtable[i] = Entry.NONE; + enterInHash(elems); + } + + private void enterInHash(Entry e) { + if (e != Entry.NONE) { + enterInHash(e.next); + int i = e.sym.name.index & HASHMASK; + e.tail = hashtable[i]; + hashtable[i] = e; + } + } + + /** remove entry + */ + public void unlink(Entry e) { + Entry e1 = hashtable[e.sym.name.index & HASHMASK]; + if (e1 == e) { + hashtable[e.sym.name.index & HASHMASK] = e.tail; + } else { + while (e1.tail != e) e1 = e1.tail; + } + if (elems == e) { + elems = e.next; + } else { + e1 = elems; + while (e1.next != e) e1 = e1.next; + e1.next = e.next; + } + elemsCache = null; + } + + /** lookup a symbol + */ + public Symbol lookup(Name name) { + return lookupEntry(name).sym; + } + + /** lookup a symbol entry. + */ + public Entry lookupEntry(Name name) { + Entry e; + if (hashtable != null) { + e = hashtable[name.index & HASHMASK]; + while (e != Entry.NONE && e.sym.name != name) e = e.tail; + } else { + e = elems; + while (e != Entry.NONE && e.sym.name != name) e = e.next; + } + return e; + } + + /** return all symbols as an array, + * in the order they were entered in this scope. + */ + public Symbol[] elements() { + if (elemsCache == null) { + int s = size(); + elemsCache = new Symbol[s]; + for (Entry e = elems; e != Entry.NONE; e = e.next) + elemsCache[--s] = e.sym; + } + return elemsCache; + } + + /** return all symbols as an iterator, + * in the order they were entered in this scope. + */ + public SymbolIterator iterator() { return new MySymbols(); } + + class MySymbols extends SymbolIterator { + + private int index; + MySymbols() { + elements(); + index = 0; + } + + public boolean hasNext() { + return index < elemsCache.length; + } + + public Symbol next() { + return elemsCache[index++]; + } + } + + public String toString() { + StringBuffer str = new StringBuffer("{"); + SymbolIterator it = iterator(); + while (it.hasNext()) { + str.append("\n " + it.next().defString()); + } + str.append("}"); + return str.toString(); + } + + public String simpleToString() { + StringBuffer str = new StringBuffer("{"); + SymbolIterator it = iterator(); + while (it.hasNext()) { + str.append("\n " + it.next().name); + } + str.append("}"); + return str.toString(); + } + + public static Scope EMPTY = new Scope(); +} + diff --git a/sources/scalac/symtab/SourceCompleter.java b/sources/scalac/symtab/SourceCompleter.java new file mode 100644 index 0000000000..1656f2029f --- /dev/null +++ b/sources/scalac/symtab/SourceCompleter.java @@ -0,0 +1,53 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +import scalac.*; +import scalac.ast.parser.*; +import scalac.typechecker.Analyzer; +import java.io.*; + + +public class SourceCompleter extends Type.LazyType { + + /** the global compilation environment + */ + protected Global global; + protected String filename; + private boolean completed = false; + + public SourceCompleter(Global global, String filename) { + this.global = global; + this.filename = filename; + } + + /** complete class symbol c by loading the class + */ + public void complete(Symbol c) { + if (completed) { + c.setInfo(Type.ErrorType); + } else if (filename != null) { + try { + String fname = filename; + long msec = System.currentTimeMillis(); + Unit unit = new Unit(global, new Sourcefile(filename, false)); + filename = null; + global.PHASE.PARSER.createPhase(global).apply(unit); + ((Analyzer)global.PHASE.ANALYZER.createPhase(global)).lateEnter(unit, c); + global.operation("added " + fname + " in " + + (System.currentTimeMillis() - msec) + "ms"); + } catch (IOException e) { + e.printStackTrace(); + global.error("i/o error while loading " + c); + c.setInfo(Type.ErrorType); + } + completed = true; + } + } +} diff --git a/sources/scalac/symtab/SymSet.java b/sources/scalac/symtab/SymSet.java new file mode 100644 index 0000000000..f72b9764f5 --- /dev/null +++ b/sources/scalac/symtab/SymSet.java @@ -0,0 +1,89 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.symtab; + +/** Sets of symbols, implemented as binary trees. + */ +public class SymSet { + private SymSet l, r; + private Symbol sym; + + public SymSet() {} + + private SymSet(Symbol sym, SymSet l, SymSet r) { + this.sym = sym; this.l = l; this.r = r; + } + + /** Union of this set and `{sym}'. + */ + public SymSet incl(Symbol sym) { + if (this == EMPTY) { + return new SymSet(sym, EMPTY, EMPTY); + } else if (sym == this.sym) { + return this; + } else if (sym.isLess(this.sym)) { + return new SymSet(this.sym, l.incl(sym), r); + } else { + assert this.sym.isLess(sym); + return new SymSet(this.sym, l, r.incl(sym)); + } + } + + /** Is `sym' an element of this set? + */ + public boolean contains(Symbol sym) { + if (this == EMPTY) { + return false; + } else if (sym == this.sym) { + return true; + } else if (sym.isLess(this.sym)) { + return l.contains(sym); + } else { + assert this.sym.isLess(sym); + return r.contains(sym); + } + } + + /** The number of elements in ths set. + */ + public int size() { + if (this == EMPTY) { + return 0; + } else { + return 1 + l.size() + r.size(); + } + } + + /** Copy elements of this set into `ss', starting at index `from'. + * Return index one past last element copied. + */ + public int copyToArray(Symbol[] ss, int from) { + if (this == EMPTY) { + return from; + } else { + from = l.copyToArray(ss, from); + ss[from] = sym; + return r.copyToArray(ss, from + 1); + } + } + + /** Return all elements of this set as an array. + */ + public Symbol[] toArray() { + int s = size(); + if (s == 0) return Symbol.EMPTY_ARRAY; + Symbol[] ss = new Symbol[s]; + copyToArray(ss, 0); + return ss; + } + + /** The empty set. + */ + public final static SymSet EMPTY = new SymSet(); +} diff --git a/sources/scalac/symtab/Symbol.java b/sources/scalac/symtab/Symbol.java new file mode 100644 index 0000000000..f13192848b --- /dev/null +++ b/sources/scalac/symtab/Symbol.java @@ -0,0 +1,1291 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ 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 == constructorClass().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.constructorClass(); + 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 constructor, return the class it constructs. + * Otherwise return the symbol itself. + */ + public Symbol constructorClass() { + 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 "<root package>"; + if (isAnonymousClass()) return "<template>"; + String kstr = kindString(); + String str; + if (kstr.length() == 0) str = fullNameString(); + else str = kstr + " " + fullNameString(); + return str + idString(); + } + + /** String representation of location. + */ + public String locationString() { + if (owner.kind == CLASS && !owner.isAnonymousClass()) + return " in " + owner; + else + return ""; + } + + /** String representation of definition. + */ + public String defString() { + String inner; + if (kind == CLASS) inner = " extends "; + else if (kind == TYPE) inner = " <: "; + else if (kind == ALIAS) inner = " = "; + else inner = " : "; + return + (isParameter() ? "" : defKeyword() + " ") + + nameString() + idString() + inner + + (rawInfoAt(Global.instance.POST_ANALYZER_PHASE_ID) + instanceof Type.LazyType ? "?" : info()); + } + + public static String[] defString(Symbol[] defs) { + String[] strs = new String[defs.length]; + for (int i = 0; i < defs.length; i++) + strs[i] = defs[i].defString(); + return strs; + } + + /** String representation of kind */ + public String kindString() { + switch (kind) { + case CLASS: + if ((flags & TRAIT) != 0) + return "trait"; + else if ((flags & MODUL) != 0 && Global.instance.debug) + return "module class"; + else + return "class"; + case TYPE: + case ALIAS: + return "type"; + case VAL: + if (isVariable()) return "variable"; + else if (isModule()) return "module"; + else if (isConstructor()) return "constructor"; + else if (isInitializedMethod()) return "method"; + else return "value"; + default: return ""; + } + } + + /** Definition keyword of kind + */ + public String defKeyword() { + switch (kind) { + case CLASS: if ((flags & TRAIT) != 0) return "trait"; else return "class"; + case TYPE: + case ALIAS: return "type"; + case VAL: + if (isVariable()) return "var"; + else if (isModule()) return "module"; + else if (isInitializedMethod()) return "def"; + else return "val"; + default: return ""; + } + } + +// Overloading and Overriding ------------------------------------------- + + /** Add another overloaded alternative to this symbol. + */ + public Symbol overloadWith(Symbol that) { + assert isTerm() : this; + assert this.name == that.name : this + " " + that; + assert this.owner == that.owner : this + " " + that; + assert (this.flags & that.flags & JAVA) != 0 || + (this.flags & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) == + (that.flags & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) : this + " " + that; + TermSymbol overloaded = new TermSymbol( + pos, name, owner, + ((this.flags | that.flags) & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) | + (this.flags & that.flags & ACCESSFLAGS)); + overloaded.setInfo(new LazyOverloadedType(this, that)); + return overloaded; + } + + /** A lazy type which, when forced computed the overloaded type + * of symbols `sym1' and `sym2'. It also checks that this type is well-formed. + */ + private static class LazyOverloadedType extends Type.LazyType { + Symbol sym1; + Symbol sym2; + LazyOverloadedType(Symbol sym1, Symbol sym2) { + this.sym1 = sym1; + this.sym2 = sym2; + } + private Symbol[] alts(Symbol sym) { + if (sym == null) return Symbol.EMPTY_ARRAY; + switch (sym.type()) { + case OverloadedType(Symbol[] alts, _): return alts; + default: return new Symbol[]{sym}; + } + } + private Type[] alttypes(Symbol sym) { + if (sym == null) return Type.EMPTY_ARRAY; + switch (sym.type()) { + case OverloadedType(_, Type[] alttypes): return alttypes; + default: return new Type[]{sym.type()}; + } + } + public void complete(Symbol overloaded) { + if (sym1 != null) sym1.initialize(); + if (sym2 != null) sym2.initialize(); + + Symbol[] alts1 = alts(sym1); + Symbol[] alts2 = alts(sym2); + Symbol[] alts3 = new Symbol[alts1.length + alts2.length]; + System.arraycopy(alts1, 0, alts3, 0, alts1.length); + System.arraycopy(alts2, 0, alts3, alts1.length, alts2.length); + + Type[] alttypes1 = alttypes(sym1); + Type[] alttypes2 = alttypes(sym2); + Type[] alttypes3 = new Type[alttypes1.length + alttypes2.length]; + System.arraycopy(alttypes1, 0, alttypes3, 0, alttypes1.length); + System.arraycopy(alttypes2, 0, alttypes3, alttypes1.length, alttypes2.length); + overloaded.setInfo(Type.OverloadedType(alts3, alttypes3)); + } + } + + /** All the alternatives of this symbol if it's overloaded, the + * symbol alone otherwise. + */ + public Symbol[] alternatives() { + switch (type()) { + case OverloadedType(Symbol[] alts, _): return alts; + default: return new Symbol[]{this}; + } + } + + /** The symbol which is overridden by this symbol in base class `base' + * `base' must be a superclass of this.owner(). + */ + public Symbol overriddenSymbol(Type base) { + Symbol sym1 = base.lookupNonPrivate(name); + if (sym1.kind == Kinds.NONE || (sym1.flags & STATIC) != 0) { + return Symbol.NONE; + } else { + //System.out.println(this + ":" + this.type() + locationString() + " overrides? " + sym1 + sym1.type() + sym1.locationString()); //DEBUG + + Type symtype = owner.thisType().memberType(this); + //todo: try whether we can do: this.type(); instead + Type sym1type = owner.thisType().memberType(sym1); + switch (sym1type) { + case OverloadedType(Symbol[] alts, Type[] alttypes): + for (int i = 0; i < alts.length; i++) { + if (symtype.isSameAs(alttypes[i])) return alts[i]; + } + return Symbol.NONE; + default: + if (symtype.isSameAs(sym1type)) return sym1; + else { + System.out.println(this + locationString() + " does not override " + sym1 + sym1.locationString() + ", since " + symtype + " # " + sym1type);//DEBUG + return Symbol.NONE; + } + } + } + } +} + +/** A class for term symbols + */ +public class TermSymbol extends Symbol { + + private Symbol clazz; + + /** Constructor */ + public TermSymbol(int pos, Name name, Symbol owner, int flags) { + super(VAL, pos, name, owner, flags); + } + + public static TermSymbol newConstructor(Symbol clazz, int flags) { + TermSymbol sym = new TermSymbol( + clazz.pos, clazz.name.toConstrName(), clazz.owner(), + flags | FINAL); + sym.clazz = clazz; + return sym; + } + + public static TermSymbol newJavaConstructor(Symbol clazz) { + return newConstructor(clazz, clazz.flags & (ACCESSFLAGS | QUALIFIED | JAVA)); + } + + public static TermSymbol newModule(int pos, Name name, Symbol owner, int flags) { + TermSymbol sym = new TermSymbol(pos, name, owner, flags | MODUL | FINAL); + Symbol clazz = new ClassSymbol( + pos, name.toTypeName(), owner, flags | MODUL | FINAL, sym); + Type clazztype = Type.TypeRef(owner.thisType(), clazz, Type.EMPTY_ARRAY); + clazz.constructor().setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, clazztype)); + sym.clazz = clazz; + sym.setInfo(clazztype); + return sym; + } + + /** Constructor for companion modules to classes, which need to be completed. + */ + public static TermSymbol newCompanionModule(Symbol clazz, int flags, Type.LazyType parser) { + TermSymbol sym = newModule(Position.NOPOS, clazz.name.toTermName(), clazz.owner(), + FINAL | flags); + sym.clazz.setInfo(parser); + return sym; + } + + /** Java package module constructor + */ + public static TermSymbol newJavaPackageModule(Name name, Symbol owner, Type.LazyType parser) { + TermSymbol sym = newModule(Position.NOPOS, name, owner, JAVA | PACKAGE); + sym.clazz.flags |= SYNTHETIC; + sym.clazz.setInfo(parser); + return sym; + } + + /** Get this.type corresponding to this class or module + */ + public Type thisType() { + if ((flags & MODUL) != 0) return moduleClass().thisType(); + else return Type.localThisType; + } + /** Get the fully qualified name of this Symbol */ + public Name fullName() { + if ((flags & MODUL) != 0) return moduleClass().fullName(); + else return super.fullName(); + } + + /** Return a fresh symbol with the same fields as this one. + */ + public Symbol cloneSymbol() { + assert !isPrimaryConstructor() : Debug.show(this); + TermSymbol other; + if (isModule()) { + other = newModule(pos, name, owner(), flags); + } else { + other = new TermSymbol(pos, name, owner(), flags); + other.clazz = clazz; + } + other.setInfo(info()); + return other; + } + + public Symbol constructorClass() { + return isConstructor() ? clazz : this; + } + + public Symbol moduleClass() { + return (flags & MODUL) != 0 ? clazz : this; + } +} + +/** A class for (abstract and alias) type symbols. It has ClassSymbol as a subclass. + */ +public class TypeSymbol extends Symbol { + + /** A cache for closures + */ + private ClosureIntervalList closures = ClosureIntervalList.EMPTY; + + /** The symbol's type template */ + private Type template; + + /** The primary constructor of this type */ + public final Symbol constructor; + + /** Constructor */ + public TypeSymbol(int kind, int pos, Name name, Symbol owner, int flags) { + super(kind, pos, name, owner, flags); + this.constructor = TermSymbol.newConstructor(this, flags); + if (kind == TYPE) { // provide a constructor type + this.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType)); + } + } + + /** Return a fresh symbol with the same fields as this one. + */ + public Symbol cloneSymbol() { + if (Global.instance.debug) System.out.println("cloning " + this + this.locationString()); + TypeSymbol other = new TypeSymbol(kind, pos, name, owner(), flags); + other.setInfo(info()); + other.constructor.setInfo(constructor.info()); + return other; + } + + /** Get self type */ + public Type type() { + if (template == null || template.typeArgs().length != typeParams().length) { + template = Type.TypeRef( + owner().thisType(), this, type(typeParams())); + } + return template; + } + + //todo: needed? + public Type typeAt(int id) { + return type(); + } + + /** Get type parameters */ + public Symbol[] typeParams() { + return constructor.info().typeParams(); + } + + /** Get primary constructor */ + public Symbol constructor() { + return constructor; + } + + public Type[] closure() { + if (kind == ALIAS) return info().symbol().closure(); + int id = Global.instance.currentPhase.id; + if (closures.limit < id) { + if (id == 0 || changes(closureAt(id - 1))) { + closures = new ClosureIntervalList(closures); + closures.limit = id; + computeClosure(); + } else { + closures.limit = id; + } + return closures.closure; + } else { + ClosureIntervalList closures1 = closures; + while (closures1.prev.limit >= id) { + closures1 = closures1.prev; + } + return closures1.closure; + } + } + + //todo: needed? + private Type[] closureAt(int id) { + PhaseDescriptor savedPhase = Global.instance.currentPhase; + Global.instance.currentPhase = Global.instance.phases[id]; + Type[] c = closure(); + Global.instance.currentPhase = savedPhase; + return c; + } + + private boolean changes(Type[] closure) { + for (int i = 0; i < closure.length; i++) { + Symbol c = closure[i].symbol(); + if (c.infoAt(Global.instance.currentPhase.id - 1) != c.info()) + return true; + } + return false; + } + + private static Type[] BAD_CLOSURE = new Type[0]; + + /** Return the type itself followed by all direct and indirect + * base types of this type, sorted by isLess(). + */ + private void computeClosure() { + assert closures.closure != BAD_CLOSURE : this; + closures.closure = BAD_CLOSURE; // to catch cycles. + SymSet closureClassSet = inclClosureBases(SymSet.EMPTY, this); + Symbol[] closureClasses = new Symbol[closureClassSet.size() + 1]; + closureClasses[0] = this; + closureClassSet.copyToArray(closureClasses, 1); + //System.out.println(ArrayApply.toString(closureClasses));//DEBUG + closures.closure = Symbol.type(closureClasses); + //System.out.println(ArrayApply.toString(closures.closure));//DEBUG + adjustType(type()); + //System.out.println("closure(" + this + ") at " + Global.instance.currentPhase.name() + " = " + ArrayApply.toString(closures.closure));//DEBUG + } + //where + + private SymSet inclClosureBases(SymSet set, Symbol c) { + Type[] parents = c.type().parents(); + for (int i = 0; i < parents.length; i++) { + set = inclClosure(set, parents[i].symbol()); + } + return set; + } + + private SymSet inclClosure(SymSet set, Symbol c) { + Symbol c1 = c; + while (c1.kind == ALIAS) c1 = c1.info().symbol(); + return inclClosureBases(set.incl(c1), c1); + } + + void adjustType(Type tp) { + Type tp1 = tp.unalias(); + int pos = closurePos(tp1.symbol()); + assert pos >= 0 : this + " " + tp1 + " " + tp1.symbol(); + closures.closure[pos] = tp1; + Type[] parents = tp1.parents(); + for (int i = 0; i < parents.length; i++) { + adjustType(parents[i]); + } + } +} + +/** A class for class symbols. It has JavaClassSymbol as a subclass. + */ +public class ClassSymbol extends TypeSymbol { + + /** The mangled class name */ + private Name mangled; + + /** The module belonging to the class. This means: + * For Java classes, its statics parts. + * For module classes, the corresponding module. + * For other classes, null. + */ + private Symbol module = NONE; + + /** Principal Constructor for module classes and classes with static members. + */ + public ClassSymbol(int pos, Name name, Symbol owner, int flags) { + super(CLASS, pos, name, owner, flags); + this.mangled = name; + } + + /** Constructor for module classes and classes with static members. + */ + public ClassSymbol(int pos, Name name, Symbol owner, int flags, Symbol module) { + this(pos, name, owner, flags); + this.module = module; + } + + /** Constructor for classes to load as source files + */ + public ClassSymbol(Name name, Symbol owner, SourceCompleter parser) { + this(Position.NOPOS, name, owner, 0); + this.module = TermSymbol.newCompanionModule(this, 0, parser); + this.mangled = name; + this.setInfo(parser); + } + + /** Constructor for classes to load as class files. + */ + public ClassSymbol(Name name, Symbol owner, ClassParser parser) { + super(CLASS, Position.NOPOS, name, owner, JAVA); + this.module = TermSymbol.newCompanionModule(this, JAVA, parser.staticsParser(this)); + this.mangled = name; + this.setInfo(parser); + } + + /** Return a fresh symbol with the same fields as this one. + */ + public Symbol cloneSymbol() { + ClassSymbol other = new ClassSymbol(pos, name, owner(), flags); + other.setInfo(info()); + other.constructor.setInfo(constructor.info()); + other.mangled = mangled; + other.module = module; + return other; + } + + /** Get module */ + public Symbol module() { + return module; + } + + /** Set the mangled name of this Symbol */ + public Symbol setMangledName(Name name) { + this.mangled = name; + return this; + } + + /** Get the fully qualified name of this Symbol */ + public Name fullName() { + if (owner().kind == CLASS && owner().name.length() != 0) + return Name.fromString(owner().fullName() + "." + name); + else + return name.toTermName(); + } + + /** Get the mangled name of this Symbol */ + public Name mangledName() { + return mangled; + } + + /** Get the fully qualified mangled name of this Symbol */ + public Name mangledFullName() { + if (mangled == name) { + return fullName().replace((byte)'.', (byte)'$'); + } else { + return Name.fromString( + enclToplevelClass().mangledFullName() + "$" + mangled); + } + } + + private Type thistp = Type.ThisType(this); + + public Type thisType() { + return thistp; + } + + /** Return the next enclosing class */ + public Symbol enclClass() { + return this; + } + + public Symbol caseFieldAccessor(int index) { + assert (flags & CASE) != 0 : this; + Scope.SymbolIterator it = info().members().iterator(); + Symbol sym = null; + for (int i = 0; i <= index; i++) { + do { + sym = it.next(); + } while (sym.kind != VAL || (sym.flags & CASE) == 0); + } + //System.out.println(this + ", case field[" + index + "] = " + sym);//DEBUG + assert sym != null : this; + return sym; + } +} + +/** A class for error symbols. + */ +public final class ErrorSymbol extends Symbol { + + /** Constructor */ + public ErrorSymbol() { + super(Kinds.ERROR, Position.NOPOS, Name.fromString("<error>"), null, + INITIALIZED); + this.setOwner(this); + this.setInfo(Type.ErrorType); + } + + public Symbol cloneSymbol() { + return this; + } + + /** Set the mangled name of this Symbol */ + public Symbol mangled(Name name) { + return this; + } + + /** Set owner */ + public Symbol setOwner(Symbol owner) { + if (owner != this) + throw new ApplicationError("illegal operation on " + getClass()); + return super.setOwner(owner); + } + + /** Set type */ + public Symbol setInfo(Type info) { + if (info != Type.ErrorType) + throw new ApplicationError("illegal operation on " + getClass()); + return super.setInfo(info); + } + + /** Get primary constructor */ + public Symbol constructor() { + return TermSymbol.newConstructor(this, 0).setInfo(Type.ErrorType); + } + + /** Return the next enclosing class */ + public Symbol enclClass() { + return this; + } +} + +/** The class of Symbol.NONE + */ +public final class NoSymbol extends Symbol { + + /** Constructor */ + public NoSymbol() { + super(Kinds.NONE, Position.NOPOS, Name.fromString("<none>"), null, INITIALIZED); + this.setInfo(Type.NoType); + this.setOwner(this); + } + + /** Return a fresh symbol with the same fields as this one. + */ + public Symbol cloneSymbol() { + return this; + } + + /** Set the mangled name of this Symbol */ + public Symbol mangled(Name name) { + throw new ApplicationError("illegal operation on " + getClass()); + } + + /** Set owner */ + public Symbol setOwner(Symbol owner) { + if (owner != this) + throw new ApplicationError("illegal operation on " + getClass()); + return super.setOwner(owner); + } + + /** Set type */ + public Symbol setInfo(Type info) { + if (info != Type.NoType) + throw new ApplicationError("illegal operation on " + getClass()); + return super.setInfo(info); + } + + /** Return the next enclosing class */ + public Symbol enclClass() { + return this; + } + + public Symbol owner() { + throw new ApplicationError(); + } +} + +/** A class for symbols generated in label definitions. + */ +public class LabelSymbol extends TermSymbol { + + /** give as argument the symbol of the function that triggered + the creation of this label */ + public LabelSymbol(Symbol f) { + super(f.pos, f.name, f, LABEL); + } +} + +/** An exception for signalling cyclic references. + */ +public class CyclicReference extends Type.Error { + public Symbol sym; + public Type info; + public CyclicReference(Symbol sym, Type info) { + super("illegal cyclic reference involving " + sym); + this.sym = sym; + this.info = info; + } +} + +/** A class for types indexed by phase numbers. + */ +class TypeIntervalList { + int limit; + Type info; + TypeIntervalList prev; + TypeIntervalList(TypeIntervalList prev) { + this.prev = prev; + } + static TypeIntervalList EMPTY = new TypeIntervalList(null); + + static { + EMPTY.limit = -1; + } +} + +/** A class for closures indexed by phase numbers. + */ +class ClosureIntervalList { + int limit; + Type[] closure; + ClosureIntervalList prev; + ClosureIntervalList(ClosureIntervalList prev) { + this.prev = prev; + } + static ClosureIntervalList EMPTY = new ClosureIntervalList(null); + static { + EMPTY.limit = -1; + } +} + diff --git a/sources/scalac/symtab/Type.java b/sources/scalac/symtab/Type.java new file mode 100644 index 0000000000..3ec37799f4 --- /dev/null +++ b/sources/scalac/symtab/Type.java @@ -0,0 +1,2353 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ +//todo: implement rule single + +package scalac.symtab; + +import scalac.ApplicationError; +import scalac.util.*; +import scalac.Global; + +public class Type implements Modifiers, Kinds, TypeTags { + + public static boolean debugSwitch = false; + private static int indent = 0; + + //todo: convert C with {} to C. + + public case ErrorType; // not used after analysis + public case AnyType; // not used after analysis + public case NoType; + + public case ThisType(Symbol sym); + + public case TypeRef(Type pre, Symbol sym, Type[] args); + + public case SingleType(Type pre, Symbol sym) { + assert this instanceof ExtSingleType; + } + + public case CompoundType(Type[] parts, Scope members) { + assert this instanceof ExtCompoundType; + } + public case MethodType(Symbol[] vparams, Type result); + public case PolyType(Symbol[] tparams, Type result); + public case OverloadedType(Symbol[] alts, Type[] alttypes); + + /** Type such as +T. Only used as type arguments. + */ + public case CovarType(Type tp) { + assert !(tp instanceof CovarType); + } + + /** Hidden case to implement delayed evaluation of types. + * No need to pattern match on this type; it will never come up. + */ + public case LazyType(); + + /** Hidden case to implement local type inference. + * Later phases do not need to match on this type. + */ + public case TypeVar(Type origin, Constraint constr); + + /** Hidden cases to implement type erasure. + * Earlier phases do not need to match on these types. + */ + public case UnboxedType(int tag); + public case UnboxedArrayType(Type elemtp); + + /** Force evaluation of a lazy type. No cycle + * check is needed; since this is done in Symbol. + * @see Symbol.info(). + */ + public void complete(Symbol p) {} + +// Creators --------------------------------------------------------------------- + + /** An owner-less ThisType + */ + public static Type localThisType = ThisType(Symbol.NONE); + + /** An empty Type array */ + public static final Type[] EMPTY_ARRAY = new Type[0]; + + /** A non-existing Type array; used to express type erasure */ + public static final Type[] NO_ARRAY = new Type[0]; + + public static SingleType singleType(Type pre, Symbol sym) { + return new ExtSingleType(pre, sym); + } + + public static TypeRef appliedType(Type tycon, Type[] args) { + switch (tycon) { + case TypeRef(Type pre, Symbol sym, Type[] args1): + if (args == args1) return (TypeRef)tycon; + else return TypeRef(pre, sym, args); + default: + throw new ApplicationError(); + } + } + + public static CovarType covarType(Type tp) { + if (tp instanceof CovarType) return (CovarType)tp; + else return CovarType(tp); + } + + public static CompoundType compoundType(Type[] parts, Scope members, + Symbol clazz) { + ExtCompoundType res = new ExtCompoundType(parts, members); + res.tsym = clazz; + return res; + } + + public static CompoundType compoundType(Type[] parts, Scope members) { + ExtCompoundType res = new ExtCompoundType(parts, members); + res.tsym = new ClassSymbol( + Position.NOPOS, Names.COMPOUND_NAME.toTypeName(), Symbol.NONE, + SYNTHETIC | ABSTRACTCLASS); + res.tsym.setInfo(res); + res.tsym.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType)); + return res; + } + + static class ExtSingleType extends SingleType { + Type tp = null; + int definedId = -1; + ExtSingleType(Type pre, Symbol sym) { + super(pre, sym); + } + public Type widen() { + if (definedId != Global.instance.currentPhase.id) { + definedId = Global.instance.currentPhase.id; + tp = pre.memberType(sym).widen(); + } + return tp; + } + } + + static class ExtCompoundType extends CompoundType { + Symbol tsym; + ExtCompoundType(Type[] parts, Scope members) { + super(parts, members); + } + public Symbol symbol() { + return tsym; + } + void validate() {//debug + for (Scope.Entry e = members.elems; e != Scope.Entry.NONE; e = e.next) + assert e.sym.owner() == tsym; + } + } + + void validate() {//debug + } + +// Access methods --------------------------------------------------------------- + + /** If this is a thistype, named type, applied type, singleton type, or compound type, + * its symbol, otherwise Symbol.NONE. + */ + public Symbol symbol() { + switch (this) { + case ErrorType: + return Symbol.ERROR; + case ThisType(Symbol sym): + return sym; + case TypeRef(_, Symbol sym, _): + return sym; + case SingleType(_, Symbol sym): + return sym; + case TypeVar(Type origin, _): + return origin.symbol(); + case CompoundType(_, _): + // overridden in ExtCompoundType + throw new ApplicationError(); + default: + return Symbol.NONE; + } + } + + public static Symbol[] symbol(Type[] tps) { + Symbol[] syms = new Symbol[tps.length]; + for (int i = 0; i < syms.length; i++) + syms[i] = tps[i].symbol(); + return syms; + } + + /** If this type is a thistype or singleton type, its underlying object type, + * otherwise the type itself. + */ + public Type widen() { + switch (this) { + case ThisType(Symbol sym): + return sym.type(); + case SingleType(Type pre, Symbol sym): + // overridden in ExtSingleType + throw new ApplicationError(); + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) return constr.inst.widen(); + else return this; + default: + return this; + } + } + + public static Type[] widen(Type[] tps) { + if (tps.length == 0) return Type.EMPTY_ARRAY; + Type[] tps1 = new Type[tps.length]; + for (int i = 0; i < tps1.length; i++) { + tps1[i] = tps[i].widen(); + assert !(tps1[i] instanceof SingleType) : tps[i];//debug + } + return tps1; + } + + /** The thistype or singleton type corresponding to values of this type. + */ + public Type narrow() { + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.kind == ALIAS) return pre.memberInfo(sym).narrow(); + else if (sym.kind == CLASS) return sym.thisType(); + else return ThisType(sym); + case CompoundType(_, _): + return symbol().thisType(); + default: + return this; + } + } + + /** The this is a this-type, named-type, applied type or single-type, its prefix, + * otherwise NoType. + */ + public Type prefix() { + switch (this) { + case ThisType(Symbol sym): return sym.owner().thisType(); + case TypeRef(Type pre, _, _): return pre; + case SingleType(Type pre, _): return pre; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) return constr.inst.prefix(); + else return NoType; + default: return NoType; + } + } + + /** Get all type arguments of this type. + */ + public Type[] typeArgs() { + switch (unalias()) { + case TypeRef(_, _, Type[] args): + return args; + default: + return Type.EMPTY_ARRAY; + } + } + + /** Remove all aliases + */ + public Type unalias() { + Type result = unalias(0);//debug + //if (this != result) System.out.println(this + " ==> " + result);//DEBUG + return result; + } + + private Type unalias(int n) { + if (n == 20) throw new Type.Error("recursive type alias: " + this); + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.kind == ALIAS) { + return pre.memberInfo(sym).subst(sym.typeParams(), args).unalias(n + 1); + } + break; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) return constr.inst.unalias(n + 1); + else return this; + } + return this; + } + + /** The (prefix-adapted) parents of this type. + */ + public Type[] parents() { + switch (unalias()) { + case ThisType(_): + case SingleType(_, _): + return widen().parents(); + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.kind == ALIAS) + return unalias().parents(); + else if (sym.kind == CLASS) { + assert sym.typeParams().length == args.length : sym + " " + ArrayApply.toString(args);//debug + return subst(asSeenFrom(sym.info().parents(), pre, sym.owner()), + sym.typeParams(), args); + } else + return new Type[]{sym.info().asSeenFrom(pre, sym.owner())}; + case CompoundType(Type[] parts, _): + return parts; + default: + return Type.EMPTY_ARRAY; + } + } + + /** Get type parameters of polymorphic method + * or EMPTY_ARRAY if not applicable. + */ + public Symbol[] typeParams() { + switch (this) { + case PolyType(Symbol[] tparams, _): + return tparams; + case TypeRef(_, Symbol sym, _): + if (sym.kind == CLASS || sym.kind == ALIAS) return sym.typeParams(); + break; + } + return Symbol.EMPTY_ARRAY; + } + + /** If this type is a (possibly polymorphic) method type, its result type + * after applying all method argument sections, + * otherwise the type itself. + */ + public Type resultType() { + switch (this) { + case PolyType(_, Type tpe): + return tpe.resultType(); + case MethodType(_, Type tpe): + return tpe.resultType(); + default: + return this; + } + } + + /** The number of value parameter sections of this type. + */ + public int paramSectionCount() { + switch (this) { + case PolyType(_, Type restpe): + return restpe.paramSectionCount(); + case MethodType(_, Type restpe): + return restpe.paramSectionCount() + 1; + default: return 0; + } + } + + /** The arity of the first parameter section of this type. + */ + public Symbol[] firstParams() { + switch (this) { + case PolyType(_, Type restpe): + return restpe.firstParams(); + case MethodType(Symbol[] params, _): + return params; + default: return Symbol.EMPTY_ARRAY; + } + } + + /** If this type is overloaded, its alternative types, + * otherwise an array consisting of this type itself. + */ + public Type[] alternatives() { + switch (this) { + case OverloadedType(_, Type[] alttypes): + return alttypes; + default: + return new Type[]{this}; + } + } + + /** If type is a this type of a module class, transform to singletype of + * module. + */ + public Type expandModuleThis() { + switch (this) { + case ThisType(Symbol sym): + if ((sym.flags & MODUL) != 0 && sym.module() != Symbol.NONE) { + return singleType( + sym.owner().thisType().expandModuleThis(), sym.module()); + } + } + return this; + } + + /** If this is a covariant type, its underlying type, otherwise the type itself. + */ + public Type dropVariance() { + switch (this) { + case CovarType(Type tp): return tp; + default: return this; + } + } + + public static Map dropVarianceMap = new Map() { + public Type apply(Type t) { return t.dropVariance(); } + }; + +// Tests -------------------------------------------------------------------- + + /** Is this type a this type or singleton type? + */ + public boolean isStable() { + switch (unalias()) { + case ThisType(_): + case SingleType(_, _): + case ErrorType: + return true; + default: + return false; + } + } + + /** Is this type a reference to an object type? + */ + public boolean isObjectType() { + switch (unalias()) { + case ThisType(_): + case SingleType(_, _): + case CompoundType(_, _): + return true; + case TypeRef(Type pre, Symbol sym, _): + return sym.kind != ALIAS || unalias().isObjectType(); + default: + return false; + } + } + + /** Is this type a covariant type? + */ + public boolean isCovarType() { + switch (this) { + case CovarType(_): + return true; + default: + return false; + } + } + + /** Is this type of the form scala.Tuple_N[+T_0, ..., +T_N-1]? + */ + public boolean isTupleType() { + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.owner() == Global.instance.definitions.SCALA_CLASS && + sym.name.startsWith(Names.Tuple)) { + for (int i = 0; i < args.length; i++) + if (!args[i].isCovarType()) return false; + return true; + } + } + return false; + } + + /** Is this type of the form scala.FunctionN[T_1, ..., T_n, +T]? + */ + public boolean isFunctionType() { + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.fullName().startsWith(Names.Function)) + for (int i = 0; i < args.length - 1; i++) + if (args[i].isCovarType()) return false; + return args.length > 0 && args[args.length - 1].isCovarType(); + default: + return false; + } + } + +// Members and Lookup ------------------------------------------------------- + + /** Get the scope containing the local members of this type. + * Symbols in this scope are not prefix-adapted! + */ + public Scope members() { + switch (this) { + case ErrorType: + return new Scope(); + case TypeRef(_, Symbol sym, _): + return sym.info().members(); + case SingleType(_, Symbol sym): + return widen().members(); + case CompoundType(Type[] basetypes, Scope members): + return members; + default: + return Scope.EMPTY; + } + } + + /** Lookup symbol with given name among all local and inherited members + * of this type; return Symbol.NONE if not found. + */ + public Symbol lookup(Name name) { + switch (this) { + case ErrorType: + return Symbol.ERROR; + case ThisType(_): + case SingleType(_, _): + return widen().lookup(name); + case TypeRef(_, Symbol sym, _): + return sym.info().lookup(name); + case CompoundType(Type[] parts, Scope members): + Symbol sym = members.lookup(name); + if (sym.kind != NONE) return sym; + else return lookupNonPrivate(name); + default: + return Symbol.NONE; + } + } + + /** Lookup non-private symbol with given name among all local and + * inherited members of this type; return Symbol.NONE if not found. + */ + public Symbol lookupNonPrivate(Name name) { + return lookupNonPrivate(name, 0); + } + + /** Same as before, but with additional parameter `start'. + * If start == 0, lookup in all basetypes of a compound type. + * If start == 1, lookup only in mixin classes. + */ + private Symbol lookupNonPrivate(Name name, int start) { + switch (this) { + case ErrorType: + return Symbol.ERROR; + case ThisType(_): + case SingleType(_, _): + return widen().lookupNonPrivate(name); + case TypeRef(_, Symbol sym, _): + return sym.info().lookupNonPrivate(name, start); + case CompoundType(Type[] parts, Scope members): + Symbol sym = members.lookup(name); + if (sym.kind != NONE && (sym.flags & PRIVATE) == 0) + return sym; + + // search base types in reverse; non-abstract members + // take precedence over abstract ones. + int i = parts.length; + sym = Symbol.NONE; + while (i > start && (sym.kind == NONE || (sym.flags & ABSTRACT) != 0)) { + i--; + Symbol sym1 = parts[i].lookupNonPrivate(name, i == 0 ? 0 : 1); + if (sym1.kind != NONE && + (sym1.flags & PRIVATE) == 0 && + (sym.kind == NONE || (sym1.flags & ABSTRACT) == 0)) + sym = sym1; + } + return sym; + default: + return Symbol.NONE; + } + } + +// Maps -------------------------------------------------------------------------- + + /** The type of type-to-type functions. + */ + public abstract static class Map { + + boolean covariantOK = true; + + public abstract Type apply(Type t); + + /** Apply map to all top-level components of this type. + */ + public Type map(Type tp) { + switch (tp) { + case ErrorType: + case AnyType: + case NoType: + case UnboxedType(_): + case TypeVar(_, _): + case ThisType(_): + return tp; + case TypeRef(Type pre, Symbol sym, Type[] args): + Type pre1 = map(pre); + Type[] args1 = map(args); + if (pre1 == pre && args1 == args) return tp; + else return TypeRef(pre1, sym, args1); + case SingleType(Type pre, Symbol sym): + Type pre1 = map(pre); + if (pre1 == pre) return tp; + else return singleType(pre1, sym); + case CompoundType(Type[] parts, Scope members): + Type[] parts1 = map(parts); + Scope members1 = map(members); + if (parts1 == parts && members1 == members) { + return tp; + } else { + Scope members2 = new Scope(); + Type tp1 = (tp.symbol().isCompoundSym()) ? compoundType(parts1, members2) + : compoundType(parts1, members2, tp.symbol()); + Symbol[] syms1 = members1.elements(); + Symbol[] syms2 = new Symbol[syms1.length]; + for (int i = 0; i < syms2.length; i++) { + syms2[i] = syms1[i].cloneSymbol().setOwner(tp1.symbol()); + } + for (int i = 0; i < syms2.length; i++) { + syms2[i].setInfo(syms1[i].info().subst(syms1, syms2)); + } + for (int i = 0; i < syms2.length; i++) { + members2.enter(syms2[i]); + } + return tp1; + } + + case MethodType(Symbol[] vparams, Type result): + covariantOK = false; + Symbol[] vparams1 = map(vparams); + covariantOK = true; + Type result1 = apply(result); + if (vparams1 == vparams && result1 == result) return tp; + else return MethodType(vparams1, result1); + case PolyType(Symbol[] tparams, Type result): + covariantOK = false; + Symbol[] tparams1 = map(tparams); + covariantOK = true; + Type result1 = apply(result); + if (tparams1 != tparams) result1 = result1.subst(tparams, tparams1); + if (tparams1 == tparams && result1 == result) return tp; + else return PolyType(tparams1, result1); + case OverloadedType(Symbol[] alts, Type[] alttypes): + Type[] alttypes1 = map(alttypes); + if (alttypes1 == alttypes) return tp; + else return OverloadedType(alts, alttypes1); + case CovarType(Type t): + Type t1 = apply(t); + if (t1 == t) return tp; + else return covarType(t1); + case UnboxedArrayType(Type elemtp): + Type elemtp1 = apply(elemtp); + if (elemtp1 == elemtp) return tp; + else return UnboxedArrayType(elemtp1); + default: + throw new ApplicationError(tp); + } + } + + public Symbol map(Symbol sym) { + Type tp = sym.info(); + Type tp1 = apply(tp); + if (tp == tp1) return sym; + else return sym.cloneSymbol().setInfo(tp1); + } + + public Type[] map(Type[] tps) { + Type[] tps1 = tps; + for (int i = 0; i < tps.length; i++) { + Type tp = tps[i]; + Type tp1 = apply(tp); + if (tp1 != tp && tps1 == tps) { + tps1 = new Type[tps.length]; + System.arraycopy(tps, 0, tps1, 0, i); + } + tps1[i] = tp1; + } + return tps1; + } + + /** Apply map to all elements of this array of symbols, + * preserving recursive references to symbols in the array. + */ + public Symbol[] map(Symbol[] syms) { + Symbol[] syms1 = syms; + for (int i = 0; i < syms.length; i++) { + Symbol sym = syms[i]; + Symbol sym1 = map(sym); + if (sym != sym1 && syms1 == syms) { + syms1 = new Symbol[syms.length]; + System.arraycopy(syms, 0, syms1, 0, i); + } + syms1[i] = sym1; + } + if (syms1 != syms) { + for (int i = 0; i < syms1.length; i++) { + if (syms1[i] == syms[i]) + syms1[i] = syms[i].cloneSymbol(); + } + for (int i = 0; i < syms1.length; i++) { + syms1[i].setInfo(syms1[i].info().subst(syms, syms1)); + } + } + return syms1; + } + + /** Apply map to all elements of this array of this scope. + */ + public Scope map(Scope s) { + Symbol[] members = s.elements(); + Symbol[] members1 = map(members); + if (members == members1) return s; + else return new Scope(members1); + } + } + +// baseType and asSeenFrom -------------------------------------------------------- + + /** Return the base type of this type whose symbol is `clazz', or NoType, if + * such a type does not exist. + */ + public Type baseType(Symbol clazz) { + //System.out.println(this + ".baseType(" + clazz + ")");//DEBUG + switch (this) { + case ErrorType: + return ErrorType; + + case ThisType(_): + case SingleType(_, _): + return widen().baseType(clazz); + + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym == clazz) + return this; + else if (sym.kind == TYPE) + return pre.memberInfo(sym).baseType(clazz); + else if (sym.kind == ALIAS) + return pre.memberInfo(sym).baseType(clazz) + .subst(sym.typeParams(), args); + else if (clazz.isCompoundSym()) + return NoType; + else + return sym.baseType(clazz) + .asSeenFrom(pre, clazz.owner()) + .subst(sym.typeParams(), args); + + case CompoundType(Type[] parts, _): + for (int i = parts.length - 1; i >= 0; i--) { + Type result = parts[i].baseType(clazz); + if (result != NoType) return result; + } + break; + + case UnboxedArrayType(_): + if (clazz == Global.instance.definitions.ANY_CLASS) + return clazz.type(); + } + return NoType; + } + + /** Return overriding instance of `sym' in this type, + * or `sym' itself if none exists. + */ + public Symbol rebind(Symbol sym) { + Symbol sym1 = lookupNonPrivate(sym.name); + if (sym1.kind != NONE) { + //System.out.println("rebinding " + sym + " to " + sym1);//DEBUG + return sym1; + } + else return sym; + } + + /** A map to implement `asSeenFrom'. + */ + static class AsSeenFromMap extends Map { + + private Type pre; + private Symbol clazz; + private Type illegalType = NoType; + private boolean typeArg = false; + + AsSeenFromMap(Type pre, Symbol clazz) { + this.pre = pre; this.clazz = clazz; + } + + public Type apply(Type t) { + switch (t) { + case ThisType(Symbol sym): + return t.toPrefix(pre, clazz); + + case TypeRef(Type prefix, Symbol sym, Type[] args): + if (sym.kind == ALIAS) { + return apply(t.unalias()); + } else if (sym.owner().isPrimaryConstructor()) { + assert sym.kind == TYPE; + Type t1 = t.toInstance(pre, clazz); + //System.out.println(t + ".toInstance(" + pre + "," + clazz + ") = " + t1);//DEBUG + switch (t1) { + case CovarType(Type tp): + if (!covariantOK) { + if (illegalType == NoType) illegalType = t1; + } else if (!typeArg) { + t1 = tp; + } + } + return t1; + } else { + Type prefix1 = prefix.toPrefix(pre, clazz); + Symbol sym1 = (prefix1 == prefix || (sym.flags & MODUL) != 0) + ? sym : prefix1.rebind(sym); + boolean prevTypeArg = typeArg; + typeArg = true; + Type[] args1 = map(args); + typeArg = prevTypeArg; + if (prefix1 == prefix && args1 == args) return t; + else return TypeRef(prefix1, sym1, args1); + } + + case SingleType(Type prefix, Symbol sym): + try { + Type prefix1 = prefix.toPrefix(pre, clazz); + if (prefix1 == prefix) return t; + else return singleType(prefix1, prefix1.rebind(sym)); + } catch (Type.Error ex) { + return apply(t.widen()); + } + break; + + default: + return map(t); + } + } + + void checkLegal(Type tp) { + if (illegalType != NoType) { + throw new ApplicationError( + "malformed type: " + tp + "; " + + illegalType + " does not occur in covariant position"); + } + } + } + //where + Type toInstance(Type pre, Symbol clazz) { + if (pre == NoType || clazz.kind != CLASS) + return this; + Symbol sym = symbol(); + Symbol ownclass = sym.owner().constructorClass(); + if (ownclass.isSubClass(clazz) && + pre.symbol().isSubClass(ownclass)) { + switch (pre.baseType(ownclass)) { + case TypeRef(_, Symbol basesym, Type[] baseargs): + Symbol[] baseparams = basesym.typeParams(); + for (int i = 0; i < baseparams.length; i++) + if (sym == baseparams[i]) return baseargs[i];//??? + break; + case ErrorType: + return ErrorType; + } + throw new ApplicationError( + this + " cannot be instantiated from " + pre); + } else { + return toInstance( + pre.baseType(clazz).prefix(), clazz.owner()); + } + } + + Type toPrefix(Type pre, Symbol clazz) { + if (pre == NoType || clazz.kind != CLASS) + return this; + if (symbol().isSubClass(clazz) && + pre.symbol().isSubClass(symbol())) { + if (!pre.isStable()) { + throw new Type.Error ( + "malformed type: " + pre + "." + symbol().nameString()); + } + return pre; + } else { + return toPrefix( + pre.baseType(clazz).prefix(), clazz.owner()); + } + } + + /** This type Types as seen from prefix `pre' and class `clazz'. This means: + * Replace all this thistypes of `clazz' or one of its superclasses by `pre'. + * Proceed analogously for thistypes referring to outer classes. + */ + public Type asSeenFrom(Type pre, Symbol clazz) { + //System.out.println("computing asseenfrom of " + this + " with " + pre + "," + clazz);//DEBUG + AsSeenFromMap f = new AsSeenFromMap(pre, clazz); + Type t = f.apply(this); + f.checkLegal(t); + return t; + } + + /** Types `these' as seen from prefix `pre' and class `clazz'. + */ + public static Type[] asSeenFrom(Type[] these, Type pre, Symbol clazz) { + AsSeenFromMap f = new AsSeenFromMap(pre, clazz); + Type[] these1 = f.map(these); + for (int i = 0; i < these1.length; i++) + f.checkLegal(these1[i]); + return these1; + } + + /** The info of `sym', seen as a member of this type. + */ + public Type memberInfo(Symbol sym) { + return memberTransform(sym, sym.info()); + } + + /** The type of `sym', seen as a member of this type. + */ + public Type memberType(Symbol sym) { + return memberTransform(sym, sym.type()); + } + + private Type memberTransform(Symbol sym, Type tp) { + Type tp1 = tp.asSeenFrom(narrow(), sym.owner()); + Type tp2 = tp1.asSeenFrom(this, widen().symbol()); + //todo: sym.owner()? + //if (Global.instance.debug) System.out.println(this + "/" + widen() + ".memberType(" + sym + ":" + tp + ") = " + tp1 + "/" + tp2);//DEBUG + return tp2; + } + +// Substitutions --------------------------------------------------------------- + + /** A common map superclass for symbol/symbol and type/symbol substitutions. + */ + static abstract class SubstMap extends Map { + private Symbol[] from; + + SubstMap(Symbol[] from) { + this.from = from; + } + + /** Produce replacement type + * @param i The index in `from' of the symbol to be replaced. + * @param fromtp The type referring to this symbol. + */ + abstract Type replacement(int i, Type fromtp); + + /** Produce new substitution where some symbols are excluded. + * @param newfrom The new array of from symbols (without excluded syms) + * @param excluded The array of excluded sysmbols + */ + abstract SubstMap exclude(Symbol[] newfrom, Symbol[] excluded); + + public Type apply(Type t) { + switch (t) { + case TypeRef(ThisType(_), Symbol sym, Type[] args): + for (int i = 0; i < from.length; i++) { + if (sym == from[i]) return replacement(i, t); + } + break; + case SingleType(ThisType(_), Symbol sym): + for (int i = 0; i < from.length; i++) { + if (sym == from[i]) return replacement(i, t); + } + break; + case PolyType(Symbol[] tparams, Type result): + Symbol[] from1 = excludeSyms(from, tparams, from); + if (from1 != from) { + SubstMap f = exclude(from1, tparams); + Symbol[] tparams1 = f.map(tparams); + Type result1 = f.apply(result); + if (tparams1 != tparams) + result1 = result1.subst(tparams, tparams1); + if (tparams1 == tparams && result1 == result) return t; + else return PolyType(tparams1, result1); + } + } + return map(t); + } + //where + private boolean contains1(Symbol[] syms, Symbol sym) { + int i = 0; + while (i < syms.length && syms[i] != sym) i++; + return i < syms.length; + } + + private int nCommon(Symbol[] from, Symbol[] tparams) { + int cnt = 0; + for (int i = 0; i < from.length; i++) { + if (contains1(tparams, from[i])) cnt++; + } + return cnt; + } + + private Symbol[] excludeSyms(Symbol[] from, Symbol[] tparams, Symbol[] syms) { + int n = nCommon(from, tparams); + if (n == 0) { + return syms; + } else { + Symbol[] syms1 = new Symbol[syms.length - n]; + int j = 0; + for (int i = 0; i < from.length; i++) { + if (!contains1(tparams, from[i])) syms1[j++] = syms[i]; + } + return syms1; + } + } + + private Type[] excludeTypes(Symbol[] from, Symbol[] tparams, Type[] types) { + int n = nCommon(from, tparams); + if (n == 0) { + return types; + } else { + Type[] types1 = new Type[types.length - n]; + int j = 0; + for (int i = 0; i < from.length; i++) { + if (!contains1(tparams, from[i])) types1[j++] = types[i]; + } + return types1; + } + } + } + + /** A map for symbol/symbol substitutions + */ + static class SubstSymMap extends SubstMap { + Symbol[] to; + SubstSymMap(Symbol[] from, Symbol[] to) { + super(from); + this.to = to; + } + Type replacement(int i, Type fromtp) { + switch (fromtp) { + case TypeRef(Type pre, Symbol sym, Type[] args): + return TypeRef(pre, to[i], args); + case SingleType(Type pre, Symbol sym): + return singleType(pre, to[i]); + default: + throw new ApplicationError(); + } + } + SubstMap exclude(Symbol[] newfrom, Symbol[] excluded) { + return new SubstSymMap(newfrom, excludeSyms(from, excluded, to)); + } + } + + /** A map for type/symbol substitutions + */ + static class SubstTypeMap extends SubstMap { + Type[] to; + SubstTypeMap(Symbol[] from, Type[] to) { + super(from); + this.to = to; + } + Type replacement(int i, Type fromtp) { + return to[i]; + } + SubstMap exclude(Symbol[] newfrom, Symbol[] excluded) { + return new SubstTypeMap(newfrom, excludeTypes(from, excluded, to)); + } + } + + /** Substitute symbols `to' for occurrences of symbols `from' in this type. + */ + public Type subst(Symbol[] from, Symbol[] to) { + assert from.length == to.length + : this + ": " + from.length + " != " + to.length; + if (from.length != 0 && from != to) + return new SubstSymMap(from, to).apply(this); + else return this; + } + + /** Substitute symbols `to' for occurrences of symbols `from' in these types. + */ + public static Type[] subst(Type[] these, Symbol[] from, Symbol[] to) { + assert from.length == to.length; + if (these.length != 0 && from.length != 0 && from != to) + return new SubstSymMap(from, to).map(these); + else return these; + } + + /** Substitute types `to' for occurrences of symbols `from' in this type. + */ + public Type subst(Symbol[] from, Type[] to) { + assert from.length == to.length + : this + ": " + from.length + " != " + to.length; + if (from.length != 0) + return new SubstTypeMap(from, to).apply(this); + else return this; + } + + /** Substitute types `to' for occurrences of symbols `from' in these types. + */ + public static Type[] subst(Type[] these, Symbol[] from, Type[] to) { + assert from.length == to.length; + if (these.length != 0 && from.length != 0) + return new SubstTypeMap(from, to).map(these); + else return these; + } + + /** A map for substitutions of thistypes. + */ + static class SubstThisMap extends Map { + Symbol from; + Type to; + SubstThisMap(Symbol from, Type to) { + this.from = from; + this.to = to; + } + public Type apply(Type t) { + switch (t) { + case ThisType(Symbol sym): + if (sym == from) return to; + else return t; + default: + return map(t); + } + } + } + + public Type substThis(Symbol from, Type to) { + return new SubstThisMap(from, to).apply(this); + } + + public static Type[] substThis(Type[] these, Symbol from, Type to) { + return new SubstThisMap(from, to).map(these); + } + + static class ContainsMap extends Map { + boolean result = false; + Symbol sym; + ContainsMap(Symbol sym) { + this.sym = sym; + } + public Type apply(Type t) { + switch (t) { + case TypeRef(Type pre, Symbol sym1, Type[] args): + if (sym == sym1) result = true; + else { map(pre); map(args); } + break; + case SingleType(Type pre, Symbol sym1): + map(pre); + if (sym == sym1) result = true; + break; + default: + map(t); + } + return t; + } + } + + /** Does this type contain symbol `sym'? + */ + public boolean contains(Symbol sym) { + ContainsMap f = new ContainsMap(sym); + f.apply(this); + return f.result; + } + + /** Does this type contain any of the symbols `syms'? + */ + public boolean containsSome(Symbol[] syms) { + for (int i = 0; i < syms.length; i++) + if (contains(syms[i])) return false; + return true; + } + +// Comparisons ------------------------------------------------------------------ + + /** Is this type a subtype of that type? + */ + public boolean isSubType(Type that) { + if (debugSwitch) { + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(this + " < " + that + "?"); + indent++; + } + boolean result = isSubType0(that); + if (debugSwitch) { + indent--; + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(result); + } + return result; + } + + public boolean isSubType0(Type that) { + if (this == that) return true; + + switch (this) { + case ErrorType: + case AnyType: + return true; + } + + switch (that) { + case ErrorType: + case AnyType: + return true; + + case NoType: + return false; + + case ThisType(Symbol sym1): + switch (this) { + case ThisType(Symbol sym): + return sym == sym1; + case SingleType(Type pre, Symbol sym): + return sym.isModule() + && sym.moduleClass() == sym1 + && pre.isSameAs(sym1.owner().thisType()); + } + break; + + case SingleType(Type pre1, Symbol sym1): + switch (this) { + case SingleType(Type pre, Symbol sym): + return sym == sym1 && pre.isSameAs(pre1); + case ThisType(Symbol sym): + return sym1.isModule() + && sym == sym1.moduleClass() + && sym.owner().thisType().isSameAs(pre1); + } + break; + + case TypeRef(Type pre1, Symbol sym1, Type[] args1): + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym == sym1 && pre.isSameAs(pre1) && isSubArgs(args, args1)) + return true; + break; + } + if (sym1.kind == CLASS) { + Type base = this.baseType(sym1); + if (this != base && base.isSubType(that)) + return true; + } + break; + + case CompoundType(Type[] parts1, Scope members1): + int i = 0; + while (i < parts1.length && isSubType(parts1[i])) i++; + if (i == parts1.length && specializes(members1)) + return true; + break; + + case MethodType(Symbol[] ps1, Type res1): + switch (this) { + case MethodType(Symbol[] ps, Type res): + if (ps.length != ps1.length) return false; + for (int i = 0; i < ps.length; i++) { + Symbol p1 = ps1[i]; + Symbol p = ps[i]; + if (!p1.type().isSubType(p.type()) || + (p1.flags & Modifiers.DEF) != (p.flags & Modifiers.DEF)) + return false; + } + return res.isSubType(res1); + } + break; + + case PolyType(Symbol[] ps1, Type res1): + switch (this) { + case PolyType(Symbol[] ps, Type res): + if (ps.length != ps1.length) return false; + for (int i = 0; i < ps.length; i++) + if (!ps1[i].info().subst(ps1, ps).isSubType(ps[i].info())) + return false; + return res.isSubType(res1.subst(ps1, ps)); + } + break; + + case OverloadedType(Symbol[] alts1, Type[] alttypes1): + if (isSubSet(alttypes1, alternatives())) + return true; + break; + + case UnboxedType(int tag1): + switch (this) { + case UnboxedType(int tag): + return tag <= tag1 && tag1 <= DOUBLE && tag1 != CHAR; + } + break; + + case UnboxedArrayType(Type elemtp1): + switch (this) { + case UnboxedArrayType(Type elemtp): + return !(elemtp1 instanceof UnboxedType) && elemtp.isSubType(elemtp1); + } + break; + + case TypeVar(Type origin, Constraint constr): + //todo: should we test for equality with origin? + if (constr.inst != NoType) { + return this.isSubType(constr.inst); + } else if (!this.isCovarType()) { + constr.lobounds = new List(this, constr.lobounds); + return true; + } + break; + + default: + throw new ApplicationError(this + " <: " + that); + } + + switch (this) { + case NoType: + return false; + case ThisType(_): + case SingleType(_, _): + if (this.widen().isSubType(that)) return true; + break; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) { + return constr.inst.isSubType(that); + } else { + constr.hibounds = new List(that.dropVariance(), constr.hibounds); + return true; + } + + case TypeRef(_, Symbol sym, _): + if (sym.kind == ALIAS) return this.unalias().isSubType(that); + break; + + case OverloadedType(Symbol[] alts, Type[] alttypes): + for (int i = 0; i < alttypes.length; i++) { + if (alttypes[i].isSameAs0(that)) return true; + } + break; + } + + switch (that) { + case TypeRef(_, Symbol sym1, _): + if (sym1.kind == ALIAS) return this.isSubType(that.unalias()); + break; + } + + return false; + } + + /** Are types `these' subtypes of corresponding types `those'? + */ + public static boolean isSubType(Type[] these, Type[] those) { + if (these.length != those.length) return false; + for (int i = 0; i < these.length; i++) { + if (!these[i].isSubType(those[i])) return false; + } + return true; + } + + /** Are types `these' arguments types conforming to corresponding types `those'? + */ + static boolean isSubArgs(Type[] these, Type[] those) { + if (these.length != those.length) return false; + for (int i = 0; i < these.length; i++) { + switch (those[i]) { + case CovarType(Type tp1): + switch (these[i]) { + case CovarType(Type tp): + if (!tp.isSubType(tp1)) return false; + break; + default: + if (!these[i].isSubType(tp1)) return false; + } + break; + default: + if (these[i].isCovarType() || !these[i].isSameAs(those[i])) + return false; + } + } + return true; + } + + public static boolean isSubSet(Type[] alts, Type[] alts1) { + for (int i = 0; i < alts.length; i++) { + int j = 0; + while (j < alts1.length && !alts1[j].isSameAs(alts[i])) j++; + if (j == alts1.length) return false; + } + return true; + } + + /** Does this type implement all symbols in scope `s' with same or stronger types? + */ + public boolean specializes(Scope s) { + for (Scope.Entry e = s.elems; e != Scope.Entry.NONE; e = e.next) + if (!specializes(e.sym)) return false; + return true; + } + + /** Does this type implement symbol `sym1' with same or stronger type? + */ + public boolean specializes(Symbol sym1) { + if (debugSwitch) { + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(this + " specializes " + sym1 + "?"); + indent++; + } + boolean result = specializes0(sym1); + if (debugSwitch) { + indent--; + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(result); + } + return result; + } + + public boolean specializes0(Symbol sym1) { + Symbol sym = lookup(sym1.name); + Type self = narrow(); + return + sym == sym1 || + ((sym.kind == sym1.kind || sym1.kind == TYPE) && + self.memberInfo(sym).isSubType( + sym1.info().substThis(sym.owner(), self))) || + (sym.kind == TYPE && sym1.kind == ALIAS && + sym1.info().unalias().isSameAs(sym.type())); + } + + /** Is this type the same as that type? + */ + public boolean isSameAs(Type that) { + if (debugSwitch) { + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(this + " = " + that + "?"); + indent++; + } + boolean result = isSameAs0(that); + if (debugSwitch) { + indent--; + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(result); + } + return result; + } + + public boolean isSameAs0(Type that) { + if (this == that) return true; + + switch (this) { + case ErrorType: + case AnyType: + return true; + + case ThisType(Symbol sym): + switch (that) { + case ThisType(Symbol sym1): + return sym == sym1; + case SingleType(Type pre1, Symbol sym1): + return sym1.isModule() + && sym == sym1.moduleClass() + && sym.owner().thisType().isSameAs(pre1); + } + break; + + case SingleType(Type pre, Symbol sym): + switch (that) { + case SingleType(Type pre1, Symbol sym1): + return sym == sym1 && pre.isSameAs(pre1); + case ThisType(Symbol sym1): + return sym.isModule() + && sym.moduleClass() == sym1 + && pre.isSameAs(sym1.owner().thisType()); + } + break; + + case TypeRef(Type pre, Symbol sym, Type[] args): + switch (that) { + case TypeRef(Type pre1, Symbol sym1, Type[] args1): + if (sym == sym1 && pre.isSameAs(pre1) && isSameArgs(args, args1)) + return true; + } + break; + + case CompoundType(Type[] parts, Scope members): + switch (that) { + case CompoundType(Type[] parts1, Scope members1): + if (parts.length != parts1.length) return false; + for (int i = 0; i < parts.length; i++) + if (!parts[i].isSameAs(parts1[i])) return false; + return isSameAs(members, members1); + } + break; + + case MethodType(Symbol[] ps, Type res): + switch (that) { + case MethodType(Symbol[] ps1, Type res1): + if (ps.length != ps1.length) return false; + for (int i = 0; i < ps.length; i++) { + Symbol p1 = ps1[i]; + Symbol p = ps[i]; + if (!p1.type().isSameAs(p.type()) || + (p1.flags & Modifiers.DEF) != (p.flags & Modifiers.DEF)) + return false; + } + return res.isSameAs(res1); + } + break; + + case PolyType(Symbol[] ps, Type res): + switch (that) { + case PolyType(Symbol[] ps1, Type res1): + if (ps.length != ps1.length) return false; + for (int i = 0; i < ps.length; i++) + if (!ps1[i].info().subst(ps1, ps).isSameAs(ps[i].info())) + return false; + return res.isSameAs(res1.subst(ps1, ps)); + } + break; + + case OverloadedType(Symbol[] alts, Type[] alttypes): + switch (that) { + case OverloadedType(Symbol[] alts1, Type[] alttypes1): + return isSubSet(alttypes1, alttypes) + && isSubSet(alttypes, alttypes1); + } + break; + + case UnboxedType(int kind): + switch (that) { + case UnboxedType(int kind1): + return kind == kind1; + } + break; + + case UnboxedArrayType(Type elemtp): + switch (that) { + case UnboxedArrayType(Type elemtp1): + return elemtp.isSameAs(elemtp1); + } + break; + } + + switch (that) { + case ErrorType: + case AnyType: + return true; + case NoType: + return false; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) + return constr.inst.isSameAs(this); + else if (!this.isCovarType()) + return constr.instantiate(this.any2typevar()); + } + + switch (this) { + case NoType: + return false; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) + return constr.inst.isSameAs(that); + else if (!that.isCovarType()) + return constr.instantiate(that.any2typevar()); + break; + case TypeRef(_, Symbol sym, _): + if (sym.kind == ALIAS) return this.unalias().isSameAs(that); + } + + switch (that) { + case TypeRef(_, Symbol sym, _): + if (sym.kind == ALIAS) return this.isSameAs(that.unalias()); + } + + return false; + } + + /** Is this type argument that same as `that'? + */ + public boolean isSameArg(Type that) { + switch (this) { + case CovarType(Type tp): + switch (that) { + case CovarType(Type tp1): + return tp.isSameAs(tp1); + } + } + return this.isSameAs(that); + } + + /** Are types `these' the same as corresponding types `those'? + */ + public static boolean isSameAs(Type[] these, Type[] those) { + if (these.length != those.length) return false; + for (int i = 0; i < these.length; i++) { + if (!these[i].isSameAs(those[i])) return false; + } + return true; + } + + /** Are type arguments `these' the same as corresponding types `those'? + */ + public static boolean isSameArgs(Type[] these, Type[] those) { + if (these.length != those.length) return false; + for (int i = 0; i < these.length; i++) { + if (!these[i].isSameArg(those[i])) return false; + } + return true; + } + + /** Do scopes `s1' and `s2' define he same symbols with the same kinds and infos? + */ + public boolean isSameAs(Scope s1, Scope s2) { + return isSubScope(s1, s2) && isSubScope(s2, s1); + } + + /** Does scope `s1' define all symbols of scope `s2' with the same kinds and infos? + */ + private boolean isSubScope(Scope s1, Scope s2) { + for (Scope.Entry e = s2.elems; e != Scope.Entry.NONE; e = e.next) { + Symbol sym2 = e.sym; + Symbol sym1 = s1.lookup(sym2.name); + if (sym1.kind != sym2.kind || + !sym1.info().isSameAs( + sym2.info().substThis(sym2.owner(), sym1.owner().thisType()))) + return false; + } + return true; + } + + boolean isSameAsAll(Type[] tps) { + int i = 1; + while (i < tps.length && isSameAs(tps[i])) i++; + return i == tps.length; + } + + /** Map every occurrence of AnyType to a fresh type variable. + */ + public static Map any2typevarMap = new Map() { + public Type apply(Type t) { return t.any2typevar(); } + }; + + public Type any2typevar() { + switch (this) { + case AnyType: + return TypeVar(this, new Constraint()); + default: + return any2typevarMap.map(this); + } + } + +// Closures and Least Upper Bounds --------------------------------------------------- + + /** The closure of this type, i.e. the widened type itself followed by all + * its direct and indirect (pre-) base types, sorted by Symbol.isLess(). + * Note that (pre-) base types do _not_ carry type parameters; these + * are added by baseType(). + */ + public Type[] closure() { + switch (this.widen().unalias()) { + case TypeRef(Type pre, Symbol sym, Type[] args): + return subst( + asSeenFrom(sym.closure(), pre, sym.owner()), + sym.typeParams(), args); + + case CompoundType(Type[] parts, Scope members): + Type[][] closures = new Type[parts.length][]; + for (int i = 0; i < parts.length; i++) + closures[i] = parts[i].closure(); + return union(closures); + + default: + return new Type[]{this}; + } + } + + /** return union of array of closures + */ + static private Type[] union(Type[][] closures) { + if (closures.length == 1) return closures[0]; // fast special case + int[] index = new int[closures.length]; + int totalsize = 0; + for (int i = 0; i < index.length; i++) { + index[i] = 0; + totalsize = totalsize + closures[i].length; + } + Type[] res = new Type[totalsize]; + int j = 0; + + while (true) { + // find minimal element + Type min = null; + for (int i = 0; i < index.length; i++) { + if (index[i] < closures[i].length) { + Type cltype = closures[i][index[i]]; + if (min == null || + cltype.symbol().isLess(min.symbol()) || + cltype.symbol() == min.symbol() && cltype.isSubType(min)) { + min = cltype; + } + } + } + if (min == null) break; + + res[j] = min; + j = j + 1; + + // bump all indices that start with minimal element + for (int i = 0; i < index.length; i++) { + if (index[i] < closures[i].length && + closures[i][index[i]].symbol() == min.symbol()) + index[i] = index[i] + 1; + } + } + Type[] result = new Type[j]; + System.arraycopy(res, 0, result, 0, j); + return result; + } + + /** return intersection of array of closures + */ + static private Type[] intersection(Type[][] closures) { + if (closures.length == 1) return closures[0]; // fast special case + int[] index = new int[closures.length]; + Type[] mintypes = new Type[closures.length]; + int minsize = Integer.MAX_VALUE; + for (int i = 0; i < index.length; i++) { + index[i] = 0; + if (closures[i].length < minsize) minsize = closures[i].length; + } + Type[] res = new Type[minsize]; + int j = 0; + + L: + while (true) { + // find minimal element + Symbol min = null; + for (int i = 0; i < index.length; i++) { + if (index[i] == closures[i].length) break L; + Symbol clsym = closures[i][index[i]].symbol(); + if (min == null || clsym.isLess(min)) min = clsym; + } + + boolean agree = true; + // bump all indices that start with minimal element + for (int i = 0; i < index.length; i++) { + Type cltype = closures[i][index[i]]; + if (cltype.symbol() == min) { + mintypes[i] = cltype; + index[i] = index[i] + 1; + } else { + agree = false; + } + } + if (agree) { + Type mintype; + mintype = commonType(mintypes); + if (mintype == NoType) + mintype = arglub(mintypes); + if (mintype.symbol().kind == CLASS) { + res[j] = mintype; + j = j + 1; + } + } + } + Type[] result = new Type[j]; + System.arraycopy(res, 0, result, 0, j); + return result; + } + + //todo: catch lubs not within bounds. + static Type arglub(Type[] types) { + Type pre = types[0].prefix(); + Symbol sym = types[0].symbol(); + Type[] args = new Type[sym.typeParams().length]; + Type[][] argss = new Type[args.length][types.length]; + for (int i = 0; i < types.length; i++) { + switch (types[i]) { + case TypeRef(Type pre1, Symbol sym1, Type[] args1): + assert sym == sym1; + assert args1.length == args.length; + if (!pre.isSameAs(pre1)) return NoType; + for (int j = 0; j < args1.length; j++) + argss[j][i] = args1[j]; + case ErrorType: + return ErrorType; + default: + assert false : types[i]; + } + } + for (int j = 0; j < args.length; j++) { + args[j] = commonType(argss[j]); + if (args[j] == NoType) + args[j] = CovarType(lub(argss[j])); + } + return TypeRef(pre, sym, args); + } + + /** The frontier of a closure C is the minimal set of types such that + * the union of the closures of these types equals C. + */ + static private Type[] frontier(Type[] closure) { + Type[] front = new Type[closure.length]; + int j = 0; + for (int i = 0; i < closure.length; i++) { + int k = 0; + Type tp = closure[i]; + while (k < j && !front[k].symbol().isSubClass(tp.symbol())) + k++; + if (k == j) { + front[j] = tp; + j++; + } + } + Type[] result = new Type[j]; + System.arraycopy(front, 0, result, 0, j); + return result; + } + + /** Return the least upper bound of non-empty array of types `tps'. + * todo: do we need to consider refinements as well? + */ + public static Type lub(Type[] tps) { + //System.out.println("lub" + ArrayApply.toString(tps));//DEBUG + Type lubType = commonType(tps); + if (lubType != NoType) return lubType; + Type[][] closures = new Type[tps.length][]; + for (int i = 0; i < tps.length; i++) { + if (!tps[i].isObjectType()) return Type.NoType;//todo: change + closures[i] = tps[i].closure(); + } + Type[] allBaseTypes = intersection(closures); + Type[] leastBaseTypes = frontier(allBaseTypes); + if (leastBaseTypes.length == 0) return Type.NoType; + Scope members = new Scope(); + lubType = compoundType(leastBaseTypes, members); + Type lubThisType = lubType.narrow(); + //System.out.println("lubtype = " + lubType);//DEBUG + + Symbol[] rsyms = new Symbol[tps.length]; + Type[] rtps = new Type[tps.length]; + for (int i = 0; i < allBaseTypes.length; i++) { + for (Scope.Entry e = allBaseTypes[i].members().elems; + e != Scope.Entry.NONE; + e = e.next) { + Name name = e.sym.name; + if ((e.sym.flags & PRIVATE) == 0 && lubType.lookup(name) == e.sym) { + Type symType = lubThisType.memberInfo(e.sym); + int j = 0; + while (j < tps.length) { + rsyms[j] = tps[j].lookupNonPrivate(name); + if (rsyms[j] == e.sym) break; + rtps[j] = tps[j].memberType(rsyms[j]) + .substThis(tps[j].symbol(), lubThisType); + if (rtps[j].isSameAs(symType)) break; + j++; + } + if (j == tps.length) { + Symbol lubSym = lub(rsyms, rtps, lubType.symbol()); + if (lubSym.kind != NONE && !lubSym.info().isSameAs(symType)) + members.enter(lubSym); + } + } + } + } + //System.out.print("lub "); System.out.print(ArrayApply.toString(tps)); System.out.println(" = " + lubType);//DEBUG + if (leastBaseTypes.length == 1 && members.elems == Scope.Entry.NONE) + return leastBaseTypes[0]; + else return lubType; + } + + private static Type commonType(Type[] tps) { + Type tp = tps[0]; + if (tp.isSameAsAll(tps)) return tp; + tp = tp.widen(); + if (tp.isSameAsAll(widen(tps))) return tp; + return NoType; + } + + private static Symbol lub(Symbol[] syms, Type[] tps, Symbol owner) { + //System.out.println("lub" + ArrayApply.toString(syms));//DEBUG + int lubKind = syms[0].kind; + for (int i = 1; i < syms.length; i++) { + Symbol sym = syms[i]; + if (sym.kind == ERROR) return Symbol.NONE; + if (sym.isType() && sym.kind != lubKind) lubKind = TYPE; + } + if (lubKind == syms[0].kind && tps[0].isSameAsAll(tps)) { + return syms[0].cloneSymbol(); + } + Type lubType = lub(tps); + if (lubType == Type.NoType) return Symbol.NONE; + Symbol lubSym; + switch (lubKind) { + case VAL: + lubSym = new TermSymbol(syms[0].pos, syms[0].name, owner, 0); + break; + case TYPE: case ALIAS: case CLASS: + lubSym = new TypeSymbol(TYPE, syms[0].pos, syms[0].name, owner, 0); + break; + default: + throw new ApplicationError(); + } + lubSym.setInfo(lubType); + return lubSym; + } + +// Erasure -------------------------------------------------------------------------- + + public static Map erasureMap = new Map() { + public Type apply(Type t) { return t.erasure(); } + }; + + private static final Type[] unboxedType = + new Type[LastUnboxedTag + 1 - FirstUnboxedTag]; + private static final Type[] unboxedArrayType = + new Type[LastUnboxedTag + 1 - FirstUnboxedTag]; + private static final Name[] unboxedName = + new Name[LastUnboxedTag + 1 - FirstUnboxedTag]; + private static final Name[] boxedName = + new Name[LastUnboxedTag + 1 - FirstUnboxedTag]; + private static final Name[] boxedFullName = + new Name[LastUnboxedTag + 1 - FirstUnboxedTag]; + + private static void mkStdClassType(int kind, String unboxedstr, String boxedstr) { + unboxedType[kind - FirstUnboxedTag] = UnboxedType(kind); + unboxedArrayType[kind - FirstUnboxedTag] = UnboxedArrayType(unboxedType(kind)); + unboxedName[kind - FirstUnboxedTag] = Name.fromString(unboxedstr); + boxedName[kind - FirstUnboxedTag] = Name.fromString(boxedstr); + boxedFullName[kind - FirstUnboxedTag] = Name.fromString("scala." + boxedstr); + } + + static { + mkStdClassType(BYTE, "byte", "Byte"); + mkStdClassType(SHORT, "short", "Short"); + mkStdClassType(CHAR, "char", "Char"); + mkStdClassType(INT, "int", "Int"); + mkStdClassType(LONG, "long", "Long"); + mkStdClassType(FLOAT, "float", "Float"); + mkStdClassType(DOUBLE, "double", "Double"); + mkStdClassType(BOOLEAN, "boolean", "Boolean"); + mkStdClassType(UNIT, "void", "Unit"); + } + + /** Return unboxed type of given kind. + */ + public static Type unboxedType(int kind) { + return unboxedType[kind - FirstUnboxedTag]; + } + + /** Return unboxed array type of given element kind. + */ + public static Type unboxedArrayType(int kind) { + return unboxedArrayType[kind - FirstUnboxedTag]; + } + + /** Return the name of unboxed type of given kind. + */ + public static Name unboxedName(int kind) { + return unboxedName[kind - FirstUnboxedTag]; + } + + /** Return the name of boxed type of given kind. + */ + public static Name boxedName(int kind) { + return boxedName[kind - FirstUnboxedTag]; + } + + /** Return the full name of boxed type of given kind. + */ + public static Name boxedFullName(int kind) { + return boxedFullName[kind - FirstUnboxedTag]; + } + + /** If type is boxed, return its unboxed equivalent; otherwise return the type + * itself. + */ + public Type unbox() { + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if ((sym.flags & MODUL) == 0) { + Name fullname = sym.fullName(); + if (fullname == Names.scala_Array && args.length == 1 + /*&& args[0].unalias().symbol().kind != TYPE Q: why needed?*/) { + return UnboxedArrayType(args[0].erasure()); + } else { + for (int i = 0; i < boxedFullName.length; i++) { + if (boxedFullName[i] == fullname) return unboxedType[i]; + } + } + } + } + return this; + } + + /** Return the erasure of this type. + */ + public Type erasure() { + switch (this) { + case ThisType(_): + case SingleType(_, _): + return widen().erasure(); + case TypeRef(Type pre, Symbol sym, Type[] args): + switch (sym.kind) { + case ALIAS: case TYPE: + return pre.memberInfo(sym).erasure(); + + case CLASS: + if (sym.fullName() == Names.java_lang_Object || + sym.fullName() == Names.scala_AnyRef || + sym.fullName() == Names.scala_AnyVal) + return Global.instance.definitions.ANY_TYPE; + else { + Type this1 = unbox(); + if (this1 != this) return this1; + else if (args.length == 0) return this; + else return TypeRef(pre, sym, Type.EMPTY_ARRAY); + } + + default: throw new ApplicationError(); + } + case CompoundType(Type[] parents, _): + if (parents.length > 0) return parents[0].erasure(); + else return this; + case CovarType(Type tp): + return tp.erasure(); // note: needed because of UnboxedArrayType + case MethodType(Symbol[] params, Type tp): + Symbol[] params1 = erasureMap.map(params); + Type tp1 = tp.erasure(); + switch (tp1) { + case MethodType(Symbol[] params2, Type tp2): + Symbol[] newparams = new Symbol[params1.length + params2.length]; + System.arraycopy(params1, 0, newparams, 0, params1.length); + System.arraycopy(params2, 0, newparams, params1.length, params2.length); + return MethodType(newparams, tp2); + default: + if (params1 == params && tp1 == tp) return this; + else return MethodType(params1, tp1); + } + case PolyType(_, Type result): + return result.erasure(); + default: + return erasureMap.map(this); + } + } + +// Object Interface ----------------------------------------------------------------- + + public String toString() { + switch (this) { + case ErrorType: + return "<error>"; + case AnyType: + return "<any type>"; + case NoType: + return "<notype>"; + case ThisType(Symbol sym): + if (sym.isRoot()) return "<root>.this.type"; + else if (this == localThisType) return "<local>.this.type"; + else { + Type this1 = (Global.instance.debug) ? this : expandModuleThis(); + if (this1 == this) return sym.nameString() + ".this.type"; + else return this1.toString(); + } + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.isRoot()) return "<root>"; + if (!Global.instance.debug) { + if (isTupleType()) + return ArrayApply.toString( + dropVarianceMap.map(args), "[", ",", "]"); + if (isFunctionType()) { + Type[] params = new Type[args.length - 1]; + System.arraycopy(args, 0, params, 0, params.length); + return ArrayApply.toString(params, "(", ",", ") => ") + + args[params.length].dropVariance(); + } + } + Type pre1 = (Global.instance.debug) ? pre : pre.expandModuleThis(); + String result = pre1.prefixString() + sym.nameString() + sym.idString(); + if (args.length != 0) + result = result + ArrayApply.toString(args, "[", ",", "]"); + return result; + case SingleType(Type pre, Symbol sym): + if ((sym.flags & SYNTHETIC) != 0 && !Global.instance.debug) + return widen().toString(); + if (sym.isRoot()) return "<root>.type"; + Type pre1 = (Global.instance.debug) ? pre : pre.expandModuleThis(); + return pre1.prefixString() + sym.nameString() + sym.idString() + ".type"; + case CompoundType(Type[] parts, Scope members): + validate();//debug + StringBuffer buf = new StringBuffer(); + if (parts.length > 0) { + buf.append(parts[0].toString()); + int i = 1; + while (i < parts.length) { + buf.append(" with "); + buf.append(parts[i].toString()); + i++; + } + } + boolean first = true; + for (Scope.SymbolIterator it = members.iterator(); it.hasNext(); ) { + Symbol sym = it.next(); + buf.append(first ? "{" : ", "); + first = false; + buf.append(sym.defString()); + } + if (!first) buf.append("}"); + return buf.toString(); + case MethodType(Symbol[] vparams, Type result): + return ArrayApply.toString(Symbol.type(vparams), "(", ",", ")") + result; + case PolyType(Symbol[] tparams, Type result): + return ArrayApply.toString(Symbol.defString(tparams), "[", ",", "]") + + result; + case CovarType(Type tp): + return "+" + tp; + case OverloadedType(Symbol[] alts, Type[] alttypes): + return "<overloaded> " + ArrayApply.toString(alttypes, "", " <and> ", "");//debug + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) return constr.inst.toString(); + else return origin + "?"; + case UnboxedType(int kind): + return unboxedName(kind).toString(); + case UnboxedArrayType(Type elemtp): + return elemtp.toString() + "[]"; + case LazyType(): + return "<lazy type " + getClass() + ">"; + default: + return "<unknown type " + getClass() + ">"; + } + } + + public String toLongString() { + String str = toString(); + if (str.endsWith(".type")) return str + " (with underlying type " + widen() + ")"; + else return str; + } + + private String prefixString() { + if ((this == localThisType || symbol().isRoot()) && !Global.instance.debug) { + return ""; + } else { + String spre = toString(); + if (spre.length() == 0) + return ""; + else if (spre.endsWith(".type")) + return spre.substring(0, spre.length() - 4); + else + return spre + "@"; + } + } + + public int hashCode() { + switch (this) { + case ErrorType: + return ERROR; + case NoType: + return NOtpe; + case ThisType(Symbol sym): + return THIStpe + ^ (sym.hashCode() * 41); + case TypeRef(Type pre, Symbol sym, Type[] args): + return NAMEDtpe + ^ (pre.hashCode() * 41) + ^ (sym.hashCode() * (41*41)) + ^ (hashCode(args) * (41*41*41)); + case SingleType(Type pre, Symbol sym): + return SINGLEtpe + ^ (pre.hashCode() * 41) + ^ (sym.hashCode() * (41*41)); + case CompoundType(Type[] parts, Scope members): + return COMPOUNDtpe + ^ (hashCode(parts) * 41) + ^ (members.hashCode() * (41 * 41)); + case MethodType(Symbol[] vparams, Type result): + return METHODtpe + ^ (hashCode(Symbol.type(vparams)) * 41) + ^ (result.hashCode() * (41 * 41)); + case PolyType(Symbol[] tparams, Type result): + return POLYtpe + ^ (hashCode(tparams) * 41) + ^ (result.hashCode() * (41 * 41)); + case CovarType(Type tp): + return COVARtpe + ^ (tp.hashCode() * (41)); + case OverloadedType(Symbol[] alts, Type[] alttypes): + return OVERLOADEDtpe + ^ (hashCode(alts) * 41) + ^ (hashCode(alttypes) * (41 * 41)); + case UnboxedType(int kind): + return UNBOXEDtpe + ^ (kind * 41); + case UnboxedArrayType(Type elemtp): + return UNBOXEDARRAYtpe + ^ (elemtp.hashCode() * 41); + default: + throw new ApplicationError(); + } + } + + public static int hashCode(Object[] elems) { + int h = 0; + for (int i = 0; i < elems.length; i++) + h = h * 41 + elems[i].hashCode(); + return h; + } + + public static int hashCode(Scope.Entry elems) { + int h = 0; + for (Scope.Entry e = elems; e != Scope.Entry.NONE; e = e.next) + h = h * 41 + + e.sym.kind + + e.sym.name.hashCode() + + e.sym.info().hashCode(); + return h; + } + + // todo: change in relation to needs. + + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof Type) { + Type that = (Type) other; + switch (this) { + case ErrorType: + return that == ErrorType; + case NoType: + return that == NoType; + case ThisType(Symbol sym): + switch (that) { + case ThisType(Symbol sym1): + return sym == sym1; + default: return false; + } + case TypeRef(Type pre, Symbol sym, Type[] args): + switch (that) { + case TypeRef(Type pre1, Symbol sym1, Type[] args1): + return pre.equals(pre1) && sym == sym1 && equals(args, args1); + default: return false; + } + case SingleType(Type pre, Symbol sym): + switch (that) { + case SingleType(Type pre1, Symbol sym1): + return pre.equals(pre1) && sym == sym1; + default: return false; + } + case CompoundType(Type[] parts, Scope members): + switch (that) { + case CompoundType(Type[] parts1, Scope members1): + return parts.equals(parts1) && members.equals(members1); + default: return false; + } + case MethodType(Symbol[] vparams, Type result): + switch (that) { + case MethodType(Symbol[] vparams1, Type result1): + return equals(Symbol.type(vparams), Symbol.type(vparams1)) && + result.equals(result1); + default: return false; + } + case PolyType(Symbol[] tparams, Type result): + switch (that) { + case PolyType(Symbol[] tparams1, Type result1): + return equals(tparams, tparams1) && result.equals(result1); + default: return false; + } + case CovarType(Type tp): + switch (that) { + case CovarType(Type tp1): + return tp.equals(tp1); + default: return false; + } + case OverloadedType(Symbol[] alts, Type[] alttypes): + switch (that) { + case OverloadedType(Symbol[] alts1, Type[] alttypes1): + return equals(alts, alts1) && equals(alttypes, alttypes1); + default: return false; + } + case UnboxedType(int kind): + switch (that) { + case UnboxedType(int kind1): + return kind == kind1; + default: return false; + } + case UnboxedArrayType(Type elemtp): + switch (that) { + case UnboxedArrayType(Type elemtp1): + return elemtp.equals(elemtp1); + default: return false; + } + default: + } + } + return false; + } + + public static boolean equals(Object[] elems1, Object[] elems2) { + if (elems1.length != elems2.length) return false; + for (int i = 0; i < elems1.length; i++) { + if (!elems1[i].equals(elems2[i])) return false; + } + return true; + } + +// Type.List class ----------------------------------------------------------------- + + /** A class for lists of types. + */ + public static class List { + public Type head; + public List tail; + public List(Type head, List tail) { + this.head = head; this.tail = tail; + } + public int length() { + return (this == EMPTY) ? 0 : 1 + tail.length(); + } + public Type[] toArray() { + Type[] ts = new Type[length()]; + copyToArray(ts, 0, 1); + return ts; + } + public void copyToArray(Type[] ts, int start, int delta) { + if (this != EMPTY) { + ts[start] = head; + tail.copyToArray(ts, start+delta, delta); + } + } + public Type[] toArrayReverse() { + Type[] ts = new Type[length()]; + copyToArray(ts, ts.length - 1, -1); + return ts; + } + + public static List EMPTY = new List(null, null); + + public static List append(List l, Type tp) { + return (l == EMPTY) ? new List(tp, EMPTY) + : new List(l.head, append(l.tail, tp)); + } + } + +// Type.Constraint class ------------------------------------------------------- + + /** A class for keeping sub/supertype constraints and instantiations + * of type variables. + */ + public static class Constraint { + public List lobounds = List.EMPTY; + public List hibounds = List.EMPTY; + public Type inst = NoType; + + public boolean instantiate(Type tp) { + for (List l = lobounds; l != List.EMPTY; l = l.tail) { + if (!l.head.isSubType(tp)) return false; + } + for (List l = hibounds; l != List.EMPTY; l = l.tail) { + if (!tp.isSubType(l.head)) return false; + } + inst = tp; + return true; + } + } + +// Type.Error class -------------------------------------------------------------- + + /** A class for throwing type errors + */ + public static class Error extends java.lang.Error { + public String msg; + public Error(String msg) { + super(msg); + this.msg = msg; + } + } + + /** A class for throwing type errors + */ + public static class VarianceError extends Error { + public VarianceError(String msg) { + super(msg); + } + } +} + +/* A standard pattern match: + + case ErrorType: + case AnyType: + case NoType: + case ThisType(Symbol sym): + case TypeRef(Type pre, Symbol sym, Type[] args): + case SingleType(Type pre, Symbol sym): + case CompoundType(Type[] parts, Scope members): + case MethodType(Symbol[] vparams, Type result): + case PolyType(Symbol[] tparams, Type result): + case OverloadedType(Symbol[] alts, Type[] alttypes): + case CovarType(Type tp): +*/ + diff --git a/sources/scalac/symtab/TypeTags.java b/sources/scalac/symtab/TypeTags.java new file mode 100644 index 0000000000..a3bd103432 --- /dev/null +++ b/sources/scalac/symtab/TypeTags.java @@ -0,0 +1,47 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +public interface TypeTags { + + /** unboxed type tags + */ + int BYTE = 10; + int CHAR = 11; + int SHORT = 12; + int INT = 13; + int LONG = 14; + int FLOAT = 15; + int DOUBLE = 16; + int BOOLEAN = 17; + int UNIT = 18; + int STRING = 19; + + int FirstUnboxedTag = BYTE; + int LastUnboxedTag = UNIT; + + /** other type tags (used for hashcodes and Pickling) + */ + int ERRORtpe = 20; + int NOtpe = 21; + int THIStpe = 22; + int NAMEDtpe = 23; + int SINGLEtpe = 24; + int COMPOUNDtpe = 25; + int METHODtpe = 26; + int POLYtpe = 27; + int CONSTRUCTORtpe = 28; + int COVARtpe = 29; + int OVERLOADEDtpe = 30; + int UNBOXEDtpe = 31; + int UNBOXEDARRAYtpe = 32; + + int firstTypeTag = ERRORtpe; + int lastTypeTag = UNBOXEDARRAYtpe; +} diff --git a/sources/scalac/symtab/classfile/AttributeParser.java b/sources/scalac/symtab/classfile/AttributeParser.java new file mode 100644 index 0000000000..0ed09b44d0 --- /dev/null +++ b/sources/scalac/symtab/classfile/AttributeParser.java @@ -0,0 +1,439 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import java.util.*; + +public class AttributeParser implements ClassfileConstants { + + /** the classfile input buffer + */ + protected AbstractFileReader in; + + /** the constant pool + */ + protected ConstantPool pool; + + protected ClassfileParser parser; + + /** constructor + */ + public AttributeParser(AbstractFileReader in, ConstantPool pool, ClassfileParser parser) { + this.in = in; + this.pool = pool; + this.parser = parser; + } + + public int nameToId(Name name) { + if (name == SOURCEFILE_N) + return SOURCEFILE_ATTR; + if (name == SYNTHETIC_N) + return SYNTHETIC_ATTR; + if (name == DEPRECATED_N) + return DEPRECATED_ATTR; + if (name == CODE_N) + return CODE_ATTR; + if (name == EXCEPTIONS_N) + return EXCEPTIONS_ATTR; + if (name == CONSTANT_VALUE_N) + return CONSTANT_VALUE_ATTR; + if (name == LINE_NUM_TABLE_N) + return LINE_NUM_TABLE_ATTR; + if (name == LOCAL_VAR_TABLE_N) + return LOCAL_VAR_TABLE_ATTR; + if (name == INNERCLASSES_N) + return INNERCLASSES_ATTR; + if (name == META_N) + return META_ATTR; + if (name == SCALA_N) + return SCALA_ATTR; + return BAD_ATTR; + } + + public Symbol readAttributes(Symbol def, Type type, int attrs) { + char nattr = in.nextChar(); + for (int i = 0; i < nattr; i++) { + Name attrName = (Name)pool.readPool(in.nextChar()); + int attr = nameToId(attrName); + int attrLen = in.nextInt(); + if ((attrs & attr) == 0) { + //System.out.println("# skipping " + attrName + " of " + def); + in.skip(attrLen); + } else { + //System.out.println("# reading " + attrName + " of " + def); + readAttribute(def, type, attr, attrLen); + } + } + return def; + } + + public void readAttribute(Symbol sym, Type type, int attr, int attrLen) { + switch (attr) { + // class attributes + case SCALA_ATTR: + in.skip(attrLen); + /* not yet + Name sourcefile = (Name)pool.readPool(in.nextChar()); + new UnPickle( + (JavaClassSymbol) sym, in.nextBytes(attrLen - 2), sourcefile); + */ + return; + + case SOURCEFILE_ATTR: + // ((ClassDef)def).sourcefile = (Name)reader.readPool(in.nextChar()); + in.skip(attrLen); + return; + + case INNERCLASSES_ATTR: + /* int n = in.nextChar(); + for (int i = 0; i < n; i++) { + Symbol inner = (Symbol)pool.readPool(in.nextChar()); + Symbol outer = (Symbol)pool.readPool(in.nextChar()); + Name name = (Name)pool.readPool(in.nextChar()); + int flags = in.nextChar(); + if (name != null) { + inner.owner(outer); + inner.mangled(name); + inner.flags = flags; + } + } */ + in.skip(attrLen); + return; + + // method attributes + case CODE_ATTR: + in.skip(attrLen); + return; + + case EXCEPTIONS_ATTR: + //int nexceptions = in.nextChar(); + //Type[] thrown = new Type[nexceptions]; + //for (int j = 0; j < nexceptions; j++) + // thrown[j] = make.classType(reader.readClassName(in.nextChar())); + //((MethodType)def.type).thrown = thrown; + in.skip(attrLen); + return; + + case LINE_NUM_TABLE_ATTR: + in.skip(attrLen); + return; + + case LOCAL_VAR_TABLE_ATTR: + in.skip(attrLen); + return; + + // general attributes + case SYNTHETIC_ATTR: + sym.flags |= Modifiers.SYNTHETIC; + return; + + case DEPRECATED_ATTR: + sym.flags |= Modifiers.DEPRECATED; + return; + + case CONSTANT_VALUE_ATTR: + // Type ctype = (Type)reader.readPool(in.nextChar()); + // def.type = types.coerce(ctype, def.type); + in.skip(attrLen); + return; + + case META_ATTR: + //System.out.println("parsing meta data for " + sym); + String meta = pool.readPool(in.nextChar()).toString().trim(); + sym.setInfo(new MetaParser(meta, tvars, sym, type).parse(), parser.phaseId); + + return; + } + throw new RuntimeException("unknown classfile attribute"); + } + + Scope tvars = new Scope(); + + class MetaParser { + + Symbol owner; + StringTokenizer scanner; + Type defaultType; + String token; + Scope tvars; + Scope locals; + + MetaParser(String meta, Scope tvars, Symbol owner, Type defaultType) { + //System.out.println("meta = " + meta); + this.scanner = new StringTokenizer(meta, "()[], \t<;", true); + this.defaultType = defaultType; + this.owner = owner; + this.tvars = tvars; + } + + private Symbol getTVar(String name) { + return getTVar(name, parser.c.constructor()); + } + + private Symbol getTVar(String name, Symbol owner) { + if (name.startsWith("?")) { + if (locals != null) { + Symbol s = locals.lookup(Name.fromString(name).toTypeName()); + if (s != Symbol.NONE) + return s; + } + Symbol s = tvars.lookup(Name.fromString(name).toTypeName()); + if (s == Symbol.NONE) { + s = new TypeSymbol(Kinds.TYPE, + Position.NOPOS, + Name.fromString(token).toTypeName(), + owner, + Modifiers.PARAM); + s.setInfo(parser.defs.ANY_TYPE, parser.phaseId); + tvars.enter(s); + } + return s; + } else + return Symbol.NONE; + } + + private String nextToken() { + do { + token = scanner.nextToken().trim(); + } while (token.length() == 0); + return token; + } + + protected Type parse() { + if (scanner.hasMoreTokens()) { + nextToken(); + if (!scanner.hasMoreTokens()) + return defaultType; + if ("class".equals(token)) + return parseMetaClass(); + if ("method".equals(token)) + return parseMetaMethod(); + if ("field".equals(token)) + return parseMetaField(); + if ("constr".equals(token)) + return parseConstrField(); + } + return defaultType; + } + + protected Type parseMetaClass() { + nextToken(); + //System.out.println("parse meta class " + token);//DEBUG + if ("[".equals(token)) { + try { + Vector syms = new Vector(); + do { + nextToken(); + assert token.startsWith("?"); + Symbol s = getTVar(token); + if (s == Symbol.NONE) + return defaultType; + nextToken(); + //System.out.println("new var " + s + ", " + token);//DEBUG + if (token.equals("<")) { + nextToken(); + s.setInfo(parseType(), parser.phaseId); + } + syms.add(s); + } while (token.equals(",")); + assert "]".equals(token); + nextToken(); + Symbol[] smbls = (Symbol[])syms.toArray(new Symbol[syms.size()]); + //System.out.println("*** " + syms);//DEBUG + Type constrtype = Type.appliedType( + parser.ctype, Symbol.type(smbls)); + + if ((parser.c.flags & Modifiers.INTERFACE) != 0) { + parser.c.constructor().setInfo( + Type.PolyType(smbls, constrtype), parser.phaseId); + //System.out.println("info = " + parser.c.constructor().info());//DEBUG + } + Symbol[] constrs; + switch (parser.c.constructor().rawInfo()) { + case OverloadedType(Symbol[] alts, _): + constrs = alts; + break; + default: + constrs = new Symbol[]{parser.c.constructor()}; + } + for (int i = 0; i < constrs.length; i++) { + //System.out.println("info = " + e.sym.info()); + switch (constrs[i].rawInfo()) { + case MethodType(Symbol[] vparams, _): + constrs[i].setInfo( + Type.PolyType(smbls, + Type.MethodType( + vparams, constrtype)), parser.phaseId); + break; + case PolyType(_, _): + constrs[i].setInfo( + Type.PolyType(smbls, constrtype), parser.phaseId); + break; + } + //System.out.println("*** constructor " + e.sym + ": " + e.sym.info());//DEBUG + } + } catch (NoSuchElementException e) { + } + } + Type res = defaultType; + if ("extends".equals(token)) { + Vector basetpes = new Vector(); + do { + nextToken(); + basetpes.add(parseType()); + } while (token.equals("with")); + switch (defaultType) { + case CompoundType(_, Scope scope): + res = Type.compoundType( + (Type[])basetpes.toArray(new Type[basetpes.size()]), + scope, + defaultType.symbol()); + } + } + assert ";".equals(token); + return res; + } + + protected Type parseType() { + String name = token; + Symbol s = getTVar(name); + nextToken(); + if (s != Symbol.NONE) + return s.type(); + Symbol clazz = parser.defs.getClass(Name.fromString(name)); + if (token.equals("[")) { + Vector types = new Vector(); + do { + nextToken(); + types.add(parseType()); + } while (token.equals(",")); + assert "]".equals(token); + nextToken(); + Type[] args = new Type[types.size()]; + types.toArray(args); + return Type.TypeRef(clazz.owner().thisType(), clazz, args); + } else { + return parser.defs.monoType(clazz); + } + } + + protected Type parseMetaMethod() { + locals = new Scope(); + try { + nextToken(); + Symbol[] smbls = null; + //System.out.println("parse meta method " + token); + if ("[".equals(token)) { + Vector syms = new Vector(); + do { + nextToken(); + if ("]".equals(token)) + break; + assert token.startsWith("?"); + Symbol s = getTVar(token, owner); + if (s == Symbol.NONE) + return defaultType; + nextToken(); + //System.out.println("new var " + s + ", " + token); + if (token.equals("<")) { + nextToken(); + s.setInfo(parseType(), parser.phaseId); + } + syms.add(s); + } while (token.equals(",")); + assert "]".equals(token); + nextToken(); + smbls = (Symbol[])syms.toArray(new Symbol[syms.size()]); + } + if ("(".equals(token)) { + int i = 0; + Vector params = new Vector(); + do { + nextToken(); + if (")".equals(token)) + break; + params.add(new TermSymbol( + Position.NOPOS, + Name.fromString("x" + (i++)), + owner, + Modifiers.PARAM).setInfo(parseType(), parser.phaseId)); + //System.out.println(" + " + token); + } while (token.equals(",")); + assert ")".equals(token); + nextToken(); + //System.out.println("+++ method " + token); + Type restpe = parseType(); + assert ";".equals(token); + if (smbls == null) + return Type.MethodType( + (Symbol[])params.toArray(new Symbol[params.size()]), + restpe); + else + return Type.PolyType( + smbls, + Type.MethodType( + (Symbol[])params.toArray(new Symbol[params.size()]), + restpe)); + } else { + Type res = parseType(); + assert ";".equals(token); + if (smbls == null) + return Type.PolyType(Symbol.EMPTY_ARRAY, res); + else + return Type.PolyType(smbls, res); + } + } catch (NoSuchElementException e) { + return defaultType; + } finally { + locals = null; + } + } + + protected Type parseMetaField() { + nextToken(); + return parseType(); + } + + protected Type parseConstrField() { + try { + nextToken(); + //System.out.println("+++ constr " + token); + if ("(".equals(token)) { + int i = 0; + Vector params = new Vector(); + do { + nextToken(); + if (")".equals(token)) + break; + params.add(new TermSymbol( + Position.NOPOS, + Name.fromString("x" + (i++)), + owner, + Modifiers.PARAM).setInfo(parseType(), parser.phaseId)); + //System.out.println(" + " + token); + } while (token.equals(",")); + assert ")".equals(token); + nextToken(); + assert ";".equals(token); + return Type.MethodType( + (Symbol[])params.toArray(new Symbol[params.size()]), + parser.ctype); + } else { + assert ";".equals(token); + return Type.PolyType(Symbol.EMPTY_ARRAY, parser.ctype); + } + } catch (NoSuchElementException e) { + return defaultType; + } + } + } +} diff --git a/sources/scalac/symtab/classfile/ClassParser.java b/sources/scalac/symtab/classfile/ClassParser.java new file mode 100644 index 0000000000..9948ba33cf --- /dev/null +++ b/sources/scalac/symtab/classfile/ClassParser.java @@ -0,0 +1,111 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import java.io.*; + + +public class ClassParser extends Type.LazyType { + + /** the global compilation environment + */ + protected Global global; + + public ClassParser(Global global) { + this.global = global; + } + + /** complete class symbol c by loading the class + */ + public void complete(Symbol c) { + //System.out.println("loading " + c);//DEBUG + try { + long msec = System.currentTimeMillis(); + String filename = externalizeFileName(c.fullName()) + ".class"; + AbstractFile f = global.classPath.openFile(filename); + if (f == null) + global.error("could not read class " + c); + else { + new ClassfileParser(global, new AbstractFileReader(f), c).parse(); + global.operation("loaded " + f.getPath() + " in " + + (System.currentTimeMillis() - msec) + "ms"); + //for (Definition e = c.locals().elems; e != null; e = e.sibling) + // if (e.def.kind == TYP) + // e.def.complete(); + } + } catch (IOException e) { + e.printStackTrace(); + global.error("i/o error while loading " + c); + c.setInfo(Type.ErrorType); + } + } + + /** return external representation of file name s, + * converting '.' to File.separatorChar + */ + public String externalizeFileName(Name n) { + if ((n == null) || (n.length() == 0)) + return "."; + byte[] ascii = n.toAscii(); + String s = SourceRepresentation.ascii2string( + ascii, 0, ascii.length); + return s.replace('.', File.separatorChar); + } + + public Type.LazyType staticsParser(Symbol clazz) { + return new StaticsParser(clazz); + } + + public Type.LazyType aliasParser(Symbol alias) { + return new AliasParser(alias); + } + + class StaticsParser extends Type.LazyType { + Symbol clazz; + + StaticsParser(Symbol clazz) { + this.clazz = clazz; + } + + public void complete(Symbol statics) { + ClassParser.this.complete(clazz); + } + } + + class AliasParser extends Type.LazyType { + Symbol alias; + + AliasParser(Symbol alias) { + this.alias = alias; + } + + public void complete(Symbol c) { + try { + long msec = System.currentTimeMillis(); + String filename = externalizeFileName(alias.fullName()) + ".class"; + AbstractFile f = global.classPath.openFile(filename); + if (f == null) + global.error("could not read class " + c); + else { + new ClassfileParser(global, new AbstractFileReader(f), c).parse(); + global.operation("loaded " + f.getPath() + " in " + + (System.currentTimeMillis() - msec) + "ms"); + } + } catch (IOException e) { + e.printStackTrace(); + global.error("i/o error while loading " + c); + c.setInfo(Type.ErrorType); + } + } + } +} + diff --git a/sources/scalac/symtab/classfile/ClassfileConstants.java b/sources/scalac/symtab/classfile/ClassfileConstants.java new file mode 100644 index 0000000000..16b9f5af44 --- /dev/null +++ b/sources/scalac/symtab/classfile/ClassfileConstants.java @@ -0,0 +1,57 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.util.Name; + +public interface ClassfileConstants { + + int JAVA_MAGIC = 0xCAFEBABE; + int JAVA_MAJOR_VERSION = 45; + int JAVA_MINOR_VERSION = 3; + + int CONSTANT_UTF8 = 1; + int CONSTANT_UNICODE = 2; + int CONSTANT_INTEGER = 3; + int CONSTANT_FLOAT = 4; + int CONSTANT_LONG = 5; + int CONSTANT_DOUBLE = 6; + int CONSTANT_CLASS = 7; + int CONSTANT_STRING = 8; + int CONSTANT_FIELDREF = 9; + int CONSTANT_METHODREF = 10; + int CONSTANT_INTFMETHODREF = 11; + int CONSTANT_NAMEANDTYPE = 12; + + int BAD_ATTR = 0x00000; + int SOURCEFILE_ATTR = 0x00001; + int SYNTHETIC_ATTR = 0x00002; + int DEPRECATED_ATTR = 0x00004; + int CODE_ATTR = 0x00008; + int EXCEPTIONS_ATTR = 0x00010; + int CONSTANT_VALUE_ATTR = 0x00020; + int LINE_NUM_TABLE_ATTR = 0x00040; + int LOCAL_VAR_TABLE_ATTR = 0x00080; + int INNERCLASSES_ATTR = 0x08000; + int META_ATTR = 0x10000; + int SCALA_ATTR = 0x20000; + + Name SOURCEFILE_N = Name.fromString("SourceFile"); + Name SYNTHETIC_N = Name.fromString("Synthetic"); + Name DEPRECATED_N = Name.fromString("Deprecated"); + Name CODE_N = Name.fromString("Code"); + Name EXCEPTIONS_N = Name.fromString("Exceptions"); + Name CONSTANT_VALUE_N = Name.fromString("ConstantValue"); + Name LINE_NUM_TABLE_N = Name.fromString("LineNumberTable"); + Name LOCAL_VAR_TABLE_N = Name.fromString("LocalVariableTable"); + Name INNERCLASSES_N = Name.fromString("InnerClasses"); + Name META_N = Name.fromString("JacoMeta"); + Name SCALA_N = Name.fromString("ScalaSignature"); + Name CONSTR_N = Name.fromString("<init>"); +} diff --git a/sources/scalac/symtab/classfile/ClassfileParser.java b/sources/scalac/symtab/classfile/ClassfileParser.java new file mode 100644 index 0000000000..956c83968f --- /dev/null +++ b/sources/scalac/symtab/classfile/ClassfileParser.java @@ -0,0 +1,239 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.util.*; +import scalac.symtab.*; +import java.io.*; +import java.util.*; + +//todo: don't keep statics module in scope. + +public class ClassfileParser implements ClassfileConstants { + + static final int CLASS_ATTR = SOURCEFILE_ATTR + | INNERCLASSES_ATTR + | SYNTHETIC_ATTR + | DEPRECATED_ATTR + | META_ATTR + | SCALA_ATTR; + static final int METH_ATTR = CODE_ATTR + | EXCEPTIONS_ATTR + | SYNTHETIC_ATTR + | DEPRECATED_ATTR + | META_ATTR; + static final int FIELD_ATTR = CONSTANT_VALUE_ATTR + | SYNTHETIC_ATTR + | DEPRECATED_ATTR + | META_ATTR; + + protected Global global; + protected AbstractFileReader in; + protected Symbol c; + protected Type ctype; + protected Scope locals; + protected Scope statics; + protected Scope constr; + protected JavaTypeFactory make; + protected Signatures sigs; + protected ConstantPool pool; + protected AttributeParser attrib; + protected Definitions defs; + protected int phaseId; + + + public ClassfileParser(Global global, AbstractFileReader in, Symbol c) { + this.global = global; + this.in = in; + this.c = c; + this.ctype = Type.TypeRef(c.owner().thisType(), c, Type.EMPTY_ARRAY); + this.make = new JavaTypeCreator(global); + this.sigs = new Signatures(global, make); + this.pool = new ConstantPool(in, sigs); + this.attrib = new AttributeParser(in, pool, this); + this.defs = global.definitions; + this.phaseId = global.POST_ANALYZER_PHASE_ID; + } + + + /** parse the classfile and throw IO exception if there is an + * error in the classfile structure + */ + public void parse() throws IOException { + try { + if (in.nextInt() != JAVA_MAGIC) + throw new IOException("illegal start of class file"); + int minorVersion = in.nextChar(); + int majorVersion = in.nextChar(); + if ((majorVersion < JAVA_MAJOR_VERSION) || + ((majorVersion == JAVA_MAJOR_VERSION) && + (minorVersion < JAVA_MINOR_VERSION))) + throw new IOException("class file has wrong version " + + majorVersion + "." + minorVersion + ", should be " + + JAVA_MAJOR_VERSION + "." + JAVA_MINOR_VERSION); + pool.indexPool(); + int flags = in.nextChar(); + Name name = readClassName(in.nextChar()); + if (c.fullName() != name) + throw new IOException("class file '" + c.fullName() + + "' contains wrong class " + name); + // todo: correct flag transition + c.flags = transFlags(flags); + if ((c.flags & Modifiers.ABSTRACT) != 0) + c.flags = c.flags & ~Modifiers.ABSTRACT | Modifiers.ABSTRACTCLASS; + Type supertpe = readClassType(in.nextChar()); + Type[] basetpes = new Type[in.nextChar() + 1]; + this.locals = new Scope(); + this.statics = new Scope(); + this.constr = new Scope(); + // set type of class + Type classType = Type.compoundType(basetpes, locals, c); + c.setInfo(classType, phaseId); + // set type of statics + Symbol staticsClass = c.module().moduleClass(); + Type staticsInfo = Type.compoundType(Type.EMPTY_ARRAY, statics, staticsClass); + staticsClass.setInfo(staticsInfo, phaseId); + c.module().setInfo(Type.TypeRef(staticsClass.owner().thisType(), + staticsClass, Type.EMPTY_ARRAY)); + basetpes[0] = supertpe; + for (int i = 1; i < basetpes.length; i++) + basetpes[i] = readClassType(in.nextChar()); + int fieldCount = in.nextChar(); + for (int i = 0; i < fieldCount; i++) + parseField(); + int methodCount = in.nextChar(); + for (int i = 0; i < methodCount; i++) + parseMethod(); + // set constructor type to the declared type + Symbol[] constrs = constr.elements(); + if (constrs.length != 0) { + assert constrs.length == 1; + c.constructor().setInfo(constrs[0].info(), phaseId); + } else { + Type constrtype = ((c.flags & Modifiers.INTERFACE) != 0) + ? Type.PolyType(Symbol.EMPTY_ARRAY, ctype) + : Type.MethodType(new Symbol[]{Symbol.NONE}, ctype); + c.constructor().setInfo(constrtype, phaseId); + } + attrib.readAttributes(c, classType, CLASS_ATTR); + //System.out.println("dynamic class: " + c); + //System.out.println("statics class: " + staticsClass); + //System.out.println("module: " + c.module()); + //System.out.println("modules class: " + c.module().type().symbol()); + } catch (RuntimeException e) { + e.printStackTrace(); + throw new IOException("bad class file (" + e.getMessage() + ")"); + } + } + + /** convert Java modifiers into Scala flags + */ + public int transFlags(int flags) { + int res = 0; + if (((flags & 0x0007) == 0) || + ((flags & 0x0002) != 0)) + res |= Modifiers.PRIVATE; + else if ((flags & 0x0004) != 0) + res |= Modifiers.PROTECTED; + if ((flags & 0x0400) != 0) + res |= Modifiers.ABSTRACT; + if ((flags & 0x0010) != 0) + res |= Modifiers.FINAL; + if ((flags & 0x0200) != 0) + res |= Modifiers.INTERFACE | Modifiers.ABSTRACTCLASS; + return res | Modifiers.JAVA; + } + + /** read a class name + */ + protected Name readClassName(int i) { + return (Name)pool.readPool(i); + } + + /** read a class name and return the corresponding class type + */ + protected Type readClassType(int i) { + if (i == 0) + return defs.ANY_TYPE; + Type res = defs.getJavaType((Name)pool.readPool(i)); + if (res == Type.ErrorType) + global.error("unknown class reference " + pool.readPool(i)); + if (res.symbol() == defs.JAVA_OBJECT_TYPE.symbol()) + return defs.ANYREF_TYPE; + else + return res; + } + + /** read a signature and return it as a type + */ + protected Type readType(int i) { + Name sig = pool.readExternal(i); + return sigs.sigToType(Name.names, sig.index, sig.length()); + } + + /** read a field + */ + protected void parseField() { + int flags = in.nextChar(); + Name name = (Name)pool.readPool(in.nextChar()); + Type type = readType(in.nextChar()); + int mods = transFlags(flags); + if ((flags & 0x0010) == 0) + mods |= Modifiers.MUTABLE; + Symbol owner = c; + if ((flags & 0x0008) != 0) + owner = c.module().moduleClass(); + Symbol s = new TermSymbol(Position.NOPOS, name, owner, mods); + s.setInfo(type, phaseId); + attrib.readAttributes(s, type, FIELD_ATTR); + ((flags & 0x0008) != 0 ? statics : locals).enterOrOverload(s); + } + + /** read a method + */ + protected void parseMethod() { + int flags = in.nextChar(); + Name name = (Name)pool.readPool(in.nextChar()); + Type type = readType(in.nextChar()); + if (CONSTR_N.equals(name)) { + Symbol s = TermSymbol.newConstructor(c, transFlags(flags)); + // kick out protected, package visible or + // private constructors + if (((flags & 0x0004) != 0) || + ((flags & 0x0002) != 0) || + ((flags & 0x0007) == 0)) { + attrib.readAttributes(s, type, METH_ATTR); + return; + } + switch (type) { + case MethodType(Symbol[] vparams, _): + if (c == defs.OBJECT_CLASS) + type = Type.PolyType(Symbol.EMPTY_ARRAY, ctype); + else + type = Type.MethodType(vparams, ctype); + break; + default: + throw new ApplicationError(); + } + s.setInfo(type, phaseId); + attrib.readAttributes(s, type, METH_ATTR); + //System.out.println("-- enter " + s); + constr.enterOrOverload(s); + } else { + Symbol s = new TermSymbol( + Position.NOPOS, name, + ((flags & 0x0008) != 0) ? c.module().moduleClass() : c, + transFlags(flags)); + s.setInfo(type, phaseId); + attrib.readAttributes(s, type, METH_ATTR); + ((flags & 0x0008) != 0 ? statics : locals).enterOrOverload(s); + } + } +} diff --git a/sources/scalac/symtab/classfile/ConstantPool.java b/sources/scalac/symtab/classfile/ConstantPool.java new file mode 100644 index 0000000000..4260b9e6ad --- /dev/null +++ b/sources/scalac/symtab/classfile/ConstantPool.java @@ -0,0 +1,183 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; + +public class ConstantPool implements ClassfileConstants { + + AbstractFileReader in; + Signatures sigparser; + + /** the objects of the constant pool + */ + protected Object[] poolObj; + + /** for every constant pool entry, an index into in.buf where the + * defining section of the entry is found + */ + protected int[] poolIdx; + + /** constructor + */ + protected ConstantPool(AbstractFileReader in, Signatures sigparser) { + this.in = in; + this.sigparser = sigparser; + } + + /** index all constant pool entries, writing their start + * addresses into poolIdx + */ + public void indexPool() { + poolIdx = new int[in.nextChar()]; + poolObj = new Object[poolIdx.length]; + int i = 1; + while (i < poolIdx.length) { + poolIdx[i++] = in.bp; + byte tag = in.nextByte(); + switch (tag) { + case CONSTANT_UTF8: + case CONSTANT_UNICODE: { + int len = in.nextChar(); + in.skip(len); + break; + } + case CONSTANT_CLASS: + case CONSTANT_STRING: + in.skip(2); + break; + case CONSTANT_FIELDREF: + case CONSTANT_METHODREF: + case CONSTANT_INTFMETHODREF: + case CONSTANT_NAMEANDTYPE: + case CONSTANT_INTEGER: + case CONSTANT_FLOAT: + in.skip(4); + break; + case CONSTANT_LONG: + case CONSTANT_DOUBLE: + in.skip(8); + i++; + break; + default: + throw new RuntimeException("bad constant pool tag: " + tag + + " at " + (in.bp - 1)); + } + } + } + + /** if name is an array type or class signature, return the + * corresponding type; otherwise return a class definition with given name + */ + protected Object classOrType(Name name) { + if ((name.sub(0) == '[') || (name.sub(name.length() - 1) == ';')) { + byte[] ascii = name.toAscii(); + return sigparser.sigToType(ascii, 0, ascii.length); + } else + return name; + } + + /** read constant pool entry at start address i, use poolObj as a cache. + */ + public Object readPool(int i) { + if (poolObj[i] != null) + return poolObj[i]; + int index = poolIdx[i]; + if (index == 0) + return null; + switch (in.byteAt(index)) { + case CONSTANT_UTF8: + poolObj[i] = Name.fromAscii(in.buf, index + 3, in.getChar(index + 1)); + break; + + case CONSTANT_UNICODE: + throw new RuntimeException("can't read unicode strings in classfiles"); + + case CONSTANT_CLASS: + poolObj[i] = classOrType(readExternal(in.getChar(index + 1))); + break; + + case CONSTANT_FIELDREF: { + //Symbol owner = (Symbol)readPool(in.getChar(index + 1)); + //NameAndType nt = (NameAndType)readPool(in.getChar(index + 3)); + //poolObj[i] = new TermSymbol(Kinds.VAR, Position.NOPOS, nt.name, owner, 0) + // .type(sigparser.sigToType(Name.names, nt.sig.index, nt.sig.length())); + throw new RuntimeException("can't read constant_fieldrefs in classfiles"); + } + + case CONSTANT_METHODREF: + case CONSTANT_INTFMETHODREF: { + //Symbol owner = (Symbol)readPool(in.getChar(index + 1)); + //NameAndType nt = (NameAndType)readPool(in.getChar(index + 3)); + //poolObj[i] = new TermSymbol(Kinds.FUN, Position.NOPOS, nt.name, owner, 0) + // .type(sigparser.sigToType(Name.names, nt.sig.index, nt.sig.length())); + throw new RuntimeException("can't read constant_methodrefs in classfiles"); + } + + case CONSTANT_NAMEANDTYPE: + poolObj[i] = new NameAndType((Name)readPool(in.getChar(index + 1)), + readExternal(in.getChar(index + 3))); + break; + + case CONSTANT_STRING: + case CONSTANT_INTEGER: + case CONSTANT_FLOAT: + case CONSTANT_LONG: + case CONSTANT_DOUBLE: + throw new RuntimeException("can't read constants in classfiles"); + + default: + throw new RuntimeException("bad constant pool tag: " + in.byteAt(index)); + } + return poolObj[i]; + } + + /** return internal representation of buf[offset..offset+len-1], + * converting '/' to '.' + */ + public byte[] internalize(byte[] buf, int offset, int len) { + byte[] translated = new byte[len]; + for (int j = 0; j < len; j++) { + byte b = buf[offset + j]; + if (b == '/') + translated[j] = '.'; + else + translated[j] = b; + } + return translated; + } + + /** read a constant pool string and convert to internal representation. + */ + public Name readExternal(int i) { + if (poolObj[i] == null) { + int index = poolIdx[i]; + if (in.byteAt(index) == CONSTANT_UTF8) { + int len = in.getChar(index + 1); + byte[] translated = internalize(in.buf, index + 3, len); + poolObj[i] = Name.fromAscii(translated, 0, len); + } + } + return (Name)poolObj[i]; + } + + /** the name and type signature of a method or field + */ + public static final class NameAndType { + public Name name; + public Name sig; + + public NameAndType(Name name, Name sig) { + this.name = name; + this.sig = sig; + } + } +} diff --git a/sources/scalac/symtab/classfile/JavaTypeCreator.java b/sources/scalac/symtab/classfile/JavaTypeCreator.java new file mode 100644 index 0000000000..5bdc9402ba --- /dev/null +++ b/sources/scalac/symtab/classfile/JavaTypeCreator.java @@ -0,0 +1,82 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.Global; +import scalac.util.*; +import scalac.symtab.*; +import Type.*; + + +public class JavaTypeCreator implements JavaTypeFactory { + + protected Global global; + + public JavaTypeCreator(Global global) { + this.global = global; + } + + public Type byteType() { + return global.definitions.BYTE_TYPE; + } + + public Type shortType() { + return global.definitions.SHORT_TYPE; + } + + public Type charType() { + return global.definitions.CHAR_TYPE; + } + + public Type intType() { + return global.definitions.INT_TYPE; + } + + public Type longType() { + return global.definitions.LONG_TYPE; + } + + public Type floatType() { + return global.definitions.FLOAT_TYPE; + } + + public Type doubleType() { + return global.definitions.DOUBLE_TYPE; + } + + public Type booleanType() { + return global.definitions.BOOLEAN_TYPE; + } + + public Type voidType() { + return global.definitions.UNIT_TYPE; + } + + public Type classType(Name classname) { + return global.definitions.getJavaType(classname); + } + + public Type arrayType(Type elemtpe) { + return global.definitions.arrayType(elemtpe); + } + + public Type methodType(Type[] argtpes, Type restpe, Type[] thrown) { + Symbol[] args = new Symbol[argtpes.length]; + for (int i = 0; i < args.length; i++) { + args[i] = new TermSymbol( + Position.NOPOS, Name.fromString("x" + i), Symbol.NONE, Modifiers.PARAM); + args[i].setInfo(argtpes[i]); + } + return new MethodType(args, restpe); + } + + public Type packageType(Name packagename) { + return null; + } +} diff --git a/sources/scalac/symtab/classfile/JavaTypeFactory.java b/sources/scalac/symtab/classfile/JavaTypeFactory.java new file mode 100644 index 0000000000..8205055bc9 --- /dev/null +++ b/sources/scalac/symtab/classfile/JavaTypeFactory.java @@ -0,0 +1,28 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.symtab.Type; +import scalac.util.Name; + +public interface JavaTypeFactory { + Type byteType(); + Type shortType(); + Type charType(); + Type intType(); + Type longType(); + Type floatType(); + Type doubleType(); + Type booleanType(); + Type voidType(); + Type classType(Name classname); + Type arrayType(Type elemtpe); + Type methodType(Type[] argtpes, Type restpe, Type[] thrown); + Type packageType(Name packagename); +} diff --git a/sources/scalac/symtab/classfile/PackageParser.java b/sources/scalac/symtab/classfile/PackageParser.java new file mode 100644 index 0000000000..50fcb46b00 --- /dev/null +++ b/sources/scalac/symtab/classfile/PackageParser.java @@ -0,0 +1,129 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import java.io.*; + +public class PackageParser extends Type.LazyType { + + /** the global compilation environment + */ + protected Global global; + + /** the class parser + */ + public ClassParser classCompletion; + + public PackageParser(Global global) { + this.global = global; + this.classCompletion = new ClassParser(global); + } + + /** complete package type symbol p by loading all package members + */ + public void complete(Symbol p) { + long msec = System.currentTimeMillis(); + Scope members = new Scope(); + String dirname = null; + Name name = p.fullName(); + if (name.length() == 0) { + // includeMembers(AbstractFile.open(null, "."), p, members, false); + } else { + dirname = externalizeFileName(name); + assert !dirname.startsWith("com") : p;//debug + if (!dirname.endsWith("/")) + dirname += "/"; + } + String[] base = global.classPath.components(); + for (int i = 0; i < base.length; i++) { + includeMembers( + AbstractFile.open(base[i], dirname), p, members, dirname != null); + } + p.setInfo(Type.compoundType(Type.EMPTY_ARRAY, members, p)); + if (dirname == null) + dirname = "anonymous package"; + global.operation("scanned " + dirname + " in " + + (System.currentTimeMillis() - msec) + "ms"); + } + + /** read directory of a classpath directory and include members + * in package/module scope + */ + protected void includeMembers(AbstractFile dir, Symbol p, Scope locals, + boolean inclClasses) { + if (dir == null) + return; + String[] filenames = null; + try { + if ((filenames = dir.list()) == null) + return; + for (int j = 0; j < filenames.length; j++) { + String fname = filenames[j]; + if (inclClasses && fname.endsWith(".class")) { + Name n = Name.fromString(fname.substring(0, fname.length() - 6)) + .toTypeName(); + ClassSymbol clazz = new ClassSymbol(n, p, classCompletion); + clazz.constructor().setInfo( + classCompletion.staticsParser(clazz)); + // enter class + locals.enter(clazz); + locals.enter(clazz.constructor()); + // enter module, except for scala.Object class + // todo: why not there also?. + if (!(n == Names.Object.toTypeName() && + p.fullName().toTermName() == Names.scala)) { + Scope.Entry e = locals.lookupEntry(clazz.module().name); + if (e != Scope.Entry.NONE) { + // we already have a package of the same name; delete it + locals.unlink(e); + } + locals.enter(clazz.module()); + } + } else if (fname.endsWith("/") && !fname.equals("META-INF/")) { + Name n = Name.fromString(fname.substring(0, fname.length() - 1)); + if (locals.lookup(n) == Symbol.NONE) { + TermSymbol module = TermSymbol.newJavaPackageModule(n, p, this); + locals.enter(module); + } + } else if (fname.endsWith(".scala")) { + Name n = Name.fromString(fname.substring(0, fname.length() - 6)) + .toTypeName(); + if (locals.lookup(n) == Symbol.NONE) { + SourceCompleter completer = new SourceCompleter(global, + dir.getPath() + File.separatorChar + fname); + ClassSymbol clazz = new ClassSymbol(n, p, completer); + clazz.constructor().setInfo(completer); + clazz.module().setInfo(completer); + // enter class + locals.enter(clazz); + locals.enter(clazz.constructor()); + locals.enter(clazz.module()); + } + } + } + } catch (IOException e) { + } + } + + /** return external representation of file name s, + * converting '.' to File.separatorChar + */ + + public String externalizeFileName(Name n) { + if ((n == null) || (n.length() == 0)) + return "."; + byte[] ascii = n.toAscii(); + String s = SourceRepresentation.ascii2string( + ascii, 0, ascii.length); + return s.replace('.', File.separatorChar); + } +} diff --git a/sources/scalac/symtab/classfile/Signatures.java b/sources/scalac/symtab/classfile/Signatures.java new file mode 100644 index 0000000000..9676882aed --- /dev/null +++ b/sources/scalac/symtab/classfile/Signatures.java @@ -0,0 +1,122 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import java.util.*; +import Type.*; + +public class Signatures { + + /** signature constants + */ + Name BYTE_SIG = Name.fromString("B"); + Name SHORT_SIG = Name.fromString("S"); + Name CHAR_SIG = Name.fromString("C"); + Name INT_SIG = Name.fromString("I"); + Name LONG_SIG = Name.fromString("J"); + Name FLOAT_SIG = Name.fromString("F"); + Name DOUBLE_SIG = Name.fromString("D"); + Name BOOLEAN_SIG = Name.fromString("Z"); + Name VOID_SIG = Name.fromString("V"); + Name CLASS_SIG = Name.fromString("L"); + Name ARRAY_SIG = Name.fromString("["); + Name ARGBEGIN_SIG = Name.fromString("("); + Name ARGEND_SIG = Name.fromString(")"); + + Global global; + JavaTypeFactory make; + + + public Signatures(Global global, JavaTypeFactory make) { + this.make = make; + this.global = global; + } + + /** the type represented by signature[offset..]. + */ + protected byte[] signature; + protected int sigp; + protected int limit; + + public Type sigToType(byte[] sig, int offset, int len) { + signature = sig; + sigp = offset; + limit = offset + len; + return sigToType(); + } + + protected Type sigToType() { + switch (signature[sigp]) { + case 'B': + sigp++; + return make.byteType(); + case 'C': + sigp++; + return make.charType(); + case 'D': + sigp++; + return make.doubleType(); + case 'F': + sigp++; + return make.floatType(); + case 'I': + sigp++; + return make.intType(); + case 'J': + sigp++; + return make.longType(); + case 'L': + sigp++; + int start = sigp; + while (signature[sigp] != ';') + sigp++; + return make.classType(Name.fromAscii(signature, start, (sigp++) - start)); + case 'S': + sigp++; + return make.shortType(); + case 'V': + sigp++; + return make.voidType(); + case 'Z': + sigp++; + return make.booleanType(); + case '[': + sigp++; + while (('0' <= signature[sigp]) && (signature[sigp] <= '9')) + sigp++; + return make.arrayType(sigToType()); + case '(': + return make.methodType(sigToTypes(')'), sigToType(), Type.EMPTY_ARRAY); + default: + global.error("bad signature: " + + SourceRepresentation.ascii2string(signature, sigp, 1)); + return Type.ErrorType; + } + } + + protected Type[] sigToTypes(char terminator) { + sigp++; + return sigToTypes(terminator, 0); + } + + protected Type[] sigToTypes(char terminator, int i) { + if (signature[sigp] == terminator) { + sigp++; + return new Type[i]; + } else { + Type t = sigToType(); + Type[] vec = sigToTypes(terminator, i+1); + vec[i] = t; + return vec; + } + } +} |