From dbeab9b86fe2ef4fe9fc9aa0dee0db0e04945026 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 16 Jul 2011 09:53:47 +0000 Subject: Changes to reflection. --- .../scala/reflect/internal/AnnotationInfos.scala | 1 + .../scala/reflect/internal/ByteCodecs.scala | 216 --------------------- .../scala/reflect/internal/Definitions.scala | 3 + src/compiler/scala/reflect/internal/Phase.scala | 5 + .../scala/reflect/internal/SymbolTable.scala | 2 +- .../reflect/internal/pickling/ByteCodecs.scala | 3 + .../scala/reflect/runtime/JavaConversions.scala | 196 ++++++++++++++----- src/compiler/scala/reflect/runtime/Loaders.scala | 87 +++++++++ src/compiler/scala/reflect/runtime/Settings.scala | 4 + src/compiler/scala/reflect/runtime/Universe.scala | 41 ++-- .../tools/nsc/symtab/classfile/ICodeReader.scala | 3 +- .../scala/tools/nsc/symtab/classfile/package.scala | 7 + 12 files changed, 280 insertions(+), 288 deletions(-) delete mode 100644 src/compiler/scala/reflect/internal/ByteCodecs.scala create mode 100644 src/compiler/scala/reflect/runtime/Loaders.scala create mode 100644 src/compiler/scala/tools/nsc/symtab/classfile/package.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/AnnotationInfos.scala b/src/compiler/scala/reflect/internal/AnnotationInfos.scala index 7f78d3a470..62b27c2daf 100644 --- a/src/compiler/scala/reflect/internal/AnnotationInfos.scala +++ b/src/compiler/scala/reflect/internal/AnnotationInfos.scala @@ -7,6 +7,7 @@ package scala.reflect package internal import util._ +import pickling.ByteCodecs /** AnnotationInfo and its helpers */ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => diff --git a/src/compiler/scala/reflect/internal/ByteCodecs.scala b/src/compiler/scala/reflect/internal/ByteCodecs.scala deleted file mode 100644 index 8b0fc9300a..0000000000 --- a/src/compiler/scala/reflect/internal/ByteCodecs.scala +++ /dev/null @@ -1,216 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2007-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ -package scala.reflect.internal - -object ByteCodecs { - - def avoidZero(src: Array[Byte]): Array[Byte] = { - var i = 0 - val srclen = src.length - var count = 0 - while (i < srclen) { - if (src(i) == 0x7f) count += 1 - i += 1 - } - val dst = new Array[Byte](srclen + count) - i = 0 - var j = 0 - while (i < srclen) { - val in = src(i) - if (in == 0x7f) { - dst(j) = (0xc0).toByte - dst(j + 1) = (0x80).toByte - j += 2 - } else { - dst(j) = (in + 1).toByte - j += 1 - } - i += 1 - } - dst - } - - def regenerateZero(src: Array[Byte]): Int = { - var i = 0 - val srclen = src.length - var j = 0 - while (i < srclen) { - val in: Int = src(i) & 0xff - if (in == 0xc0 && (src(i + 1) & 0xff) == 0x80) { - src(j) = 0x7f - i += 2 - } else { - src(j) = (in - 1).toByte - i += 1 - } - j += 1 - } - j - } - - def encode8to7(src: Array[Byte]): Array[Byte] = { - val srclen = src.length - val dstlen = (srclen * 8 + 6) / 7 - val dst = new Array[Byte](dstlen) - var i = 0 - var j = 0 - while (i + 6 < srclen) { - var in: Int = src(i) & 0xff - dst(j) = (in & 0x7f).toByte - var out: Int = in >>> 7 - in = src(i + 1) & 0xff - dst(j + 1) = (out | (in << 1) & 0x7f).toByte - out = in >>> 6 - in = src(i + 2) & 0xff - dst(j + 2) = (out | (in << 2) & 0x7f).toByte - out = in >>> 5 - in = src(i + 3) & 0xff - dst(j + 3) = (out | (in << 3) & 0x7f).toByte - out = in >>> 4 - in = src(i + 4) & 0xff - dst(j + 4) = (out | (in << 4) & 0x7f).toByte - out = in >>> 3 - in = src(i + 5) & 0xff - dst(j + 5) = (out | (in << 5) & 0x7f).toByte - out = in >>> 2 - in = src(i + 6) & 0xff - dst(j + 6) = (out | (in << 6) & 0x7f).toByte - out = in >>> 1 - dst(j + 7) = out.toByte - i += 7 - j += 8 - } - if (i < srclen) { - var in: Int = src(i) & 0xff - dst(j) = (in & 0x7f).toByte; j += 1 - var out: Int = in >>> 7 - if (i + 1 < srclen) { - in = src(i + 1) & 0xff - dst(j) = (out | (in << 1) & 0x7f).toByte; j += 1 - out = in >>> 6 - if (i + 2 < srclen) { - in = src(i + 2) & 0xff - dst(j) = (out | (in << 2) & 0x7f).toByte; j += 1 - out = in >>> 5 - if (i + 3 < srclen) { - in = src(i + 3) & 0xff - dst(j) = (out | (in << 3) & 0x7f).toByte; j += 1 - out = in >>> 4 - if (i + 4 < srclen) { - in = src(i + 4) & 0xff - dst(j) = (out | (in << 4) & 0x7f).toByte; j += 1 - out = in >>> 3 - if (i + 5 < srclen) { - in = src(i + 5) & 0xff - dst(j) = (out | (in << 5) & 0x7f).toByte; j += 1 - out = in >>> 2 - } - } - } - } - } - if (j < dstlen) dst(j) = out.toByte - } - dst - } - - @deprecated("use 2-argument version instead", "2.8.0") - def decode7to8(src: Array[Byte], srclen: Int, dstlen: Int) { decode7to8(src, srclen) } - - def decode7to8(src: Array[Byte], srclen: Int): Int = { - var i = 0 - var j = 0 - val dstlen = (srclen * 7 + 7) / 8 - while (i + 7 < srclen) { - var out: Int = src(i) - var in: Byte = src(i + 1) - src(j) = (out | (in & 0x01) << 7).toByte - out = in >>> 1 - in = src(i + 2) - src(j + 1) = (out | (in & 0x03) << 6).toByte - out = in >>> 2 - in = src(i + 3) - src(j + 2) = (out | (in & 0x07) << 5).toByte - out = in >>> 3 - in = src(i + 4) - src(j + 3) = (out | (in & 0x0f) << 4).toByte - out = in >>> 4 - in = src(i + 5) - src(j + 4) = (out | (in & 0x1f) << 3).toByte - out = in >>> 5 - in = src(i + 6) - src(j + 5) = (out | (in & 0x3f) << 2).toByte - out = in >>> 6 - in = src(i + 7) - src(j + 6) = (out | in << 1).toByte - i += 8 - j += 7 - } - if (i < srclen) { - var out: Int = src(i) - if (i + 1 < srclen) { - var in: Byte = src(i + 1) - src(j) = (out | (in & 0x01) << 7).toByte; j += 1 - out = in >>> 1 - if (i + 2 < srclen) { - in = src(i + 2) - src(j) = (out | (in & 0x03) << 6).toByte; j += 1 - out = in >>> 2 - if (i + 3 < srclen) { - in = src(i + 3) - src(j) = (out | (in & 0x07) << 5).toByte; j += 1 - out = in >>> 3 - if (i + 4 < srclen) { - in = src(i + 4) - src(j) = (out | (in & 0x0f) << 4).toByte; j += 1 - out = in >>> 4 - if (i + 5 < srclen) { - in = src(i + 5) - src(j) = (out | (in & 0x1f) << 3).toByte; j += 1 - out = in >>> 5 - if (i + 6 < srclen) { - in = src(i + 6) - src(j) = (out | (in & 0x3f) << 2).toByte; j += 1 - out = in >>> 6 - } - } - } - } - } - } - if (j < dstlen) src(j) = out.toByte - } - dstlen - } - - def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs)) - - @deprecated("use 1-argument version instead", "2.8.0") - def decode(xs: Array[Byte], dstlen: Int) { decode(xs) } - - /** - * Destructively decodes array xs and returns the length of the decoded array. - * - * Sometimes returns (length+1) of the decoded array. Example: - * - * scala> val enc = reflect.generic.ByteCodecs.encode(Array(1,2,3)) - * enc: Array[Byte] = Array(2, 5, 13, 1) - * - * scala> reflect.generic.ByteCodecs.decode(enc) - * res43: Int = 4 - * - * scala> enc - * res44: Array[Byte] = Array(1, 2, 3, 0) - * - * However, this does not always happen. - */ - def decode(xs: Array[Byte]): Int = { - val len = regenerateZero(xs) - decode7to8(xs, len) - } -} diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 500a5cb428..314fe1ed82 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -647,6 +647,8 @@ trait Definitions extends reflect.api.StandardDefinitions { var j = fullname.pos('.', i) while (j < fullname.length) { sym = sym.info.member(fullname.subName(i, j)) + // if (sym == NoSymbol) + // println("no member "+fullname.subName(i, j)+" found in "+sym0+sym0.info.getClass+" "+sym0.info.typeSymbol.info.getClass) i = j + 1 j = fullname.pos('.', i) } @@ -654,6 +656,7 @@ trait Definitions extends reflect.api.StandardDefinitions { if (module) sym.info.member(fullname.subName(i, j)).suchThat(_ hasFlag MODULE) else sym.info.member(fullname.subName(i, j).toTypeName) if (result == NoSymbol) { + // println("no member "+fullname.subName(i, j)+" found in "+sym+" "+module) if (settings.debug.value) { log(sym.info); log(sym.info.members) }//debug throw new MissingRequirementError((if (module) "object " else "class ") + fullname) diff --git a/src/compiler/scala/reflect/internal/Phase.scala b/src/compiler/scala/reflect/internal/Phase.scala index f65af1eaae..996fd423d0 100644 --- a/src/compiler/scala/reflect/internal/Phase.scala +++ b/src/compiler/scala/reflect/internal/Phase.scala @@ -51,3 +51,8 @@ object NoPhase extends Phase(null) { def name = "" def run() { throw new Error("NoPhase.run") } } + +object SomePhase extends Phase(NoPhase) { + def name = "" + def run() { throw new Error("SomePhase.run") } +} diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index 32fa88ac15..81c4cef4d4 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -50,7 +50,7 @@ abstract class SymbolTable extends api.Universe final val NoRunId = 0 private var ph: Phase = NoPhase - protected var per = NoPeriod + private var per = NoPeriod final def phase: Phase = ph diff --git a/src/compiler/scala/reflect/internal/pickling/ByteCodecs.scala b/src/compiler/scala/reflect/internal/pickling/ByteCodecs.scala index 0a02d09a71..1add69ddd4 100644 --- a/src/compiler/scala/reflect/internal/pickling/ByteCodecs.scala +++ b/src/compiler/scala/reflect/internal/pickling/ByteCodecs.scala @@ -44,6 +44,9 @@ object ByteCodecs { if (in == 0xc0 && (src(i + 1) & 0xff) == 0x80) { src(j) = 0x7f i += 2 + } else if (in == 0) { + src(j) = 0x7f + i += 1 } else { src(j) = (in - 1).toByte i += 1 diff --git a/src/compiler/scala/reflect/runtime/JavaConversions.scala b/src/compiler/scala/reflect/runtime/JavaConversions.scala index b724292836..2ba648cec5 100644 --- a/src/compiler/scala/reflect/runtime/JavaConversions.scala +++ b/src/compiler/scala/reflect/runtime/JavaConversions.scala @@ -5,10 +5,10 @@ import java.lang.{Class => jClass, Package => jPackage} import java.lang.reflect.{ Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField, Member => jMember, Type => jType, GenericDeclaration} -import internal.ByteCodecs +import internal.pickling.ByteCodecs import internal.ClassfileConstants._ import internal.pickling.UnPickler -import scala.collection.{ mutable, immutable } +import collection.mutable.HashMap trait JavaConversions { self: Universe => @@ -16,9 +16,12 @@ trait JavaConversions { self: Universe => val global: JavaConversions.this.type = self } - class TwoWayCache[J, S] { - val toScalaMap = mutable.HashMap[J, S]() - val toJavaMap = mutable.HashMap[S, J]() + /** A cache that maintains a bijection between Java reflection type `J` + * and Scala reflection type `S`. + */ + private class TwoWayCache[J, S] { + private val toScalaMap = new HashMap[J, S] + private val toJavaMap = new HashMap[S, J] def toScala(key: J)(body: => S) = toScalaMap.getOrElseUpdate(key, body) def toJava(key: S)(body: => J) = toJavaMap.getOrElseUpdate(key, body) @@ -30,27 +33,63 @@ trait JavaConversions { self: Universe => private val constructorCache = new TwoWayCache[jConstructor[_], Symbol] private val fieldCache = new TwoWayCache[jField, Symbol] - private def createClassModule(owner: Symbol, name: TypeName) = { - val clazz = owner.newClass(NoPosition, name) - val module = owner.newModule(NoPosition, name) - if (owner.isClass) { - owner.info.decls enter clazz - owner.info.decls enter module + /** Generate types for top-level Scala root class and root companion object + * from the pickled information stored in a corresponding Java class + * @param clazz The top-level Scala class for which info is unpickled + * @param module The top-level Scala companion object for which info is unpickled + * @param jclazz The Java class which contains the unpickled information in a + * ScalaSignature or ScalaLongSignature annotation. + */ + def unpickleClass(clazz: Symbol, module: Symbol, jclazz: jClass[_]): Unit = { + println("unpickling "+clazz+" "+module) + val ssig = jclazz.getAnnotation(classOf[scala.reflect.ScalaSignature]) + if (ssig != null) { + val bytes = ssig.bytes.getBytes + val len = ByteCodecs.decode(bytes) + unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName) + } else { + val slsig = jclazz.getAnnotation(classOf[scala.reflect.ScalaLongSignature]) + if (slsig != null) { + val byteSegments = slsig.bytes map (_.getBytes) + val lens = byteSegments map ByteCodecs.decode + val bytes = Array.ofDim[Byte](lens.sum) + var len = 0 + for ((bs, l) <- byteSegments zip lens) { + bs.copyToArray(bytes, len, l) + len += l + } + println("long sig") + unpickler.unpickle(bytes, 0, clazz, module, jclazz.getName) + } else { // class does not have a Scala signature; it's a Java class + println("no sig found for "+jclazz) + copyMembers(clazz, module, jclazz) + } } - (clazz, module) } - private def unpickleClass(jclazz: jClass[_], bytes: Array[Byte], len: Int): ClassSymbol = { - val (clazz, module) = createClassModule(sOwner(jclazz), newTypeName(jclazz.getSimpleName)) - unpickler.unpickle(bytes, 0, clazz, module, jclazz.getName) - println("found: "+len+" bytes from "+bytes.deep) - clazz + /** Generate types for top-level Scala root class and root companion object + * by copying corresponding types from a Java class. This method used + * to reflect classes in Scala that do not have a Scala pickle info, be it + * because they are local classes or have been compiled from Java sources. + * @param clazz The top-level Scala class for which info is copied + * @param module The top-level Scala companion object for which info is copied + * @param jclazz The Java class + */ + def copyMembers(clazz: Symbol, module: Symbol, jclazz: jClass[_]) { + clazz setInfo new ClassInfoType(List(), newScope, clazz) + module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass) + module setInfo module.moduleClass.tpe } - def followStatic(clazz: Symbol, mods: Int) = + /** If Java modifiers `mods` contain STATIC, return the module class + * of the companion module of `clazz`, otherwise the class `clazz` itself. + */ + private def followStatic(clazz: Symbol, mods: Int) = if (jModifier.isStatic(mods)) clazz.companionModule.moduleClass else clazz - def sOwner(jclazz: jClass[_]): Symbol = { + /** The Scala owner of the Scala class corresponding to the Java class `jclazz` + */ + private def sOwner(jclazz: jClass[_]): Symbol = { if (jclazz.isMemberClass) followStatic(classToScala(jclazz.getEnclosingClass), jclazz.getModifiers) else if (jclazz.isLocalClass) @@ -59,11 +98,18 @@ trait JavaConversions { self: Universe => packageToScala(jclazz.getPackage) } - def sOwner(jmember: jMember): Symbol = { + /** The Scala owner of the Scala symbol corresponding to the Java member `jmember` + */ + private def sOwner(jmember: jMember): Symbol = { followStatic(classToScala(jmember.getDeclaringClass), jmember.getModifiers) } - def lookup(clazz: Symbol, jname: String): Symbol = + /** Find declarations or definition in class `clazz` that maps to a Java + * entity with name `jname`. Because of name-mangling, this is more difficult + * than a simple name-based lookup via `decl`. If `decl` fails, members + * that start with the given name are searched instead. + */ + private def lookup(clazz: Symbol, jname: String): Symbol = clazz.info.decl(newTermName(jname)) orElse { (clazz.info.decls.iterator filter (_.name.toString startsWith jname)).toList match { case List() => NoSymbol @@ -72,82 +118,128 @@ trait JavaConversions { self: Universe => } } - def erasesTo(meth: Symbol, jmeth: jMethod): Boolean = true - def erasesTo(meth: Symbol, jconstr: jConstructor[_]): Boolean = true - + /** Does method `meth` erase to Java method `jmeth`? + * This is true if the Java method type is the same as the Scala method type after performing + * all Scala-specific transformations in InfoTransformers. (to be done) + */ + def erasesTo(meth: Symbol, jmeth: jMethod): Boolean = true //to do: implement + + /** Does constructor `meth` erase to Java method `jconstr`? + * This is true if the Java constructor type is the same as the Scala constructor type after performing + * all Scala-specific transformations in InfoTransformers. (to be done) + */ + def erasesTo(meth: Symbol, jconstr: jConstructor[_]): Boolean = true // to do: implement + + /** The Scala method corresponding to given Java method. + * @param jmeth The Java method + * @return A Scala method object that corresponds to `jmeth`. + */ def methodToScala(jmeth: jMethod): Symbol = methodCache.toScala(jmeth) { val owner = followStatic(classToScala(jmeth.getDeclaringClass), jmeth.getModifiers) lookup(owner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth) } + /** The Scala constructor corresponding to given Java constructor. + * @param jconstr The Java constructor + * @return A Scala method object that corresponds to `jconstr`. + */ def constrToScala(jconstr: jConstructor[_]): Symbol = constructorCache.toScala(jconstr) { val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.getModifiers) lookup(owner, "") suchThat (erasesTo(_, jconstr)) orElse jconstrAsScala(jconstr) } + /** The Scala package corresponding to given Java package + */ def packageToScala(jpkg: jPackage): Symbol = packageCache.toScala(jpkg) { makeScalaPackage(jpkg.getName) } + /** The Scala package with given fully qualified name. + */ def packageNameToScala(fullname: String): Symbol = { val jpkg = jPackage.getPackage(fullname) if (jpkg != null) packageToScala(jpkg) else makeScalaPackage(fullname) } + /** The Scala package with given fully qualified name. Unlike `packageNameToScala`, + * this one bypasses the cache. + */ private def makeScalaPackage(fullname: String): Symbol = { val split = fullname lastIndexOf '.' val owner = if (split > 0) packageNameToScala(fullname take split) else definitions.RootClass + assert(owner.isModuleClass) val name = fullname drop (split + 1) - val pkg = owner.newPackage(NoPosition, newTermName(name)) - pkg.moduleClass setInfo new ClassInfoType(List(), new Scope, pkg) - pkg setInfo pkg.moduleClass.tpe + val pkg = owner.info decl newTermName(name) pkg.moduleClass } + /** The Scala class that corresponds to a given Java class. + * @param jclazz The Java class + * @return A Scala class symbol that reflects all elements of the Java class, + * in the form they appear in the Scala pickling info, or, if that is + * not available, wrapped from the Java reflection info. + */ def classToScala(jclazz: jClass[_]): Symbol = classCache.toScala(jclazz) { if (jclazz.isMemberClass) { sOwner(jclazz).info.decl(newTypeName(jclazz.getSimpleName)).asInstanceOf[ClassSymbol] } else if (jclazz.isLocalClass) { // local classes not preserved by unpickling - treat as Java jclassAsScala(jclazz) } else { // jclazz is top-level - get signature - val ssig = jclazz.getAnnotation(classOf[scala.reflect.ScalaSignature]) - if (ssig != null) { - val bytes = ssig.bytes.getBytes - val len = ByteCodecs.decode(bytes) - unpickleClass(jclazz, bytes, len) - } else { - val slsig = jclazz.getAnnotation(classOf[scala.reflect.ScalaLongSignature]) - if (slsig != null) { - val byteSegments = slsig.bytes map (_.getBytes) - val lens = byteSegments map ByteCodecs.decode - val bytes = Array.ofDim[Byte](lens.sum) - var len = 0 - for ((bs, l) <- byteSegments zip lens) { - bs.copyToArray(bytes, len, l) - len += l - } - unpickleClass(jclazz, bytes, len) - } else { // class does not have a Scala signature; it's a Java class - jclassAsScala(jclazz) - } - } + val (clazz, module) = createClassModule(sOwner(jclazz), newTypeName(jclazz.getSimpleName)) + clazz } } + /** The Scala type that corresponds to given Java type (to be done) + */ def typeToScala(tpe: jType): Type = NoType - def jclassAsScala(jclazz: jClass[_]): Symbol = { + /** The Scala class that corresponds to given Java class without taking + * Scala pickling info into account. + * @param jclazz The Java class + * @return A Scala class symbol that wraps all reflection info of `jclazz` + */ + private def jclassAsScala(jclazz: jClass[_]): Symbol = { val (clazz, module) = createClassModule(sOwner(jclazz), newTypeName(jclazz.getSimpleName)) // fill in clazz, module from jclazz + copyMembers(clazz, module, jclazz) clazz } - def jfieldAsScala(jfield: jField): Symbol = fieldCache.toScala(jfield) { + /** The Scala field that corresponds to given Java field without taking + * Scala pickling info into account. + * @param jfield The Java field + * @return A Scala value symbol that wraps all reflection info of `jfield` + */ + private def jfieldAsScala(jfield: jField): Symbol = fieldCache.toScala(jfield) { sOwner(jfield).newValue(NoPosition, newTermName(jfield.getName)) .setFlag(toScalaFlags(jfield.getModifiers, isClass = false)) .setInfo(typeToScala(jfield.getGenericType)) + // todo: copy annotations } - def jmethodAsScala(jmeth: jMethod): Symbol = NoSymbol - def jconstrAsScala(jconstr: jConstructor[_]): Symbol = NoSymbol + /** The Scala method that corresponds to given Java method without taking + * Scala pickling info into account. + * @param jmeth The Java method + * @return A Scala method symbol that wraps all reflection info of `jmethod` + */ + private def jmethodAsScala(jmeth: jMethod): Symbol = NoSymbol // to be done + + /** The Scala constructor that corresponds to given Java constructor without taking + * Scala pickling info into account. + * @param jconstr The Java constructor + * @return A Scala constructor symbol that wraps all reflection info of `jconstr` + */ + private def jconstrAsScala(jconstr: jConstructor[_]): Symbol = NoSymbol // to be done + + // to be done: + + def packageToJava(pkg: Symbol): jPackage = null // to be done + + /** The Java class corresponding to given Scala class + */ + def classToJava(clazz: Symbol): jClass[_] = null // to be done + def fieldToJava(fld: Symbol): jField = null // to be done + def methodToJava(meth: Symbol): jMethod = null // to be done + def constrToJava(constr: Symbol): jConstructor[_] = null // to be done } \ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/Loaders.scala b/src/compiler/scala/reflect/runtime/Loaders.scala new file mode 100644 index 0000000000..cd608cd471 --- /dev/null +++ b/src/compiler/scala/reflect/runtime/Loaders.scala @@ -0,0 +1,87 @@ +package scala.reflect +package runtime + +import java.lang.{Class => jClass, Package => jPackage} + +trait Loaders { self: Universe => + + /** The lazy type for root. + */ + override val rootLoader = new LazyType { + override def complete(sym: Symbol) = sym setInfo new PackageType(definitions.RootClass) + } + + /** The standard completer for top-level classes + * @param clazz The top-level class + * @param module The companion object of `clazz` + * Calling `complete` on this type will assign the infos of `clazz` and `module` + * by unpickling information from the corresponding Java class. If no Java class + * is found, a package is created instead. + */ + class TopClassCompleter(clazz: Symbol, module: Symbol) extends LazyType { + def makePackage() { + val ptpe = new PackageType(module.moduleClass) + clazz setInfo ptpe + module setInfo ptpe + module.moduleClass setInfo ptpe + } + override def complete(sym: Symbol) = { + println("completing "+sym+"/"+clazz.fullName) + assert(sym == clazz || sym == module || sym == module.moduleClass) + try { + unpickleClass(clazz, module, jClass.forName(clazz.fullName)) + } catch { + case ex: ClassNotFoundException => makePackage() + case ex: NoClassDefFoundError => makePackage() + // Note: We catch NoClassDefFoundError because there are situations + // where a package and a class have the same name except for capitalization. + // It seems in this case the class is loaded even if capitalization differs + // but then a NoClassDefFound error is issued with a ("wrong name: ...") + // reason. (I guess this is a concession to Windows). + // The present behavior is a bit too forgiving, in that it masks + // all class loade errors, not just wrong name errors. We should try + // to be more discriminating. To get on the right track simply delete + // the clause above and load a collection class such as collection.Iterable. + // You'll see an error that class `parallel` has the wrong name. + } + } + } + + /** Create a class and a companion object, enter in enclosing scope, + * and initialize with a lazy type completer. + * @param owner The owner of the newly created class and object + * @param name The simple name of the newly created class + */ + protected def createClassModule(owner: Symbol, name: TypeName) = { + val clazz = owner.newClass(NoPosition, name) + val module = owner.newModule(NoPosition, name.toTermName) + owner.info.decls enter clazz + owner.info.decls enter module + val classCompleter = new TopClassCompleter(clazz, module) + clazz.setInfo(classCompleter) + module.setInfo(classCompleter) + module.moduleClass.setInfo(classCompleter) + (clazz, module) + } + + /** The type for packages. + * Since we cannot search the file system for classes in directories to populate + * a package, we do the following instead: For every member that is looked up in + * the package, create a class and companion object optimistically, giving it + * a TopClassCompleter type. When any of the two symbols is forced via info, + * the TopClassCompleter will sort things out. + */ + class PackageType(pkg: Symbol) extends ClassInfoType(List(), newScope, pkg) { + override def decl(name: Name): Symbol = + (decls lookup name) orElse { + assert(this eq pkg.info, this+" "+pkg.info) + assert(decls eq pkg.info.decls) + println("creating "+name+" in "+pkg) + val (clazz, module) = createClassModule(pkg, name.toTypeName) + if (name.isTypeName) clazz else module + } + override def member(name: Name): Symbol = decl(name) + override def findMember(name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean) = + member(name).filter (m => m.hasAllFlags(requiredFlags) && !m.hasFlag(excludedFlags)) + } +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/Settings.scala b/src/compiler/scala/reflect/runtime/Settings.scala index 2fdcaa2e5f..088ea019e2 100644 --- a/src/compiler/scala/reflect/runtime/Settings.scala +++ b/src/compiler/scala/reflect/runtime/Settings.scala @@ -1,6 +1,10 @@ package scala.reflect package runtime +/** The Settings class for runtime reflection. + * This should be refined, so that settings are settable via command + * line options or properties. + */ class Settings extends internal.settings.MutableSettings { class Setting extends SettingValue diff --git a/src/compiler/scala/reflect/runtime/Universe.scala b/src/compiler/scala/reflect/runtime/Universe.scala index 9157cd9001..f6ffeba401 100644 --- a/src/compiler/scala/reflect/runtime/Universe.scala +++ b/src/compiler/scala/reflect/runtime/Universe.scala @@ -1,32 +1,30 @@ package scala.reflect package runtime -class Universe extends internal.SymbolTable with JavaConversions { +import internal.{SomePhase, NoPhase, Phase, TreeGen} - type AbstractFileType = AbstractFile +/** The universe for standard runtime reflection from Java. + * This type implements all abstract term members in internal.SymbolTable. + * It also provides methods to go from Java members to Scala members, + * using the code in JavaConversions. + */ +class Universe extends internal.SymbolTable with JavaConversions with Loaders { - def picklerPhase = internal.NoPhase + type AbstractFileType = AbstractFile - //val treePrinter = null + def picklerPhase = SomePhase - val gen = new internal.TreeGen { val global: Universe.this.type = Universe.this } + val gen = new TreeGen { val global: Universe.this.type = Universe.this } def settings = new Settings def forInteractive = false def forScaladoc = false - val phaseWithId: Array[internal.Phase] = Array() - val currentRunId = 0 - per = 1 << 8 + 1 // fake a period so that it is different from NoPeriod + val phaseWithId: Array[Phase] = Array(NoPhase, SomePhase) + val currentRunId = 1 // fake a run id so that it is different from NoRunId + phase = SomePhase // set to a phase different from NoPhase def log(msg: => AnyRef): Unit = println(" [] "+msg) - override val rootLoader = new LazyType { - override def complete(sym: Symbol) = sym setInfo packageType(definitions.RootClass) - } - - private def packageType(clazz: Symbol) = new ClassInfoType(List(), newScope, clazz) - - // definitions.RootClass.setInfo(packageType(definitions.RootClass)) type TreeCopier = TreeCopierOps def newStrictTreeCopier: TreeCopier = new StrictTreeCopier @@ -40,6 +38,15 @@ class Universe extends internal.SymbolTable with JavaConversions { val NoPosition = "" } -object Universe extends Universe with App { - classToScala(classOf[scala.collection.Iterable[_]]) +object Universe extends Universe + +/** test code; should go to tests once things settle down a bit + */ +object Test extends Universe with App { + val sym = classToScala(classOf[scala.collection.Iterable[_]]) + println(sym) + println("parents = "+sym.info.parents) + println("decls = "+(sym.info.decls.toList map (_.defString))) + val ms = sym.info.members.toList map (_.initialize) + println("members = "+(ms map (_.defString) mkString ("\n "))) } \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 3c98d0adcc..1d8588209e 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -10,8 +10,7 @@ package classfile import scala.collection.{ mutable, immutable } import mutable.ListBuffer import backend.icode._ -import scala.reflect.internal.ClassfileConstants -import scala.reflect.internal.ClassfileConstants._ +import ClassfileConstants._ import scala.reflect.internal.Flags._ /** ICode reader from Java bytecode. diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/package.scala b/src/compiler/scala/tools/nsc/symtab/classfile/package.scala new file mode 100644 index 0000000000..fe66c515de --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/package.scala @@ -0,0 +1,7 @@ +package scala.tools.nsc.symtab + +package object classfile { + + val ClassfileConstants = reflect.internal.ClassfileConstants + +} -- cgit v1.2.3