From 8c650924748b9433bf4b3e51d5be97fcdff2e67f Mon Sep 17 00:00:00 2001 From: Matthias Zenger Date: Tue, 23 Sep 2003 00:32:46 +0000 Subject: Initial implementation of scalap. --- sources/scala/tools/scalap/AbstractFile.scala | 265 +++++++++++++++++++++++ sources/scala/tools/scalap/ByteArrayReader.scala | 133 ++++++++++++ sources/scala/tools/scalap/ClassPath.scala | 175 +++++++++++++++ sources/scala/tools/scalap/Classfile.scala | 116 ++++++++++ sources/scala/tools/scalap/Classfiles.scala | 47 ++++ sources/scala/tools/scalap/CodeWriter.scala | 131 +++++++++++ sources/scala/tools/scalap/Entity.scala | 123 +++++++++++ sources/scala/tools/scalap/EntityTable.scala | 141 ++++++++++++ sources/scala/tools/scalap/FileCache.scala | 37 ++++ sources/scala/tools/scalap/Flags.scala | 112 ++++++++++ sources/scala/tools/scalap/Main.scala | 46 ++++ sources/scala/tools/scalap/Names.scala | 85 ++++++++ sources/scala/tools/scalap/ScalaAttribute.scala | 95 ++++++++ sources/scala/tools/scalap/ScalaWriter.scala | 219 +++++++++++++++++++ 14 files changed, 1725 insertions(+) create mode 100644 sources/scala/tools/scalap/AbstractFile.scala create mode 100644 sources/scala/tools/scalap/ByteArrayReader.scala create mode 100644 sources/scala/tools/scalap/ClassPath.scala create mode 100644 sources/scala/tools/scalap/Classfile.scala create mode 100644 sources/scala/tools/scalap/Classfiles.scala create mode 100644 sources/scala/tools/scalap/CodeWriter.scala create mode 100644 sources/scala/tools/scalap/Entity.scala create mode 100644 sources/scala/tools/scalap/EntityTable.scala create mode 100644 sources/scala/tools/scalap/FileCache.scala create mode 100644 sources/scala/tools/scalap/Flags.scala create mode 100644 sources/scala/tools/scalap/Main.scala create mode 100644 sources/scala/tools/scalap/Names.scala create mode 100644 sources/scala/tools/scalap/ScalaAttribute.scala create mode 100644 sources/scala/tools/scalap/ScalaWriter.scala (limited to 'sources') diff --git a/sources/scala/tools/scalap/AbstractFile.scala b/sources/scala/tools/scalap/AbstractFile.scala new file mode 100644 index 0000000000..38e2acebf6 --- /dev/null +++ b/sources/scala/tools/scalap/AbstractFile.scala @@ -0,0 +1,265 @@ +// AbstractFile +// 04-Feb-2002, Matthias Zenger + +package scalap; + +import scala.collection._; +import java.io._; +import java.util.jar._; + + +trait AbstractFile { + + /** separator + */ + protected val separator: Char = File.separatorChar; + + /** get name of the file + */ + def getName: String; + + /** get path of the file + */ + def getPath: String; + + /** does the file exist? + */ + def exists: Boolean; + + /** is the file a directory? + */ + def isDirectory: Boolean; + + /** read content of the file into a byte[] buffer + */ + def content: Array[Byte]; + + /** list contents of a directory + */ + def elements: Iterator[String]; + + /** open a new file + */ + def open(name: String): AbstractFile; + + /** return an input stream for the file + */ + def getInputStream: InputStream = new ByteArrayInputStream(content); +} + +class PlainFile(f: File) with AbstractFile { + + def getName = f.getName(); + + def getPath = f.getPath(); + + def exists = f.exists(); + + def isDirectory = f.isDirectory(); + + def content = { + val in = new FileInputStream(f); + var rest = f.length().asInstanceOf[Int]; + val buf = new Array[Byte](rest); + do { + val res = in.read(buf, buf.length - rest, rest); + if (res == -1) + error("read error"); + rest = rest - res; + } while (rest > 0); + in.close(); + buf; + } + + def elements = { + val fs = f.listFiles(); + if (fs == null) + Iterator.empty[String] + else + new Iterator[String] { + var i = 0; + def hasNext = (i < fs.length); + def next = { + val res = fs(i).getName(); + i = i + 1; + if (fs(i - 1).isDirectory() && + !res.endsWith("/")) res + "/" else res; + } + } + } + + def open(name: String) = new PlainFile(new File(f, name)); +} + +class JarArchive(f: File) with AbstractFile { + val jarFile = try { new JarFile(f) } catch { case e => null }; + var entries: mutable.Map[String, JarArchiveEntry] = _; + + def getName = f.getName(); + + def getPath = f.getPath(); + + def exists = (jarFile != null); + + def isDirectory = (jarFile != null); + + def content: Array[Byte] = error("cannot read archive"); + + private def load = { + //entries = new mutable.HashMap[String, JarArchiveEntry]; + entries = new mutable.JavaMapAdaptor(new java.util.HashMap()); + if (jarFile != null) { + val enum = jarFile.entries(); + while (enum.hasMoreElements()) { + val candidate = enum.nextElement().asInstanceOf[JarEntry].getName(); + var i = candidate.indexOf('/'); + var j = 0; + var files = entries; + while (i >= 0) { + val dirname = candidate.substring(j, i + 1); + j = i + 1; + if (!files.isDefinedAt(dirname)) + files(dirname) = new JarDirEntry(candidate.substring(0, j)); + files = files(dirname).entries; + i = candidate.indexOf('/', j); + } + if (j < (candidate.length() - 1)) { + val filename = candidate.substring(j); + if (!files.isDefinedAt(filename)) + files(filename) = new JarFileEntry(candidate); + } + } + } + } + + def list(prefix: String) = { + val pref = prefix.replace(File.separatorChar, '/'); + if (entries == null) + load; + var i = pref.indexOf('/'); + var j = 0; + var files = entries; + var continue = true; + while (continue && (i >= 0)) { + val dirname = pref.substring(j, i + 1); + j = i + 1; + continue = files.isDefinedAt(dirname); + if (continue) { + files = files(dirname).entries; + i = pref.indexOf('/', j); + } + } + if (!continue) + Iterator.empty; + else if (j < (pref.length() - 1)) { + if (files.isDefinedAt(pref.substring(j))) + List(pref).elements; + else + Iterator.empty; + } else + files.keys; + } + + def elements = list(""); + + def open(fname: String) = { + if (entries == null) + load; + val name = fname.replace(File.separatorChar, '/'); + var i = name.indexOf('/'); + var j = 0; + var namelen = name.length(); + var files = entries; + var res: AbstractFile = null; + while ((res == null) && (i >= 0)) { + val dirname = name.substring(j, i + 1); + j = i + 1; + if (files != null) { + if (files.isDefinedAt(dirname)) { + if (j == namelen) + res = files(dirname); + else + files = files(dirname).entries; + } else + files = null; + } + i = name.indexOf('/', j); + } + if (res != null) + res + else if (j < (namelen - 1)) { + if (files == null) + new JarArchiveEntry(name, false); + else { + val filename = name.substring(j); + if (files.isDefinedAt(filename)) + files(filename) + else + new JarArchiveEntry(name, false) + } + } else + new JarArchiveEntry(name, true); + } + + class JarArchiveEntry(name: String, dir: Boolean) with AbstractFile { + + def getName = name.substring( + name.lastIndexOf('/', name.length() - (if (dir) 2 else 1)) + 1); + + def getPath = name; + + def getFullName = name; + + def exists = false; + + def isDirectory = dir; + + def elements: Iterator[String] = error("nothing to iterate over"); + + def content: Array[Byte] = error("cannot read archive"); + + def open(n: String): AbstractFile = error("cannot open archive entry"); + + def entries: mutable.Map[String, JarArchiveEntry] = error("no entries"); + } + + final class JarDirEntry(name: String) extends JarArchiveEntry(name, true) { + //val entr = new mutable.HashMap[String, JarArchiveEntry]; + val entr = new mutable.JavaMapAdaptor[String, JarArchiveEntry](new java.util.HashMap()); + + override def getPath = JarArchive.this.getPath + "(" + name + ")"; + + override def exists = true; + + override def elements = JarArchive.this.list(name); + + override def open(fname: String) = JarArchive.this.open( + name + fname.replace(File.separatorChar, '/')); + + override def entries: mutable.Map[String, JarArchiveEntry] = entr; + } + + final class JarFileEntry(name: String) extends JarArchiveEntry(name, false) { + + override def getPath = JarArchive.this.getPath + "(" + name + ")"; + + override def exists = true; + + override def content: Array[Byte] = { + val jarEntry = jarFile.getJarEntry(name); + if (jarEntry == null) + error("unable to read " + name); + val in = jarFile.getInputStream(jarEntry); + var rest = jarEntry.getSize().asInstanceOf[Int]; + val buf = new Array[Byte](rest); + do { + val res = in.read(buf, buf.length - rest, rest); + if (res == -1) + error("read error"); + rest = rest - res; + } while (rest > 0); + in.close(); + buf; + } + } +} diff --git a/sources/scala/tools/scalap/ByteArrayReader.scala b/sources/scala/tools/scalap/ByteArrayReader.scala new file mode 100644 index 0000000000..71938dbe82 --- /dev/null +++ b/sources/scala/tools/scalap/ByteArrayReader.scala @@ -0,0 +1,133 @@ +package scalap; + +class ByteArrayReader(content: Array[Byte]) { + import java.io._; + + /** the buffer containing the file + */ + val buf: Array[Byte] = content; + + /** the current input pointer + */ + var bp: Int = 0; + + /** return byte at offset 'pos' + */ + def byteAt(pos: Int): Byte = buf(pos); + + /** read a byte + */ + def nextByte: Byte = { + bp = bp + 1; + buf(bp - 1) + } + + /** read some bytes + */ + def nextBytes(len: Int): Array[Byte] = { + val res = new Array[Byte](len); + System.arraycopy(buf, bp, res, 0, len); + bp = bp + len; + res + } + + /** read a character + */ + def nextChar: Char = { + bp = bp + 2; + (((buf(bp - 2) & 0xff) << 8) + (buf(bp - 1) & 0xff)).asInstanceOf[Char]; + } + + /** read an integer + */ + def nextInt: Int = { + bp = bp + 4; + ((buf(bp - 4) & 0xff) << 24) + + ((buf(bp - 3) & 0xff) << 16) + + ((buf(bp - 2) & 0xff) << 8) + + (buf(bp - 1) & 0xff); + } + + /** read a long + */ + def nextLong: Long = + (nextInt.asInstanceOf[Long] << 32) + (nextInt.asInstanceOf[Long] & 0xffffffffL); + + /** read a float + */ + def nextFloat: Float = java.lang.Float.intBitsToFloat(nextInt); + + /** read a double + */ + def nextDouble: Double = java.lang.Double.longBitsToDouble(nextLong); + + /** read the next integer number + */ + def nextNat: Int = { + var x = 0; + var b: Byte = 0; + do { + b = buf(bp); + bp = bp + 1; + x = (x << 7) + (b & 0x7f); + } while ((b & 0x80) != 0); + x + } + + /** read an UTF8 encoded string + */ + def nextUTF8(len: Int): String = { + val cs: Array[Char] = new Array(len); + var i = bp; + var j = 0; + bp = bp + len; + while (i < bp) { + var b: Int = buf(i) & 0xFF; + i = i + 1; + if (b >= 0xE0) { + b = ((b & 0x0F) << 12) | (buf(i) & 0x3F) << 6; + i = i + 1; + b = b | (buf(i) & 0x3F); + i = i + 1; + } else if (b >= 0xC0) { + b = ((b & 0x1F) << 6) | (buf(i) & 0x3F); + i = i + 1; + } + cs(j) = b.asInstanceOf[Char]; + j = j + 1; + } + new String(cs, 0, j) + } + + /** extract a character at position bp from buf + */ + def getChar(bp: Int): Char = + (((buf(bp) & 0xff) << 8) + (buf(bp + 1) & 0xff)).asInstanceOf[Char]; + + /** extract an integer at position bp from buf + */ + def getInt(bp: Int): Int = + ((buf(bp ) & 0xff) << 24) + + ((buf(bp + 1) & 0xff) << 16) + + ((buf(bp + 2) & 0xff) << 8) + + (buf(bp + 3) & 0xff); + + /** extract a long integer at position bp from buf + */ + def getLong(bp: Int): Long = + (getInt(bp).asInstanceOf[Long] << 32) + (getInt(bp + 4).asInstanceOf[Long] & 0xffffffffL); + + /** extract a float at position bp from buf + */ + def getFloat(bp: Int): Float = java.lang.Float.intBitsToFloat(getInt(bp)); + + /** extract a double at position bp from buf + */ + def getDouble(bp: Int): Double = java.lang.Double.longBitsToDouble(getLong(bp)); + + /** skip next 'n' bytes + */ + def skip(n: Int): Unit = { + bp = bp + n; + } +} diff --git a/sources/scala/tools/scalap/ClassPath.scala b/sources/scala/tools/scalap/ClassPath.scala new file mode 100644 index 0000000000..74c81de732 --- /dev/null +++ b/sources/scala/tools/scalap/ClassPath.scala @@ -0,0 +1,175 @@ +// ClassPath +// 04-Mar-2002, Matthias Zenger + +package scalap; + +import java.io._; +import scala.collection._; + + +class ClassPath { + + /** the character separating files + */ + protected val FILE_SEP = File.separator; + + /** the separator in class path specifications + */ + protected val PATH_SEP = System.getProperty("path.separator"); + + /** the default class path + */ + val classPath = System.getProperty("java.class.path"); + + /** the default boot class path + */ + val bootPath = System.getProperty("sun.boot.class.path"); + + /** the default extension path + */ + val extensionPath = System.getProperty("java.ext.dirs"); + + /** the corresponding file cache (for not reading .jar files over + * and over again) + */ + val cache = new FileCache; + + /** the various class path roots + */ + protected var root: List[String] = decompose(bootPath) ::: + expand(extensionPath) ::: + decompose(classPath); + + + /** append files from the extension directories + */ + protected def expand(edirs: String): List[String] = + if (edirs == null) + Nil + else { + val extdirs = edirs + PATH_SEP; + val length = extdirs.length(); + var i = 0; + var path: List[String] = Nil; + while (i < length) { + val k = extdirs.indexOf(PATH_SEP, i); + val dirname = extdirs.substring(i, k); + if ((dirname != null) && (dirname.length() > 0)) { + val iter = Iterator.fromArray(new File(dirname).list()); + val dname = if (dirname.endsWith(FILE_SEP)) dirname else dirname + FILE_SEP; + while (iter.hasNext) { + val entry = iter.next; + if (entry.endsWith(".jar")) + path = (dname + entry) :: path; + } + } + i = k + 1; + } + path + }; + + /** parse a class path specification and return an array + * of existing class file locations + */ + protected def decompose(p: String): List[String] = { + val path = if (p.endsWith(PATH_SEP)) p else p + PATH_SEP; + var components: List[String] = Nil; + var i = 0; + while (i < path.length()) { + val j = path.indexOf(PATH_SEP, i); + val subpath = path.substring(i, j); + if (new File(subpath).exists()) + components = subpath :: components; + i = j + 1; + } + components.reverse; + } + + /** find file with given name in class path and return an abstract + * file representation together with the originating class path + * component. + */ + def findFile(name: String): Pair[AbstractFile, String] = { + val iter = root.elements; + var entry: AbstractFile = null; + var continue: Boolean = true; + var origin: String = null; + while (continue && iter.hasNext) { + origin = iter.next; + entry = cache.open(origin, name); + if (entry.exists) + continue = false; + } + Pair(entry, origin) + } + + /** find file with given name in class path and return an abstract + * file representation. + */ + def openFile(name: String): AbstractFile = findFile(name)._1; + + /** find class with given name in class path and return an abstract + * file representation. + */ + def openClass(name: String): AbstractFile = + openFile(name.replace('.', File.separatorChar) + ".class"); + + def elements: Iterator[String] = root.elements; + + def classes: Iterator[String] = new Iterator[String] { + val todo: mutable.Stack[Pair[AbstractFile, String]] = new mutable.Stack; + var iter: Iterator[String] = Iterator.empty; + var file: AbstractFile = null; + var path: String = null; + var clazz: String = null; + root.foreach { component => { + val f = cache.open(component, null); + if (f.exists && f.isDirectory) + todo.push(Pair(f, null)); + }}; + scan; + def hasNext = (clazz != null); + def next = + if (clazz == null) + error("no next element"); + else { + val res = clazz; scan; res + }; + def scan: Unit = { + if (!iter.hasNext) { + if (todo.isEmpty) + clazz = null; + else { + val Pair(f, p) = todo.top; + todo.pop; + iter = f.elements; + file = f; + path = if ((p != null) && p.endsWith("/")) + p.substring(0, p.length() - 1) else p; + scan; + } + } else { + var continue = true; + while (continue && iter.hasNext) { + val g = file.open(iter.next); + clazz = if (path == null) g.getName else path + "." + g.getName; + if (clazz.endsWith(".class")) { + clazz = clazz.substring(0, clazz.length() - 6); + continue = false; + } else if (g.exists && g.isDirectory) + todo.push(Pair(g, clazz)); + } + if (continue) + scan; + } + } + } + + /** return a textual representation of this class path + */ + override def toString() = root match { + case Nil => "" + case x :: Nil => x + case x :: xs => xs.foldLeft(x)((s, e) => s + PATH_SEP + e); + } +} diff --git a/sources/scala/tools/scalap/Classfile.scala b/sources/scala/tools/scalap/Classfile.scala new file mode 100644 index 0000000000..c37459ba92 --- /dev/null +++ b/sources/scala/tools/scalap/Classfile.scala @@ -0,0 +1,116 @@ +package scalap; + +class Classfile(in: ByteArrayReader) { + import Classfiles._; + + assert(in.nextInt == JAVA_MAGIC); + val minorVersion = in.nextChar; + val majorVersion = in.nextChar; + val pool = readPool; + val flags = in.nextChar; + val classname = in.nextChar; + val superclass = in.nextChar; + val interfaces = readInterfaces; + val fields = readMembers(true); + val methods = readMembers(false); + val attribs = readAttribs; + + def readAttribs = { + val n = in.nextChar; + var attribs: List[Attribute] = Nil; + var i = 0; + while (i < n) { + attribs = Attribute(in.nextChar, in.nextBytes(in.nextInt)) :: attribs; + i = i + 1; + } + attribs + } + + def readMembers(field: Boolean) = { + val n = in.nextChar; + var members: List[Member] = Nil; + var i = 0; + while (i < n) { + members = Member(field, in.nextChar, in.nextChar, in.nextChar, readAttribs) :: members; + i = i + 1; + } + members + } + + def readInterfaces = { + val n = in.nextChar; + var intfs: List[Int] = Nil; + var i = 0; + while (i < n) { + intfs = in.nextChar :: intfs; + i = i + 1; + } + intfs + } + + def readPool = { + val pool = new Array[PoolEntry](in.nextChar); + var i = 1; + while (i < pool.length) { + val tag: Int = in.nextByte; + tag match { + case 1 => // CONSTANT_UTF8 + pool(i) = UTF8(in.nextUTF8(in.nextChar)); + case 2 => // CONSTANT_UNICODE + in.skip(in.nextChar); + pool(i) = Empty(); + case 7 => // CONSTANT_CLASS + pool(i) = ClassRef(in.nextChar); + case 8 => // CONSTANT_STRING + pool(i) = StringConst(in.nextChar); + case 9 => // CONSTANT_FIELDREF + pool(i) = FieldRef(in.nextChar, in.nextChar); + case 10 => // CONSTANT_METHODREF + pool(i) = MethodRef(in.nextChar, in.nextChar); + case 11 => // CONSTANT_INTFMETHODREF + pool(i) = IntfMethodRef(in.nextChar, in.nextChar); + case 12 => // CONSTANT_NAMEANDTYPE + pool(i) = NameAndType(in.nextChar, in.nextChar); + case 3 => // CONSTANT_INTEGER + pool(i) = IntegerConst(in.nextInt); + case 4 => // CONSTANT_FLOAT + pool(i) = FloatConst(in.nextFloat); + case 5 => // CONSTANT_LONG + pool(i) = LongConst(in.nextLong); + i = i + 1; + pool(i) = Empty(); + case 6 => // CONSTANT_DOUBLE + pool(i) = DoubleConst(in.nextDouble); + i = i + 1; + pool(i) = Empty(); + } + i = i + 1; + } + pool + } + + class PoolEntry; + case class UTF8(str: String) extends PoolEntry; + case class ClassRef(classId: Int) extends PoolEntry; + case class FieldRef(classId: Int, memberId: Int) extends PoolEntry; + case class MethodRef(classId: Int, memberId: Int) extends PoolEntry; + case class IntfMethodRef(classId: Int, memberId: Int) extends PoolEntry; + case class StringConst(strId: Int) extends PoolEntry; + case class IntegerConst(x: Int) extends PoolEntry; + case class FloatConst(x: Float) extends PoolEntry; + case class LongConst(x: Long) extends PoolEntry; + case class DoubleConst(x: Double) extends PoolEntry; + case class NameAndType(nameId: Int, typeId: Int) extends PoolEntry; + case class Empty() extends PoolEntry; + + case class Member(field: Boolean, flags: Int, name: Int, tpe: Int, attribs: List[Attribute]); + case class Attribute(name: Int, data: Array[Byte]) { + + override def toString(): String = pool(name) match { + case UTF8(str: String) => str + } + + def reader: ByteArrayReader = new ByteArrayReader(data); + } + +} diff --git a/sources/scala/tools/scalap/Classfiles.scala b/sources/scala/tools/scalap/Classfiles.scala new file mode 100644 index 0000000000..ea3526131c --- /dev/null +++ b/sources/scala/tools/scalap/Classfiles.scala @@ -0,0 +1,47 @@ +package scalap; + + +object Classfiles { + final val JAVA_MAGIC = 0xCAFEBABE; + final val JAVA_MAJOR_VERSION = 45; + final val JAVA_MINOR_VERSION = 3; + + final val CONSTANT_UTF8 = 1; + final val CONSTANT_UNICODE = 2; + final val CONSTANT_INTEGER = 3; + final val CONSTANT_FLOAT = 4; + final val CONSTANT_LONG = 5; + final val CONSTANT_DOUBLE = 6; + final val CONSTANT_CLASS = 7; + final val CONSTANT_STRING = 8; + final val CONSTANT_FIELDREF = 9; + final val CONSTANT_METHODREF = 10; + final val CONSTANT_INTFMETHODREF = 11; + final val CONSTANT_NAMEANDTYPE = 12; + + final val BAD_ATTR = 0x00000; + final val SOURCEFILE_ATTR = 0x00001; + final val SYNTHETIC_ATTR = 0x00002; + final val DEPRECATED_ATTR = 0x00004; + final val CODE_ATTR = 0x00008; + final val EXCEPTIONS_ATTR = 0x00010; + final val CONSTANT_VALUE_ATTR = 0x00020; + final val LINE_NUM_TABLE_ATTR = 0x00040; + final val LOCAL_VAR_TABLE_ATTR = 0x00080; + final val INNERCLASSES_ATTR = 0x08000; + final val META_ATTR = 0x10000; + final val SCALA_ATTR = 0x20000; + + final val SOURCEFILE_N = "SourceFile"; + final val SYNTHETIC_N = "Synthetic"; + final val DEPRECATED_N = "Deprecated"; + final val CODE_N = "Code"; + final val EXCEPTIONS_N = "Exceptions"; + final val CONSTANT_VALUE_N = "ConstantValue"; + final val LINE_NUM_TABLE_N = "LineNumberTable"; + final val LOCAL_VAR_TABLE_N = "LocalVariableTable"; + final val INNERCLASSES_N = "InnerClasses"; + final val META_N = "JacoMeta"; + final val SCALA_N = "ScalaSignature"; + final val CONSTR_N = ""; +} diff --git a/sources/scala/tools/scalap/CodeWriter.scala b/sources/scala/tools/scalap/CodeWriter.scala new file mode 100644 index 0000000000..d0f5bb99a8 --- /dev/null +++ b/sources/scala/tools/scalap/CodeWriter.scala @@ -0,0 +1,131 @@ +package scalap; + +import java.io._; + + +class CodeWriter(writer: Writer) { + + private val nl = System.getProperty("line.separator"); + private var step = " "; + private var level = 0; + private var align = false; + private var space = false; + private var line = false; + + def getWriter = writer; + + def getIndentLevel = level; + + def setIndentLevel(level: Int): CodeWriter = { + this.level = level; + this + } + + def getIndentWidth = if (step == null) -1 else step.length(); + + def setIndentWidth(width: Int): CodeWriter = { + val buffer = new StringBuffer(width); + var i = 0; + while (i < width) + buffer.append(' '); + setIndentString(buffer.toString()) + } + + def getIndentString = step; + + def setIndentString(step: String): CodeWriter = { + this.step = step; + this + } + + def indent: CodeWriter = { + level = level + 1; + this + } + + def undent: CodeWriter = { + level = level - 1; + this + } + + def newline: CodeWriter = { + if (step == null) + newspace; + else if (!line) { + try { + writer.write(nl); + } catch { + case e => error("IO error") + } + line = align; + align = true; + space = false; + this + } else + this + } + + def newspace: CodeWriter = { + space = !align; + this + } + + def * : Unit = {} + + def println: CodeWriter = newline; + + def println(value: Boolean): CodeWriter = print(value).newline; + + def println(value: Byte): CodeWriter = print(value).newline; + + def println(value: Short): CodeWriter = print(value).newline; + + def println(value: Char): CodeWriter = print(value).newline; + + def println(value: Int): CodeWriter = print(value).newline; + + def println(value: Long): CodeWriter = print(value).newline; + + def println(value: Float): CodeWriter = print(value).newline; + + def println(value: Double): CodeWriter = print(value).newline; + + def println(value: String): CodeWriter = print(value).newline; + + def print(value: Boolean): CodeWriter = print(String.valueOf(value)); + + def print(value: Byte): CodeWriter = print(String.valueOf(value)); + + def print(value: Short): CodeWriter = print(String.valueOf(value)); + + def print(value: Char): CodeWriter = print(String.valueOf(value)); + + def print(value: Int): CodeWriter = print(String.valueOf(value)); + + def print(value: Long): CodeWriter = print(String.valueOf(value)); + + def print(value: Float): CodeWriter = print(String.valueOf(value)); + + def print(value: Double): CodeWriter = print(String.valueOf(value)); + + def print(value: String): CodeWriter = try { + if (align) { + var i = 0; + while (i < level) { + writer.write(step); + i = i + 1; + } + } + if (space) + writer.write(" "); + writer.write(value); + align = false; + space = false; + line = false; + this + } catch { + case e => error("IO error") + } + + override def toString(): String = writer.toString(); +} diff --git a/sources/scala/tools/scalap/Entity.scala b/sources/scala/tools/scalap/Entity.scala new file mode 100644 index 0000000000..6475894845 --- /dev/null +++ b/sources/scala/tools/scalap/Entity.scala @@ -0,0 +1,123 @@ +package scalap; + +import java.io._; +import scala.collection.mutable._; + + +/** Entities are either text, symbols, or types. + */ +trait Entity { + def isText: Boolean = false; + def isType: Boolean = false; + def isSymbol: Boolean = false; + def toSource: String = toString(); +} + +/** Text refers to a single string. + */ +case class Text(str: String) extends Entity { + override def isText: Boolean = true; + override def toString(): String = str; +} + +/** Types + */ +trait Type extends Entity { + override def isType: Boolean = true; + override def toSource: String = { + val writer = new ScalaWriter(new StringWriter()); + writer.setIndentString(null)*; + writer.printType(this); + writer.toString() + } +} + +case object NoType extends Type; + +case class ThisType(sym: Symbol) extends Type; + +case class SingletonType(tpe: Type, sym: Symbol) extends Type; + +case class TypeRef(tpe: Type, sym: Symbol, args: List[Type]) extends Type; + +case class CompoundType(clazz: Symbol, components: List[Type]) extends Type; + +case class MethodType(argtpe: List[Type], restpe: Type) extends Type; + +case class PolyType(tpe: Type, tvars: List[Symbol]) extends Type; + +case class OverloadedType(members: List[Symbol], tpes: List[Type]) extends Type; + +case class TypeFlag(tpe: Type, flags: Int) extends Type; + +/** Symbols + */ +abstract case class Symbol(name: String, flags: Int) extends Entity { + var tpe: Type = NoType; + var owner: Symbol = NoSymbol; + def fullname: String = owner match { + case s: ClassSymbol => { + val prefix = s.fullname; + if (prefix.length() == 0) name else (prefix + "." + name) + } + case s: ExternalSymbol => { + val prefix = s.fullname; + if (prefix.length() == 0) name else (prefix + "." + name) + } + case _ => name + } + def fix(tpe: Type, owner: Symbol): Unit = { + this.tpe = tpe; + this.owner = owner; + owner.enter(this); + } + override def isSymbol: Boolean = true; + override def toString(): String = name; + def fix(tpe: Type): Unit = {} + def fix(sym: Symbol): Unit = {} + def enter(sym: Symbol): Unit = {} + def members: Buffer[Symbol] = error("symbol does not have members"); +} + +object NoSymbol extends Symbol("", 0) { + override def fix(tpe: Type, owner: Symbol): Unit = {} +} + +class TypeSymbol(name: String, flags: Int) extends Symbol(name, flags) { + var lower: Type = NoType; + override def fix(tpe: Type): Unit = { + lower = tpe; + } +} + +class AliasSymbol(name: String, flags: Int) extends Symbol(name, flags) { + var constr: Symbol = NoSymbol; + override def fix(sym: Symbol): Unit = { + constr = sym; + } +} + +class ClassSymbol(name: String, flags: Int) extends Symbol(name, flags) { + var thistpe: Type = NoType; + var constr: Symbol = NoSymbol; + var scope: Buffer[Symbol] = new Buffer; + override def fix(tpe: Type): Unit = { + thistpe = tpe; + } + override def fix(sym: Symbol): Unit = { + constr = sym; + } + override def enter(sym: Symbol): Unit = scope += sym; + override def members: Buffer[Symbol] = scope; +} + +class ValSymbol(name: String, flags: Int) extends Symbol(name, flags) { + var clazz: Symbol = NoSymbol; + override def fix(sym: Symbol): Unit = { + clazz = sym; + } +} + +class ExternalSymbol(name: String, mod: Boolean) extends Symbol(name, 0) { + override def fix(sym: Symbol): Unit = { owner = sym; } +} diff --git a/sources/scala/tools/scalap/EntityTable.scala b/sources/scala/tools/scalap/EntityTable.scala new file mode 100644 index 0000000000..77a7decf84 --- /dev/null +++ b/sources/scala/tools/scalap/EntityTable.scala @@ -0,0 +1,141 @@ +package scalap; + +import scala.collection.mutable._; + + +class EntityTable(attrib: ScalaAttribute) { + import attrib._; + + val table: Array[Entity] = new Array(attrib.table.length); + var root: Buffer[Symbol] = new Buffer; + + { + //Console.println("created table"); + var i = 0; + while (i < attrib.table.length) { + table(i) = attrib.table(i) match { + case TermName(str) => Text(Names.decode(str)); + case TypeName(str) => Text(Names.decode(str)); + case _ => null; + } + i = i + 1; + } + //Console.println("decoded names"); + i = 0; + var fixupIds: List[Int] = Nil; + while (i < attrib.table.length) { + table(i) = attrib.table(i) match { + case NoneSym() => + NoSymbol + case TypeSym(SymbolInfo(nameId, _, flags, _), _) => + fixupIds = i :: fixupIds; + new TypeSymbol(getText(nameId), flags) + case AliasSym(SymbolInfo(nameId, _, flags, _), _) => + fixupIds = i :: fixupIds; + new AliasSymbol(getText(nameId), flags) + case ClassSym(SymbolInfo(nameId, _, flags, _), _, _) => + fixupIds = i :: fixupIds; + new ClassSymbol(getText(nameId), flags) + case ValSym(SymbolInfo(nameId, _, flags, _), _) => + fixupIds = i :: fixupIds; + new ValSymbol(getText(nameId), flags) + case ExtRef(mod, nameId, _) => + fixupIds = i :: fixupIds; + new ExternalSymbol(getText(nameId), mod) + case _ => + table(i) + } + i = i + 1; + } + //Console.println("created symbols"); + i = 0; + while (i < attrib.table.length) { + val x = getType(i); + i = i + 1; + } + //Console.println("created types"); + def fix(i: Int, info: SymbolInfo): Symbol = { + val sym = getSymbol(i); + sym.fix(getType(info.info), getSymbol(info.owner)); + sym + } + fixupIds foreach { + i => attrib.table(i) match { + case TypeSym(info, loId) => + fix(i, info).fix(getType(loId)); + case AliasSym(info, constrId) => + fix(i, info).fix(getSymbol(constrId)); + case ClassSym(info, typeId, constrId) => + val sym = fix(i, info); + sym.fix(getType(typeId)); + sym.fix(getSymbol(constrId)); + sym.owner match { + case x: ExternalSymbol => root += sym; + case _ => + } + case ValSym(info, classId) => + fix(i, info).fix(getSymbol(classId)); + case ExtRef(_, _, ownerId) => + getSymbol(i).fix(getSymbol(ownerId)); + } + } + } + + def getText(i: Int): String = table(i) match { + case Text(str) => str; + } + + def getSymbol(i: Int): Symbol = + if (i < 0) NoSymbol else table(i).asInstanceOf[Symbol]; + + def getSymbols(is: List[Int]): List[Symbol] = is map {i => getSymbol(i)}; + + def getType(i: Int): Type = { + if (i < 0) + NoType + else if (table(i) != null) { + if (table(i).isInstanceOf[Type]) + table(i).asInstanceOf[Type] + else + NoType + } else { + val res: Type = attrib.table(i) match { + case NoneType() => + NoType + case SelfType(symId) => + ThisType(getSymbol(symId)) + case SingleType(typeId, symId) => + SingletonType(getType(typeId), getSymbol(symId)) + case TypeReference(typeId, symId, argIds) => + TypeRef(getType(typeId), getSymbol(symId), getTypes(argIds)) + case CompoundTypeRef(symId, typeIds) => + CompoundType(getSymbol(symId), getTypes(typeIds)) + case MethodTypeRef(restypeId, argtypeIds) => + MethodType(getTypes(argtypeIds), getType(restypeId)) + case PolyTypeRef(typeId, symIds) => + PolyType(getType(typeId), getSymbols(symIds)) + case OverloadedTypeRef(ids) => + val Pair(symIds, typeIds) = ids partition { + i => (table(i) != null) && table(i).isSymbol + } + OverloadedType(getSymbols(symIds), getTypes(typeIds)) + case FlaggedType(flags, typeId) => + TypeFlag(getType(typeId), flags) + } + table(i) = res; + res + } + } + + def getTypes(is: List[Int]): List[Type] = is map {i => getType(i)}; + + def print: Unit = { + Console.println("ROOT = " + root); + var i = 0; + while (i < table.length) { + Console.println("" + i + ": " + table(i).toSource); + Console.println(" " + table(i)); + i = i + 1; + } + } +} diff --git a/sources/scala/tools/scalap/FileCache.scala b/sources/scala/tools/scalap/FileCache.scala new file mode 100644 index 0000000000..9021d0ab98 --- /dev/null +++ b/sources/scala/tools/scalap/FileCache.scala @@ -0,0 +1,37 @@ +// FileCache +// 20-Mar-2002, Matthias Zenger + +package scalap; + +import scala.collection._; +import java.io._; +import java.util.jar._; + + +class FileCache { + + /** table of all opened jar-files + */ + protected val opened: mutable.Map[String, AbstractFile] = + //new mutable.HashMap[String, AbstractFile]; + new mutable.JavaMapAdaptor(new java.util.HashMap()); + + def open(name: String): AbstractFile = open(null, name); + + /** open file 'name' in directory 'dirname'; 'name' is a path + * relative to 'dirname'; 'dirname' might also refer to a .zip + * or .jar file + */ + def open(dirname: String, name: String): AbstractFile = { + if (dirname == null) + new PlainFile(new File(name)) + else if (dirname.endsWith(".jar")) { + if (!opened.isDefinedAt(dirname)) + opened(dirname) = new JarArchive(new File(dirname)); + if (name == null) opened(dirname) else opened(dirname).open(name) + } else if (name == null) + new PlainFile(new File(dirname)) + else + new PlainFile(new File(dirname, name)) + } +} diff --git a/sources/scala/tools/scalap/Flags.scala b/sources/scala/tools/scalap/Flags.scala new file mode 100644 index 0000000000..004228340b --- /dev/null +++ b/sources/scala/tools/scalap/Flags.scala @@ -0,0 +1,112 @@ +package scalap; + +object Flags { + + final val DEFERRED = 0x00000001; + final val FINAL = 0x00000002; + final val PRIVATE = 0x00000004; + final val PROTECTED = 0x00000008; + + final val SEALED = 0x00000010; + final val OVERRIDE = 0x00000020; + final val CASE = 0x00000040; + final val ABSTRACT = 0x00000080; // abstract class + + final val DEF = 0x00000100; // a def parameter + final val REPEATED = 0x00000200; // a repeated parameter + final val SYNTHETIC = 0x00000400; + final val DEPRECATED = 0x00000800; + + final val JAVA = 0x00001000; // symbol was defined by a Java class + final val OBJECT = 0x00002000; // symbol is module or class implementing a module + final val MUTABLE = 0x00004000; // symbol is a mutable variable. + final val PARAM = 0x00008000; // symbol is a (type) parameter to a method + + final val INITIALIZED = 0x00010000; // symbol's definition is complete + final val LOCKED = 0x00020000; // temporary flag to catch cyclic dependencies + final val ACCESSED = 0x00040000; // symbol was accessed at least once + final val SELECTOR = 0x00080000; // symbol was used as selector in Select + + final val PACKAGE = 0x00100000; // symbol is a java package. + final val LABEL = 0x00200000; // symbol is a label symbol + final val STATIC = 0x00400000; // "static" inner classes (i.e. after class norm.) + final val STABLE = 0x00800000; // functions that are assumed to be stable + // (typically, access methods for valdefs) + + final val CAPTURED = 0x01000000; // variables is accessed from nested function. + final val CASEACCESSOR = 0x02000000; // function is a case constructor + + final val ACCESSOR = 0x04000000; // function is an access function for a + // value or variable + final val BRIDGE = 0x08000000; // function is a bridge method. + + final val INTERFACE = 0x10000000; // symbol is a Java interface + final val TRAIT = 0x20000000; // symbol is a Trait + + final val COVAR = 0x40000000; // symbol is a covariant type variable + final val CONTRAVAR = 0x80000000; // symbol is a contravariant type variable + + // masks + final val SOURCEFLAGS = 0x00000077 + | DEF | REPEATED | OBJECT | MUTABLE + | PACKAGE | PARAM | TRAIT | COVAR + | CONTRAVAR; // these modifiers can be set in source programs + final val ACCESSFLAGS = PRIVATE | PROTECTED; + final val VARIANCES = COVAR | CONTRAVAR; + + def isDeferred(flags: Int): Boolean = (flags & DEFERRED) != 0; + + def isAbstract(flags: Int): Boolean = (flags & ABSTRACT) != 0; + + def isFinal(flags: Int): Boolean = (flags & FINAL) != 0; + + def isPrivate(flags: Int): Boolean = (flags & PRIVATE) != 0; + + def isProtected(flags: Int): Boolean = (flags & PROTECTED) != 0; + + def isSealed(flags: Int): Boolean = (flags & SEALED) != 0; + + def isOverride(flags: Int): Boolean = (flags & OVERRIDE) != 0; + + def isCase(flags: Int): Boolean = (flags & CASE) != 0; + + def isCaseAccessor(flags: Int): Boolean = (flags & CASEACCESSOR) != 0; + + def isInterface(flags: Int): Boolean = (flags & INTERFACE) != 0; + + def isTrait(flags: Int): Boolean = (flags & TRAIT) != 0; + + def isObj(flags: Int): Boolean = (flags & OBJECT) != 0; + + def isDef(flags: Int): Boolean = (flags & DEF) != 0; + + def isObjClass(flags: Int): Boolean = (flags & OBJECT) != 0; + + def isStatic(flags: Int): Boolean = (flags & STATIC) != 0; + + def isJava(flags: Int): Boolean = (flags & JAVA) != 0; + + def isNoVal(flags: Int): Boolean = (flags & PACKAGE) != 0; + + def toString(flags: Int): String = { + val buffer = new StringBuffer(); + var x: StringBuffer = buffer; + if (isPrivate(flags)) + x = buffer.append("private "); + if (isProtected(flags)) + x = buffer.append("protected "); + if (isAbstract(flags) && !isTrait(flags)) + x = buffer.append("abstract "); + if (isFinal(flags) && !isObj(flags)) + x = buffer.append("final "); + if (isSealed(flags)) + x = buffer.append("sealed "); + if (isCase(flags)) + x = buffer.append("case "); + if (isDef(flags)) + x = buffer.append("def "); + if (isOverride(flags)) + x = buffer.append("override "); + buffer.toString() + } +} diff --git a/sources/scala/tools/scalap/Main.scala b/sources/scala/tools/scalap/Main.scala new file mode 100644 index 0000000000..bd44bfb732 --- /dev/null +++ b/sources/scala/tools/scalap/Main.scala @@ -0,0 +1,46 @@ +// ClassPath +// 04-Mar-2002, Matthias Zenger + +package scalap; + +import java.io._; +import scala.collection._; + + +object Main { + + def usage: Unit = { + Console.println("usage: scalap "); + } + + def main(args: Array[String]) = { + if (args.length == 0) + usage; + else { + val path = new ClassPath; + val file = path.openClass(Names.encode(args(0))); + if (file.exists) { + val reader = new ByteArrayReader(file.content); + val clazz = new Classfile(reader); + val attrib = clazz.attribs.find(a => a.toString() == "ScalaSignature"); + attrib match { + case Some(a) => + val info = new ScalaAttribute(a.reader); + //Console.println("read attribute"); + val symtab = new EntityTable(info); + //Console.println("read entities"); + //symtab.print; + val out = new OutputStreamWriter(System.out); + val writer = new ScalaWriter(out); + symtab.root.elements foreach ( + sym => { writer.printSymbol(sym); + writer.println*; }); + out.flush(); + case None => + Console.println("Java classes not supported yet."); + } + } else + Console.println("class/object not found."); + } + } +} diff --git a/sources/scala/tools/scalap/Names.scala b/sources/scala/tools/scalap/Names.scala new file mode 100644 index 0000000000..bc3d79eba1 --- /dev/null +++ b/sources/scala/tools/scalap/Names.scala @@ -0,0 +1,85 @@ +package scalap; + +object Names { + + val operatorName = new Array[String](128); + operatorName('$') = "$"; + operatorName('~') = "$tilde"; + operatorName('=') = "$eq"; + operatorName('<') = "$less"; + operatorName('>') = "$greater"; + operatorName('!') = "$bang"; + operatorName('#') = "$hash"; + operatorName('%') = "$percent"; + operatorName('^') = "$up"; + operatorName('&') = "$amp"; + operatorName('|') = "$bar"; + operatorName('*') = "$times"; + operatorName('/') = "$div"; + operatorName('+') = "$plus"; + operatorName('-') = "$minus"; + operatorName(':') = "$colon"; + + /** Replace operator symbols by corresponding "$op_name" in names. + */ + def encode(name: String): String = { + var i = 0; + val len = name.length(); + val res = new StringBuffer(); + while (i < len) { + val c = name.charAt(i); + if (c < 128) { + val nop = operatorName(c); + if (nop == null) + res.append(c); + else + res.append(nop); + } else + res.append(c); + i = i + 1; + } + res.toString() + } + + /** Replace "$op_name" by corresponding operator symbols in names. + */ + def decode(name: String): String = { + var i = 0; + val len = name.length(); + val res = new StringBuffer(); + while (i < len) { + val c = name.charAt(i); + if (c == '$') { + var j = len; + while (j > i) { + val prefix = name.substring(i, j); + val c = lookup(prefix); + if (c != null) { + i = j; + res.append(c); + } else + j = j - 1; + } + } else { + i = i + 1; + res.append(c); + } + } + res.toString() + } + + /** Looks up the array entry for the operator name. + */ + def lookup(string: String): String = { + var i = 0; + var res: String = null; + while (i < 128) { + if (string.equals(operatorName(i))) { + res = String.valueOf(i.asInstanceOf[Char]); + i = 128; + } + i = i + 1; + } + res + } +} diff --git a/sources/scala/tools/scalap/ScalaAttribute.scala b/sources/scala/tools/scalap/ScalaAttribute.scala new file mode 100644 index 0000000000..f4aa413b52 --- /dev/null +++ b/sources/scala/tools/scalap/ScalaAttribute.scala @@ -0,0 +1,95 @@ +package scalap; + +import scala.collection.mutable._; + + +class ScalaAttribute(in: ByteArrayReader) { + + val table = readTable; + + def readTable: Array[AttribEntry] = { + val res = new Array[AttribEntry](in.nextNat); + var i = 0; + while (i < res.length) { + res(i) = readTableEntry; + i = i + 1; + } + res + } + + def readTableEntry: AttribEntry = { + val tag = in.nextByte; + val len = in.nextNat; + //Console.println("" + tag + ": " + len); + val end = in.bp + len; + tag match { + case 1 /* TERMname */ => + TermName(in.nextUTF8(len)) + case 2 /* TYPEname */ => + TypeName(in.nextUTF8(len)) + case 3 /* NONEsym */ => + NoneSym() + case 4 /* TYPEsym */ => + TypeSym(readSymInfo, in.nextNat) + case 5 /* ALIASsym */ => + AliasSym(readSymInfo, in.nextNat) + case 6 /* CLASSsym */ => + ClassSym(readSymInfo, in.nextNat, in.nextNat) + case 7 /* VALsym */ => + ValSym(readSymInfo, if (in.bp < end) in.nextNat else -1) + case 8 /* EXTref */ => + ExtRef(false, in.nextNat, if (in.bp < end) in.nextNat else -1) + case 9 /* EXTMODCLASSref */ => + ExtRef(true, in.nextNat, if (in.bp < end) in.nextNat else -1) + case 10 /* NOtpe */ => + NoneType() + case 11 /* THIStpe */ => + SelfType(in.nextNat) + case 12 /* SINGLEtpe */ => + SingleType(in.nextNat, in.nextNat) + case 13 /* TYPEREFtpe */ => + TypeReference(in.nextNat, in.nextNat, readRefs(end)) + case 14 /* COMPOUNDtpe */ => + CompoundTypeRef(in.nextNat, readRefs(end)) + case 15 /* METHODtpe */ => + MethodTypeRef(in.nextNat, readRefs(end)) + case 16 /* POLYtpe */ => + PolyTypeRef(in.nextNat, readRefs(end)) + case 17 /* OVERLOADEDtpe */ => + OverloadedTypeRef(readRefs(end)) + case 20 /* FLAGGEDtpe */ => + FlaggedType(in.nextNat, in.nextNat) + } + } + + def readSymInfo: SymbolInfo = + SymbolInfo(in.nextNat, in.nextNat, in.nextNat, in.nextNat); + + def readRefs(end: Int): List[Int] = { + var res = new Buffer[Int]; + while (in.bp < end) + res += in.nextNat; + res.toList + } + + class AttribEntry; + case class TermName(name: String) extends AttribEntry; + case class TypeName(name: String) extends AttribEntry; + case class NoneSym() extends AttribEntry; + case class TypeSym(info: SymbolInfo, lobound: Int) extends AttribEntry; + case class AliasSym(info: SymbolInfo, constr: Int) extends AttribEntry; + case class ClassSym(info: SymbolInfo, thistpe: Int, constr: Int) extends AttribEntry; + case class ValSym(info: SymbolInfo, classsym: Int) extends AttribEntry; + case class ExtRef(mod: Boolean, name: Int, owner: Int) extends AttribEntry; + case class NoneType() extends AttribEntry; + case class SelfType(sym: Int) extends AttribEntry; + case class SingleType(typeref: Int, sym: Int) extends AttribEntry; + case class TypeReference(typeref: Int, sym: Int, args: List[Int]) extends AttribEntry; + case class CompoundTypeRef(sym: Int, components: List[Int]) extends AttribEntry; + case class MethodTypeRef(res: Int, args: List[Int]) extends AttribEntry; + case class PolyTypeRef(tpe: Int, sym: List[Int]) extends AttribEntry; + case class OverloadedTypeRef(symandtpe: List[Int]) extends AttribEntry; + case class FlaggedType(flags: Int, tpe: Int) extends AttribEntry; + + case class SymbolInfo(name: Int, owner: Int, flags: Int, info: Int); +} diff --git a/sources/scala/tools/scalap/ScalaWriter.scala b/sources/scala/tools/scalap/ScalaWriter.scala new file mode 100644 index 0000000000..c829abb892 --- /dev/null +++ b/sources/scala/tools/scalap/ScalaWriter.scala @@ -0,0 +1,219 @@ +package scalap; + +import java.io._; +import scala.collection.mutable._; + + +class ScalaWriter(writer: Writer) extends CodeWriter(writer) { + + def printFlags(flags: Int): Unit = { + val buffer = new StringBuffer(); + var x: StringBuffer = buffer; + if (Flags.isPrivate(flags)) + x = buffer.append("private "); + if (Flags.isProtected(flags)) + x = buffer.append("protected "); + if (Flags.isAbstract(flags) && !Flags.isTrait(flags)) + x = buffer.append("abstract "); + if (Flags.isFinal(flags) && !Flags.isObj(flags)) + x = buffer.append("final "); + if (Flags.isSealed(flags)) + x = buffer.append("sealed "); + if (Flags.isCase(flags)) + x = buffer.append("case "); + if (Flags.isDef(flags)) + x = buffer.append("def "); + if (Flags.isOverride(flags)) + x = buffer.append("override "); + print(buffer.toString())* + } + + def printType(tpe: Type): Unit = { + printType0(tpe); + tpe match { + case ThisType(_) => print(".type")* + case SingletonType(_, _) => print(".type")* + case _ => + } + } + + def printTypes(tpes: List[Type], begin: String, infix: String, end: String): Unit = { + if (!tpes.isEmpty) + printTypes0(tpes, begin, infix, end); + } + + def printTypes0(tpes: List[Type], begin: String, infix: String, end: String): Unit = { + print(begin)*; + if (!tpes.isEmpty) { + printType(tpes.head); + tpes.tail foreach (t => { print(infix)*; printType(t) }); + } + print(end)*; + } + + def printType0(tpe: Type): Unit = tpe match { + case NoType => + case ThisType(sym) => sym match { + case x: ExternalSymbol => print(sym.fullname)* + case NoSymbol => print("this")* + case _ => print(sym.fullname).print(".this")* + } + case SingletonType(tpe, sym) => + printPrefix(tpe); + print(sym.name)* + case TypeRef(tpe, sym, args) => + printPrefix(tpe); + print(sym.name)*; + printTypes(args, "[", ", ", "]"); + case CompoundType(clazz, components) => + printTypes(components, "", " with ", ""); + if (clazz != NoSymbol) + printScope(clazz.members); + case MethodType(_, _) => + var tpe0 = tpe; + while (tpe0.isInstanceOf[MethodType]) { + tpe0 match { + case MethodType(argtpes, restpe) => + printTypes0(argtpes, "(", ", ", ")"); + tpe0 = restpe; + } + } + print(":").newspace*; + printType(tpe0); + case PolyType(tpe, tvars) => + print("[")*; + if (!tvars.isEmpty) { + printTVar(tvars.head); + tvars.tail foreach (sym => {print(", ")*; printTVar(sym);}); + } + print("]")*; + printType(tpe); + case OverloadedType(_, tpes) => + printTypes(tpes, "", " ", ""); + case TypeFlag(TypeRef(_, _, List(tpe0)), flags) => + if ((flags & 8) != 0) + print("def ")*; + printType(tpe0); + if ((flags & 4) != 0) + print("*")*; + case TypeFlag(tpe0, flags) => + if ((flags & 8) != 0) + print("def ")*; + printType(tpe0); + case _ => print("")*; + } + + def printPrefix(tpe: Type): Unit = tpe match { + case NoType => + case ThisType(NoSymbol) => + case ThisType(sym) => + if (sym.name.length() != 0) { + printType0(tpe); + print(".")* + } + case TypeRef(_, _, _) => + printType0(tpe); + print("#")* + case _ => + printType0(tpe); + print(".")* + } + + def printTVar(tvar: Symbol): Unit = tvar match { + case sym: TypeSymbol => print(sym.name); + if (!isExternalType(sym.tpe, "Any")) { + print(" <: ")*; + printType(sym.tpe); + } + if (!isExternalType(sym.lower, "All")) { + print(" >: ")*; + printType(sym.lower); + } + } + + def isExternalType(tpe: Type, name: String): Boolean = tpe match { + case TypeRef(SingletonType(ThisType(root), pck), sym, Nil) => + (root.name.length() == 0) && + pck.name.equals("scala") && + sym.name.equals(name) + case _ => false + } + + def printSymbol(sym: Symbol): Unit = sym match { + case NoSymbol => + print("")* + case s: TypeSymbol => + print(Flags.toString(s.flags))*; + print("type ")*; + printTVar(s); + case s: AliasSymbol => + print(Flags.toString(s.flags))*; + print("type " + s.name + " = ")*; + printType(s.tpe); + case s: ClassSymbol => + print(Flags.toString(s.flags))*; + if (Flags.isDeferred(s.flags)) + print("/*deferred*/ ")*; + if (Flags.isObj(s.flags)) + print("object " + s.name); + else if (Flags.isTrait(s.flags)) + print("trait " + s.name)*; + else + print("class " + s.name)*; + printConstr(s.constr); + print(" extends "); + printType(s.tpe); + case s: ValSymbol => + s.tpe match { + case PolyType(tpe, Nil) => + print("def " + s.name + ": ")*; + printType(tpe); + case PolyType(_, _) => + print("def " + s.name)*; + printType(s.tpe); + case MethodType(_, _) => + print("def " + s.name)*; + printType(s.tpe); + case _ => + print("val " + s.name + ": ")*; + printType(s.tpe); + } + case s: ExternalSymbol => + print("")* + } + + def printConstr(sym: Symbol): Unit = sym match { + case s: ValSymbol => + s.tpe match { + case PolyType(MethodType(argtpes, _), tvars) => + print("[")*; + if (!tvars.isEmpty) { + printTVar(tvars.head); + tvars.tail foreach (sym => {print(", ")*; printTVar(sym);}); + } + print("]")*; + printTypes(argtpes, "(", ", ", ")"); + case MethodType(argtpes, _) => + printTypes(argtpes, "(", ", ", ")"); + case _ => + } + } + + def printScope(scope: Buffer[Symbol]): Unit = { + var first = true; + scope.elements foreach ( + sym => { sym match { + case s: ValSymbol if + (s.tpe.isInstanceOf[OverloadedType] || + (Flags.isCaseAccessor(s.flags) && + !s.tpe.isInstanceOf[PolyType])) => + case _ => + if (first) print(" {").indent* else print(";")*; + first = false; + newline*; + printSymbol(sym); + }}); + if (!first) + newline.undent.print("}")* + } +} -- cgit v1.2.3