From 970d4132b66eb77fa0c3d1d2626a1e377bea6ad4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 19 Jul 2011 13:57:37 +0000 Subject: Refactored infoTransformer functionality from n... Refactored infoTransformer functionality from nsc.transform to reflect.internal.transform. Needed so that we can find Java methods that correspond to Scala methods. Review by extempore. --- .../scala/reflect/internal/transform/Erasure.scala | 271 +++++++++++++++++++++ .../reflect/internal/transform/RefChecks.scala | 13 + .../reflect/internal/transform/Transforms.scala | 16 ++ .../scala/reflect/internal/transform/UnCurry.scala | 70 ++++++ .../scala/reflect/runtime/ConversionUtil.scala | 58 +++++ .../scala/reflect/runtime/JavaConversions.scala | 248 ------------------- .../scala/reflect/runtime/JavaToScala.scala | 215 ++++++++++++++++ .../scala/reflect/runtime/ScalaToJava.scala | 46 ++++ src/compiler/scala/reflect/runtime/Universe.scala | 2 +- .../scala/tools/nsc/transform/Erasure.scala | 260 +------------------- .../scala/tools/nsc/transform/ExplicitOuter.scala | 2 + .../scala/tools/nsc/transform/UnCurry.scala | 63 +---- .../scala/tools/nsc/typechecker/RefChecks.scala | 14 +- 13 files changed, 709 insertions(+), 569 deletions(-) create mode 100644 src/compiler/scala/reflect/internal/transform/Erasure.scala create mode 100644 src/compiler/scala/reflect/internal/transform/RefChecks.scala create mode 100644 src/compiler/scala/reflect/internal/transform/Transforms.scala create mode 100644 src/compiler/scala/reflect/internal/transform/UnCurry.scala create mode 100644 src/compiler/scala/reflect/runtime/ConversionUtil.scala delete mode 100644 src/compiler/scala/reflect/runtime/JavaConversions.scala create mode 100644 src/compiler/scala/reflect/runtime/JavaToScala.scala create mode 100644 src/compiler/scala/reflect/runtime/ScalaToJava.scala diff --git a/src/compiler/scala/reflect/internal/transform/Erasure.scala b/src/compiler/scala/reflect/internal/transform/Erasure.scala new file mode 100644 index 0000000000..06f5009578 --- /dev/null +++ b/src/compiler/scala/reflect/internal/transform/Erasure.scala @@ -0,0 +1,271 @@ +package scala.reflect +package internal +package transform + +trait Erasure { + + val global: SymbolTable + import global._ + import definitions._ + + /** An extractor object for generic arrays */ + object GenericArray { + + /** Is `tp` an unbounded generic type (i.e. which could be instantiated + * with primitive as well as class types)?. + */ + private def genericCore(tp: Type): Type = tp.normalize match { + case TypeRef(_, sym, _) if sym.isAbstractType && !sym.owner.isJavaDefined => + tp + case ExistentialType(tparams, restp) => + genericCore(restp) + case _ => + NoType + } + + /** If `tp` is of the form Array[...Array[T]...] where `T` is an abstract type + * then Some(N, T) where N is the number of Array constructors enclosing `T`, + * otherwise None. Existentials on any level are ignored. + */ + def unapply(tp: Type): Option[(Int, Type)] = tp.normalize match { + case TypeRef(_, ArrayClass, List(arg)) => + genericCore(arg) match { + case NoType => + unapply(arg) match { + case Some((level, core)) => Some((level + 1, core)) + case None => None + } + case core => + Some(1, core) + } + case ExistentialType(tparams, restp) => + unapply(restp) + case _ => + None + } + } + + protected def unboundedGenericArrayLevel(tp: Type): Int = tp match { + case GenericArray(level, core) if !(core <:< AnyRefClass.tpe) => level + case _ => 0 + } + + // @M #2585 when generating a java generic signature that includes a selection of an inner class p.I, (p = `pre`, I = `cls`) + // must rewrite to p'.I, where p' refers to the class that directly defines the nested class I + // see also #2585 marker in javaSig: there, type arguments must be included (use pre.baseType(cls.owner)) + // requires cls.isClass + @inline protected def rebindInnerClass(pre: Type, cls: Symbol): Type = + if (cls.owner.isClass) cls.owner.tpe else pre // why not cls.isNestedClass? + + abstract class ErasureMap extends TypeMap { + def mergeParents(parents: List[Type]): Type + + def apply(tp: Type): Type = { + tp match { + case ConstantType(_) => + tp + case st: SubType => + apply(st.supertype) + case TypeRef(pre, sym, args) => + if (sym == ArrayClass) + if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe + else if (args.head.typeSymbol == NothingClass || args.head.typeSymbol == NullClass) arrayType(ObjectClass.tpe) + else typeRef(apply(pre), sym, args map this) + else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) erasedTypeRef(ObjectClass) + else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass) + else if (sym.isRefinementClass) apply(mergeParents(tp.parents)) + else if (sym.isClass) typeRef(apply(rebindInnerClass(pre, sym)), sym, List()) // #2585 + else apply(sym.info) // alias type or abstract type + case PolyType(tparams, restpe) => + apply(restpe) + case ExistentialType(tparams, restpe) => + apply(restpe) + case mt @ MethodType(params, restpe) => + MethodType( + cloneSymbols(params) map (p => p.setInfo(apply(p.tpe))), + if (restpe.typeSymbol == UnitClass) + erasedTypeRef(UnitClass) + else if (settings.YdepMethTpes.value) + // this replaces each typeref that refers to an argument + // by the type `p.tpe` of the actual argument p (p in params) + apply(mt.resultType(params map (_.tpe))) + else + apply(restpe)) + case RefinedType(parents, decls) => + apply(mergeParents(parents)) + case AnnotatedType(_, atp, _) => + apply(atp) + case ClassInfoType(parents, decls, clazz) => + ClassInfoType( + if (clazz == ObjectClass || isValueClass(clazz)) Nil + else if (clazz == ArrayClass) List(erasedTypeRef(ObjectClass)) + else removeDoubleObject(parents map this), + decls, clazz) + case _ => + mapOver(tp) + } + } + } + + protected def verifyJavaErasure = false + + /** The erasure |T| of a type T. This is: + * + * - For a constant type, itself. + * - For a type-bounds structure, the erasure of its upper bound. + * - For every other singleton type, the erasure of its supertype. + * - For a typeref scala.Array+[T] where T is an abstract type, AnyRef. + * - For a typeref scala.Array+[T] where T is not an abstract type, scala.Array+[|T|]. + * - For a typeref scala.Any or scala.AnyVal, java.lang.Object. + * - For a typeref scala.Unit, scala.runtime.BoxedUnit. + * - For a typeref P.C[Ts] where C refers to a class, |P|.C. + * (Where P is first rebound to the class that directly defines C.) + * - For a typeref P.C[Ts] where C refers to an alias type, the erasure of C's alias. + * - For a typeref P.C[Ts] where C refers to an abstract type, the + * erasure of C's upper bound. + * - For a non-empty type intersection (possibly with refinement) + * - in scala, the erasure of the intersection dominator + * - in java, the erasure of its first parent <--- @PP: not yet in spec. + * - For an empty type intersection, java.lang.Object. + * - For a method type (Fs)scala.Unit, (|Fs|)scala#Unit. + * - For any other method type (Fs)Y, (|Fs|)|T|. + * - For a polymorphic type, the erasure of its result type. + * - For the class info type of java.lang.Object, the same type without any parents. + * - For a class info type of a value class, the same type without any parents. + * - For any other class info type with parents Ps, the same type with + * parents |Ps|, but with duplicate references of Object removed. + * - for all other types, the type itself (with any sub-components erased) + */ + def erasure(sym: Symbol, tp: Type): Type = { + if (sym != NoSymbol && sym.enclClass.isJavaDefined) { + val res = javaErasure(tp) + if (verifyJavaErasure && sym.isMethod) { + val old = scalaErasure(tp) + if (!(res =:= old)) + log("Identified divergence between java/scala erasure:\n scala: " + old + "\n java: " + res) + } + res + } + else scalaErasure(tp) + } + + /** Scala's more precise erasure than java's is problematic as follows: + * + * - Symbols are read from classfiles and populated with types + * - The textual signature read from the bytecode is forgotten + * - Bytecode generation must know the precise signature of a method + * - the signature is derived from the erasure of the method type + * - If that derivation does not adhere to the rules by which the original + * signature was created, a NoSuchMethod error will result. + * + * For this reason and others (such as distinguishing constructors from other methods) + * erasure is now (Symbol, Type) => Type rather than Type => Type. + */ + object scalaErasure extends ErasureMap { + /** In scala, calculate a useful parent. + * An intersection such as `Object with Trait` erases to Trait. + */ + def mergeParents(parents: List[Type]): Type = + intersectionDominator(parents) + } + + /** The intersection dominator (SLS 3.7) of a list of types is computed as follows. + * + * - If the list contains one or more occurrences of scala.Array with + * type parameters El1, El2, ... then the dominator is scala.Array with + * type parameter of intersectionDominator(List(El1, El2, ...)). <--- @PP: not yet in spec. + * - Otherwise, the list is reduced to a subsequence containing only types + * which are not subtypes of other listed types (the span.) + * - If the span is empty, the dominator is Object. + * - If the span contains a class Tc which is not a trait and which is + * not Object, the dominator is Tc. <--- @PP: "which is not Object" not in spec. + * - Otherwise, the dominator is the first element of the span. + */ + def intersectionDominator(parents: List[Type]): Type = { + if (parents.isEmpty) ObjectClass.tpe + else { + val psyms = parents map (_.typeSymbol) + if (psyms contains ArrayClass) { + // treat arrays specially + arrayType( + intersectionDominator( + parents filter (_.typeSymbol == ArrayClass) map (_.typeArgs.head))) + } else { + // implement new spec for erasure of refined types. + def isUnshadowed(psym: Symbol) = + !(psyms exists (qsym => (psym ne qsym) && (qsym isNonBottomSubClass psym))) + val cs = parents.iterator.filter { p => // isUnshadowed is a bit expensive, so try classes first + val psym = p.typeSymbol + psym.initialize + psym.isClass && !psym.isTrait && isUnshadowed(psym) + } + (if (cs.hasNext) cs else parents.iterator.filter(p => isUnshadowed(p.typeSymbol))).next() + } + } + } + + object javaErasure extends ErasureMap { + /** In java, always take the first parent. + * An intersection such as `Object with Trait` erases to Object. + */ + def mergeParents(parents: List[Type]): Type = + if (parents.isEmpty) ObjectClass.tpe + else parents.head + } + + /** Type reference after erasure */ + def erasedTypeRef(sym: Symbol): Type = + typeRef(erasure(sym, sym.owner.tpe), sym, List()) + + /** Remove duplicate references to class Object in a list of parent classes */ + private def removeDoubleObject(tps: List[Type]): List[Type] = tps match { + case List() => List() + case tp :: tps1 => + if (tp.typeSymbol == ObjectClass) tp :: tps1.filter(_.typeSymbol != ObjectClass) + else tp :: removeDoubleObject(tps1) + } + + /** The symbol's erased info. This is the type's erasure, except for the following symbols: + * + * - For $asInstanceOf : [T]T + * - For $isInstanceOf : [T]scala#Boolean + * - For class Array : [T]C where C is the erased classinfo of the Array class. + * - For Array[T]. : {scala#Int)Array[T] + * - For a type parameter : A type bounds type consisting of the erasures of its bounds. + */ + def transformInfo(sym: Symbol, tp: Type): Type = { + if (sym == Object_asInstanceOf) + sym.info + else if (sym == Object_isInstanceOf || sym == ArrayClass) + PolyType(sym.info.typeParams, erasure(sym, sym.info.resultType)) + else if (sym.isAbstractType) + TypeBounds(WildcardType, WildcardType) + else if (sym.isTerm && sym.owner == ArrayClass) { + if (sym.isClassConstructor) + tp match { + case MethodType(params, TypeRef(pre, sym1, args)) => + MethodType(cloneSymbols(params) map (p => p.setInfo(erasure(sym, p.tpe))), + typeRef(erasure(sym, pre), sym1, args)) + } + else if (sym.name == nme.apply) + tp + else if (sym.name == nme.update) + (tp: @unchecked) match { + case MethodType(List(index, tvar), restpe) => + MethodType(List(index.cloneSymbol.setInfo(erasure(sym, index.tpe)), tvar), + erasedTypeRef(UnitClass)) + } + else erasure(sym, tp) + } else if ( + sym.owner != NoSymbol && + sym.owner.owner == ArrayClass && + sym == Array_update.paramss.head(1)) { + // special case for Array.update: the non-erased type remains, i.e. (Int,A)Unit + // since the erasure type map gets applied to every symbol, we have to catch the + // symbol here + tp + } else { + erasure(sym, tp) + } + } +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/transform/RefChecks.scala b/src/compiler/scala/reflect/internal/transform/RefChecks.scala new file mode 100644 index 0000000000..d6108ab665 --- /dev/null +++ b/src/compiler/scala/reflect/internal/transform/RefChecks.scala @@ -0,0 +1,13 @@ +package scala.reflect +package internal +package transform + +trait RefChecks { + + val global: SymbolTable + import global._ + + def transformInfo(sym: Symbol, tp: Type): Type = + if (sym.isModule && !sym.isStatic) NullaryMethodType(tp) + else tp +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/transform/Transforms.scala b/src/compiler/scala/reflect/internal/transform/Transforms.scala new file mode 100644 index 0000000000..8ed40dc55c --- /dev/null +++ b/src/compiler/scala/reflect/internal/transform/Transforms.scala @@ -0,0 +1,16 @@ +package scala.reflect +package internal +package transform + +trait Transforms { self: SymbolTable => + + object refChecks extends { val global: Transforms.this.type = self } with RefChecks + object uncurry extends { val global: Transforms.this.type = self } with UnCurry + object erasure extends { val global: Transforms.this.type = self } with Erasure + + def javaType(sym: Symbol) = + erasure.transformInfo(sym, + uncurry.transformInfo(sym, + refChecks.transformInfo(sym, sym.info))) + +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/transform/UnCurry.scala b/src/compiler/scala/reflect/internal/transform/UnCurry.scala new file mode 100644 index 0000000000..1d63aa4582 --- /dev/null +++ b/src/compiler/scala/reflect/internal/transform/UnCurry.scala @@ -0,0 +1,70 @@ +package scala.reflect +package internal +package transform + +import Flags._ + +trait UnCurry { + + val global: SymbolTable + import global._ + import definitions._ + + private def expandAlias(tp: Type): Type = if (!tp.isHigherKinded) tp.normalize else tp + + private def isUnboundedGeneric(tp: Type) = tp match { + case t @ TypeRef(_, sym, _) => sym.isAbstractType && !(t <:< AnyRefClass.tpe) + case _ => false + } + + protected val uncurry: TypeMap = new TypeMap { + def apply(tp0: Type): Type = { + // tp0.typeSymbolDirect.initialize + val tp = expandAlias(tp0) + tp match { + case MethodType(params, MethodType(params1, restpe)) => + apply(MethodType(params ::: params1, restpe)) + case MethodType(params, ExistentialType(tparams, restpe @ MethodType(_, _))) => + assert(false, "unexpected curried method types with intervening existential") + tp0 + case MethodType(h :: t, restpe) if h.isImplicit => + apply(MethodType(h.cloneSymbol.resetFlag(IMPLICIT) :: t, restpe)) + case NullaryMethodType(restpe) => + apply(MethodType(List(), restpe)) + case TypeRef(pre, ByNameParamClass, List(arg)) => + apply(functionType(List(), arg)) + case TypeRef(pre, RepeatedParamClass, args) => + apply(appliedType(SeqClass.typeConstructor, args)) + case TypeRef(pre, JavaRepeatedParamClass, args) => + apply(arrayType( + if (isUnboundedGeneric(args.head)) ObjectClass.tpe else args.head)) + case _ => + expandAlias(mapOver(tp)) + } + } + } + + private val uncurryType = new TypeMap { + def apply(tp0: Type): Type = { + val tp = expandAlias(tp0) + tp match { + case ClassInfoType(parents, decls, clazz) => + val parents1 = parents mapConserve uncurry + if (parents1 eq parents) tp + else ClassInfoType(parents1, decls, clazz) // @MAT normalize in decls?? + case PolyType(_, _) => + mapOver(tp) + case _ => + tp + } + } + } + + /** - return symbol's transformed type, + * - if symbol is a def parameter with transformed type T, return () => T + * + * @MAT: starting with this phase, the info of every symbol will be normalized + */ + def transformInfo(sym: Symbol, tp: Type): Type = + if (sym.isType) uncurryType(tp) else uncurry(tp) +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/ConversionUtil.scala b/src/compiler/scala/reflect/runtime/ConversionUtil.scala new file mode 100644 index 0000000000..e406f99cdf --- /dev/null +++ b/src/compiler/scala/reflect/runtime/ConversionUtil.scala @@ -0,0 +1,58 @@ +package scala.reflect +package runtime + +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 collection.mutable.HashMap + +trait ConversionUtil extends internal.transform.Transforms { self: Universe => + + /** A cache that maintains a bijection between Java reflection type `J` + * and Scala reflection type `S`. + */ + protected class TwoWayCache[J, S] { + private val toScalaMap = new HashMap[J, S] + private val toJavaMap = new HashMap[S, J] + + def toScala(key: J)(body: => S): S = toScalaMap.getOrElseUpdate(key, body) + + def toJava(key: S)(body: => J): J = toJavaMap.getOrElseUpdate(key, body) + + def toJavaOption(key: S)(body: => Option[J]): Option[J] = toJavaMap get key match { + case None => + val result = body + for (value <- result) toJavaMap(key) = value + result + case some => some + } + } + + protected val classCache = new TwoWayCache[jClass[_], Symbol] + protected val packageCache = new TwoWayCache[Package, Symbol] + protected val methodCache = new TwoWayCache[jMethod, Symbol] + protected val constructorCache = new TwoWayCache[jConstructor[_], Symbol] + protected val fieldCache = new TwoWayCache[jField, Symbol] + + def typeToJavaClass(tpe: Type): jClass[_] + + /** 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) + */ + protected def erasesTo(meth: Symbol, jmeth: jMethod): Boolean = { + val mtpe = javaType(meth) + (mtpe.paramTypes map typeToJavaClass) == jmeth.getParameterTypes.toList && + typeToJavaClass(mtpe.resultType) == jmeth.getReturnType + } + + /** 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) + */ + protected def erasesTo(meth: Symbol, jconstr: jConstructor[_]): Boolean = { + val mtpe = javaType(meth) + (mtpe.paramTypes map typeToJavaClass) == jconstr.getParameterTypes.toList + } +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/JavaConversions.scala b/src/compiler/scala/reflect/runtime/JavaConversions.scala deleted file mode 100644 index e695c5a9cf..0000000000 --- a/src/compiler/scala/reflect/runtime/JavaConversions.scala +++ /dev/null @@ -1,248 +0,0 @@ -package scala.reflect -package runtime - -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.pickling.ByteCodecs -import internal.ClassfileConstants._ -import internal.pickling.UnPickler -import collection.mutable.HashMap - -trait JavaConversions { self: Universe => - - private object unpickler extends UnPickler { - val global: JavaConversions.this.type = self - } - - /** 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) - } - - private val classCache = new TwoWayCache[jClass[_], Symbol] - private val packageCache = new TwoWayCache[jPackage, Symbol] - private val methodCache = new TwoWayCache[jMethod, Symbol] - private val constructorCache = new TwoWayCache[jConstructor[_], Symbol] - private val fieldCache = new TwoWayCache[jField, Symbol] - - /** 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) - } - } - } - - /** 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 - } - - /** 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 - - /** 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) - methodToScala(jclazz.getEnclosingMethod) orElse constrToScala(jclazz.getEnclosingConstructor) - else - packageToScala(jclazz.getPackage) - } - - /** 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) - } - - /** 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 - case List(sym) => sym - case alts => clazz.newOverloaded(alts.head.tpe.prefix, alts) - } - } - - /** 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.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 (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 - - /** 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 - } - - /** 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 - } - - /** 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[_] = classCache.toJava(clazz) { - jClass.forName(clazz.fullName) // todo: what about local classes? - } - - 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/JavaToScala.scala b/src/compiler/scala/reflect/runtime/JavaToScala.scala new file mode 100644 index 0000000000..e67dbd3ce4 --- /dev/null +++ b/src/compiler/scala/reflect/runtime/JavaToScala.scala @@ -0,0 +1,215 @@ +package scala.reflect +package runtime + +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.pickling.ByteCodecs +import internal.ClassfileConstants._ +import internal.pickling.UnPickler +import collection.mutable.HashMap + +trait JavaToScala extends ConversionUtil { self: Universe => + + private object unpickler extends UnPickler { + val global: JavaToScala.this.type = self + } + + + /** 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) + } + } + } + + /** 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 + } + + /** 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 + + /** 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) + methodToScala(jclazz.getEnclosingMethod) orElse constrToScala(jclazz.getEnclosingConstructor) + else + packageToScala(jclazz.getPackage) + } + + /** 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) + } + + /** Returns `true` if Scala name `name` equals Java name `jstr`, possibly after + * make-not-private expansion. + */ + private def approximateMatch(sym: Symbol, jstr: String): Boolean = + (sym.name.toString == jstr) || + sym.isPrivate && nme.expandedName(sym.name, sym.owner).toString == jstr + + /** 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 (approximateMatch(_, jname))).toList match { + case List() => NoSymbol + case List(sym) => sym + case alts => clazz.newOverloaded(alts.head.tpe.prefix, alts) + } + } + + /** 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.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 (clazz, module) = createClassModule(sOwner(jclazz), newTypeName(jclazz.getSimpleName)) + clazz + } + } + + /** The Scala type that corresponds to given Java type (to be done) + */ + def jtypeToScala(tpe: jType): Type = NoType + + /** 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 + } + + /** 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(jtypeToScala(jfield.getGenericType)) + // todo: copy annotations + } + + /** 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 + + +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/ScalaToJava.scala b/src/compiler/scala/reflect/runtime/ScalaToJava.scala new file mode 100644 index 0000000000..7682566bf4 --- /dev/null +++ b/src/compiler/scala/reflect/runtime/ScalaToJava.scala @@ -0,0 +1,46 @@ +package scala.reflect +package runtime + +import java.lang.{Class => jClass, Package => jPackage} +import java.lang.reflect.{ + Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField, + Member => jMember, Type => jType, GenericDeclaration} + +trait ScalaToJava extends ConversionUtil { self: Universe => + + /** Optionally, the Java package corresponding to a given Scala package, or None if no such Java package exists. + * @param pkg The Scala package + */ + def packageToJava(pkg: Symbol): Option[jPackage] = packageCache.toJavaOption(pkg) { + Option(jPackage.getPackage(pkg.fullName.toString)) + } + + /** The Java class corresponding to given Scala class. + * Note: This only works for + * - top-level classes + * - Scala classes that were generated via jclassToScala + * - classes that have a class owner that has a corresponding Java class + * @throws A `NoClassDefFoundError` for all Scala classes not in one of these categories. + */ + def classToJava(clazz: Symbol): jClass[_] = classCache.toJava(clazz) { + def noClass = throw new NoClassDefFoundError("no Java class corresponding to "+clazz+" found") + if (clazz.owner.isPackageClass) + jClass.forName(clazz.fullName) + else if (clazz.owner.isClass) + classToJava(clazz.owner) + .getDeclaredClasses + .find(_.getSimpleName == clazz.name.toString) + .getOrElse(noClass) + else + noClass + } + + 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 + + /** The Java class corresponds to given Scala type (to be done) + */ + def typeToJavaClass(tpe: Type): jClass[_] = null + +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/Universe.scala b/src/compiler/scala/reflect/runtime/Universe.scala index f6ffeba401..bc295694be 100644 --- a/src/compiler/scala/reflect/runtime/Universe.scala +++ b/src/compiler/scala/reflect/runtime/Universe.scala @@ -8,7 +8,7 @@ import internal.{SomePhase, NoPhase, Phase, TreeGen} * 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 { +class Universe extends internal.SymbolTable with JavaToScala with ScalaToJava with Loaders { type AbstractFileType = AbstractFile diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 837796e261..c4a728a091 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -12,6 +12,7 @@ import symtab._ import Flags._ abstract class Erasure extends AddInterfaces + with reflect.internal.transform.Erasure with typechecker.Analyzer with TypingTransformers with ast.TreeDSL @@ -29,43 +30,6 @@ abstract class Erasure extends AddInterfaces // -------- erasure on types -------------------------------------------------------- - /** An extractor object for generic arrays */ - object GenericArray { - - /** Is `tp` an unbounded generic type (i.e. which could be instantiated - * with primitive as well as class types)?. - */ - private def genericCore(tp: Type): Type = tp.normalize match { - case TypeRef(_, sym, _) if sym.isAbstractType && !sym.owner.isJavaDefined => - tp - case ExistentialType(tparams, restp) => - genericCore(restp) - case _ => - NoType - } - - /** If `tp` is of the form Array[...Array[T]...] where `T` is an abstract type - * then Some(N, T) where N is the number of Array constructors enclosing `T`, - * otherwise None. Existentials on any level are ignored. - */ - def unapply(tp: Type): Option[(Int, Type)] = tp.normalize match { - case TypeRef(_, ArrayClass, List(arg)) => - genericCore(arg) match { - case NoType => - unapply(arg) match { - case Some((level, core)) => Some((level + 1, core)) - case None => None - } - case core => - Some(1, core) - } - case ExistentialType(tparams, restp) => - unapply(restp) - case _ => - None - } - } - // A type function from T => Class[U], used to determine the return // type of getClass calls. The returned type is: // @@ -108,170 +72,6 @@ abstract class Erasure extends AddInterfaces atPos(tree.pos)(Apply(Select(tree, conversion), Nil)) } - private def unboundedGenericArrayLevel(tp: Type): Int = tp match { - case GenericArray(level, core) if !(core <:< AnyRefClass.tpe) => level - case _ => 0 - } - - // @M #2585 when generating a java generic signature that includes a selection of an inner class p.I, (p = `pre`, I = `cls`) - // must rewrite to p'.I, where p' refers to the class that directly defines the nested class I - // see also #2585 marker in javaSig: there, type arguments must be included (use pre.baseType(cls.owner)) - // requires cls.isClass - @inline private def rebindInnerClass(pre: Type, cls: Symbol): Type = - if (cls.owner.isClass) cls.owner.tpe else pre // why not cls.isNestedClass? - - /** The erasure |T| of a type T. This is: - * - * - For a constant type, itself. - * - For a type-bounds structure, the erasure of its upper bound. - * - For every other singleton type, the erasure of its supertype. - * - For a typeref scala.Array+[T] where T is an abstract type, AnyRef. - * - For a typeref scala.Array+[T] where T is not an abstract type, scala.Array+[|T|]. - * - For a typeref scala.Any or scala.AnyVal, java.lang.Object. - * - For a typeref scala.Unit, scala.runtime.BoxedUnit. - * - For a typeref P.C[Ts] where C refers to a class, |P|.C. - * (Where P is first rebound to the class that directly defines C.) - * - For a typeref P.C[Ts] where C refers to an alias type, the erasure of C's alias. - * - For a typeref P.C[Ts] where C refers to an abstract type, the - * erasure of C's upper bound. - * - For a non-empty type intersection (possibly with refinement) - * - in scala, the erasure of the intersection dominator - * - in java, the erasure of its first parent <--- @PP: not yet in spec. - * - For an empty type intersection, java.lang.Object. - * - For a method type (Fs)scala.Unit, (|Fs|)scala#Unit. - * - For any other method type (Fs)Y, (|Fs|)|T|. - * - For a polymorphic type, the erasure of its result type. - * - For the class info type of java.lang.Object, the same type without any parents. - * - For a class info type of a value class, the same type without any parents. - * - For any other class info type with parents Ps, the same type with - * parents |Ps|, but with duplicate references of Object removed. - * - for all other types, the type itself (with any sub-components erased) - */ - def erasure(sym: Symbol, tp: Type): Type = { - if (sym != NoSymbol && sym.enclClass.isJavaDefined) { - val res = javaErasure(tp) - if (verifyJavaErasure && sym.isMethod) { - val old = scalaErasure(tp) - if (!(res =:= old)) - log("Identified divergence between java/scala erasure:\n scala: " + old + "\n java: " + res) - } - res - } - else scalaErasure(tp) - } - - /** Scala's more precise erasure than java's is problematic as follows: - * - * - Symbols are read from classfiles and populated with types - * - The textual signature read from the bytecode is forgotten - * - Bytecode generation must know the precise signature of a method - * - the signature is derived from the erasure of the method type - * - If that derivation does not adhere to the rules by which the original - * signature was created, a NoSuchMethod error will result. - * - * For this reason and others (such as distinguishing constructors from other methods) - * erasure is now (Symbol, Type) => Type rather than Type => Type. - */ - object scalaErasure extends ErasureMap { - /** In scala, calculate a useful parent. - * An intersection such as `Object with Trait` erases to Trait. - */ - def mergeParents(parents: List[Type]): Type = - intersectionDominator(parents) - } - object javaErasure extends ErasureMap { - /** In java, always take the first parent. - * An intersection such as `Object with Trait` erases to Object. - */ - def mergeParents(parents: List[Type]): Type = - if (parents.isEmpty) ObjectClass.tpe - else parents.head - } - - /** The intersection dominator (SLS 3.7) of a list of types is computed as follows. - * - * - If the list contains one or more occurrences of scala.Array with - * type parameters El1, El2, ... then the dominator is scala.Array with - * type parameter of intersectionDominator(List(El1, El2, ...)). <--- @PP: not yet in spec. - * - Otherwise, the list is reduced to a subsequence containing only types - * which are not subtypes of other listed types (the span.) - * - If the span is empty, the dominator is Object. - * - If the span contains a class Tc which is not a trait and which is - * not Object, the dominator is Tc. <--- @PP: "which is not Object" not in spec. - * - Otherwise, the dominator is the first element of the span. - */ - def intersectionDominator(parents: List[Type]): Type = { - if (parents.isEmpty) ObjectClass.tpe - else { - val psyms = parents map (_.typeSymbol) - if (psyms contains ArrayClass) { - // treat arrays specially - arrayType( - intersectionDominator( - parents filter (_.typeSymbol == ArrayClass) map (_.typeArgs.head))) - } else { - // implement new spec for erasure of refined types. - def isUnshadowed(psym: Symbol) = - !(psyms exists (qsym => (psym ne qsym) && (qsym isNonBottomSubClass psym))) - val cs = parents.iterator.filter { p => // isUnshadowed is a bit expensive, so try classes first - val psym = p.typeSymbol - psym.initialize - psym.isClass && !psym.isTrait && isUnshadowed(psym) - } - (if (cs.hasNext) cs else parents.iterator.filter(p => isUnshadowed(p.typeSymbol))).next() - } - } - } - - abstract class ErasureMap extends TypeMap { - def mergeParents(parents: List[Type]): Type - - def apply(tp: Type): Type = { - tp match { - case ConstantType(_) => - tp - case st: SubType => - apply(st.supertype) - case TypeRef(pre, sym, args) => - if (sym == ArrayClass) - if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe - else if (args.head.typeSymbol == NothingClass || args.head.typeSymbol == NullClass) arrayType(ObjectClass.tpe) - else typeRef(apply(pre), sym, args map this) - else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) erasedTypeRef(ObjectClass) - else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass) - else if (sym.isRefinementClass) apply(mergeParents(tp.parents)) - else if (sym.isClass) typeRef(apply(rebindInnerClass(pre, sym)), sym, List()) // #2585 - else apply(sym.info) // alias type or abstract type - case PolyType(tparams, restpe) => - apply(restpe) - case ExistentialType(tparams, restpe) => - apply(restpe) - case mt @ MethodType(params, restpe) => - MethodType( - cloneSymbols(params) map (p => p.setInfo(apply(p.tpe))), - if (restpe.typeSymbol == UnitClass) - erasedTypeRef(UnitClass) - else if (settings.YdepMethTpes.value) - // this replaces each typeref that refers to an argument - // by the type `p.tpe` of the actual argument p (p in params) - apply(mt.resultType(params map (_.tpe))) - else - apply(restpe)) - case RefinedType(parents, decls) => - apply(mergeParents(parents)) - case AnnotatedType(_, atp, _) => - apply(atp) - case ClassInfoType(parents, decls, clazz) => - ClassInfoType( - if (clazz == ObjectClass || isValueClass(clazz)) Nil - else if (clazz == ArrayClass) List(erasedTypeRef(ObjectClass)) - else removeDoubleObject(parents map this), - decls, clazz) - case _ => - mapOver(tp) - } - } - } private object NeedsSigCollector extends TypeCollector(false) { def traverse(tp: Type) { @@ -299,7 +99,7 @@ abstract class Erasure extends AddInterfaces } } - private def verifyJavaErasure = settings.Xverify.value || settings.debug.value + override protected def verifyJavaErasure = settings.Xverify.value || settings.debug.value private def needsJavaSig(tp: Type) = !settings.Ynogenericsig.value && NeedsSigCollector.collect(tp) // only refer to type params that will actually make it into the sig, this excludes: @@ -521,18 +321,6 @@ abstract class Erasure extends AddInterfaces class UnknownSig extends Exception - /** Type reference after erasure */ - def erasedTypeRef(sym: Symbol): Type = - typeRef(erasure(sym, sym.owner.tpe), sym, List()) - - /** Remove duplicate references to class Object in a list of parent classes */ - private def removeDoubleObject(tps: List[Type]): List[Type] = tps match { - case List() => List() - case tp :: tps1 => - if (tp.typeSymbol == ObjectClass) tp :: tps1.filter(_.typeSymbol != ObjectClass) - else tp :: removeDoubleObject(tps1) - } - /** The symbol's erased info. This is the type's erasure, except for the following symbols: * * - For $asInstanceOf : [T]T @@ -541,48 +329,8 @@ abstract class Erasure extends AddInterfaces * - For Array[T]. : {scala#Int)Array[T] * - For a type parameter : A type bounds type consisting of the erasures of its bounds. */ - def transformInfo(sym: Symbol, tp: Type): Type = { - if (sym == Object_asInstanceOf) - sym.info - else if (sym == Object_isInstanceOf || sym == ArrayClass) - PolyType(sym.info.typeParams, erasure(sym, sym.info.resultType)) - else if (sym.isAbstractType) - TypeBounds(WildcardType, WildcardType) - else if (sym.isTerm && sym.owner == ArrayClass) { - if (sym.isClassConstructor) - tp match { - case MethodType(params, TypeRef(pre, sym1, args)) => - MethodType(cloneSymbols(params) map (p => p.setInfo(erasure(sym, p.tpe))), - typeRef(erasure(sym, pre), sym1, args)) - } - else if (sym.name == nme.apply) - tp - else if (sym.name == nme.update) - (tp: @unchecked) match { - case MethodType(List(index, tvar), restpe) => - MethodType(List(index.cloneSymbol.setInfo(erasure(sym, index.tpe)), tvar), - erasedTypeRef(UnitClass)) - } - else erasure(sym, tp) - } else if ( - sym.owner != NoSymbol && - sym.owner.owner == ArrayClass && - sym == Array_update.paramss.head(1)) { - // special case for Array.update: the non-erased type remains, i.e. (Int,A)Unit - // since the erasure type map gets applied to every symbol, we have to catch the - // symbol here - tp - } else { -/* - val erased = - if (sym.isGetter && sym.tpe.isInstanceOf[MethodType]) - erasure mapOver sym.tpe // for getters, unlike for normal methods, always convert Unit to BoxedUnit. - else - erasure(tp) -*/ - transformMixinInfo(erasure(sym, tp)) - } - } + override def transformInfo(sym: Symbol, tp: Type): Type = + transformMixinInfo(super.transformInfo(sym, tp)) val deconstMap = new TypeMap { // For some reason classOf[Foo] creates ConstantType(Constant(tpe)) with an actual Type for tpe, diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index f2e4495783..992a746e1e 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -123,6 +123,8 @@ abstract class ExplicitOuter extends InfoTransform * Remove protected flag from all members of traits. * * + * Note: this transformInfo need not be reflected as the JVM reflection already + * elides outer pointers. */ def transformInfo(sym: Symbol, tp: Type): Type = tp match { case MethodType(params, restpe1) => diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 0916cf989d..bd9f24177a 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -34,7 +34,11 @@ import scala.collection.{ mutable, immutable } * - convert non-local returns to throws with enclosing try statements. */ /* */ -abstract class UnCurry extends InfoTransform with TypingTransformers with ast.TreeDSL { +abstract class UnCurry extends InfoTransform + with reflect.internal.transform.UnCurry + with TypingTransformers with ast.TreeDSL { + val global: Global // need to repeat here because otherwise last mixin defines global as + // SymbolTable. If we had DOT this would not be an issue import global._ // the global environment import definitions._ // standard classes and methods import CODE._ @@ -47,63 +51,6 @@ abstract class UnCurry extends InfoTransform with TypingTransformers with ast.Tr // ------ Type transformation -------------------------------------------------------- // uncurry and uncurryType expand type aliases - private def expandAlias(tp: Type): Type = if (!tp.isHigherKinded) tp.normalize else tp - - private def isUnboundedGeneric(tp: Type) = tp match { - case t @ TypeRef(_, sym, _) => sym.isAbstractType && !(t <:< AnyRefClass.tpe) - case _ => false - } - - private val uncurry: TypeMap = new TypeMap { - def apply(tp0: Type): Type = { - // tp0.typeSymbolDirect.initialize - val tp = expandAlias(tp0) - tp match { - case MethodType(params, MethodType(params1, restpe)) => - apply(MethodType(params ::: params1, restpe)) - case MethodType(params, ExistentialType(tparams, restpe @ MethodType(_, _))) => - assert(false, "unexpected curried method types with intervening existential") - tp0 - case MethodType(h :: t, restpe) if h.isImplicit => - apply(MethodType(h.cloneSymbol.resetFlag(IMPLICIT) :: t, restpe)) - case NullaryMethodType(restpe) => - apply(MethodType(List(), restpe)) - case TypeRef(pre, ByNameParamClass, List(arg)) => - apply(functionType(List(), arg)) - case TypeRef(pre, RepeatedParamClass, args) => - apply(appliedType(SeqClass.typeConstructor, args)) - case TypeRef(pre, JavaRepeatedParamClass, args) => - apply(arrayType( - if (isUnboundedGeneric(args.head)) ObjectClass.tpe else args.head)) - case _ => - expandAlias(mapOver(tp)) - } - } - } - - private val uncurryType = new TypeMap { - def apply(tp0: Type): Type = { - val tp = expandAlias(tp0) - tp match { - case ClassInfoType(parents, decls, clazz) => - val parents1 = parents mapConserve uncurry - if (parents1 eq parents) tp - else ClassInfoType(parents1, decls, clazz) // @MAT normalize in decls?? - case PolyType(_, _) => - mapOver(tp) - case _ => - tp - } - } - } - - /** - return symbol's transformed type, - * - if symbol is a def parameter with transformed type T, return () => T - * - * @MAT: starting with this phase, the info of every symbol will be normalized - */ - def transformInfo(sym: Symbol, tp: Type): Type = - if (sym.isType) uncurryType(tp) else uncurry(tp) /** Traverse tree omitting local method definitions. * If a `return` is encountered, set `returnFound` to true. diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index bf6e7a91ba..8e1ee97387 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -37,7 +37,10 @@ import scala.collection.mutable.ListBuffer * * @todo Check whether we always check type parameter bounds. */ -abstract class RefChecks extends InfoTransform { +abstract class RefChecks extends InfoTransform with reflect.internal.transform.RefChecks { + + val global: Global // need to repeat here because otherwise last mixin defines global as + // SymbolTable. If we had DOT this would not be an issue import global._ import definitions._ @@ -51,11 +54,10 @@ abstract class RefChecks extends InfoTransform { new RefCheckTransformer(unit) override def changesBaseClasses = false - def transformInfo(sym: Symbol, tp: Type): Type = - if (sym.isModule && !sym.isStatic) { - sym setFlag (lateMETHOD | STABLE) - NullaryMethodType(tp) - } else tp + override def transformInfo(sym: Symbol, tp: Type): Type = { + if (sym.isModule && !sym.isStatic) sym setFlag (lateMETHOD | STABLE) + super.transformInfo(sym, tp) + } val toJavaRepeatedParam = new TypeMap { def apply(tp: Type) = tp match { -- cgit v1.2.3