summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/reflect/internal/transform/Erasure.scala271
-rw-r--r--src/compiler/scala/reflect/internal/transform/RefChecks.scala13
-rw-r--r--src/compiler/scala/reflect/internal/transform/Transforms.scala16
-rw-r--r--src/compiler/scala/reflect/internal/transform/UnCurry.scala70
-rw-r--r--src/compiler/scala/reflect/runtime/ConversionUtil.scala58
-rw-r--r--src/compiler/scala/reflect/runtime/JavaToScala.scala (renamed from src/compiler/scala/reflect/runtime/JavaConversions.scala)65
-rw-r--r--src/compiler/scala/reflect/runtime/ScalaToJava.scala46
-rw-r--r--src/compiler/scala/reflect/runtime/Universe.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala260
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala63
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala14
12 files changed, 510 insertions, 370 deletions
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].<init> : {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/JavaToScala.scala
index e695c5a9cf..e67dbd3ce4 100644
--- a/src/compiler/scala/reflect/runtime/JavaConversions.scala
+++ b/src/compiler/scala/reflect/runtime/JavaToScala.scala
@@ -10,28 +10,12 @@ import internal.ClassfileConstants._
import internal.pickling.UnPickler
import collection.mutable.HashMap
-trait JavaConversions { self: Universe =>
+trait JavaToScala extends ConversionUtil { self: Universe =>
private object unpickler extends UnPickler {
- val global: JavaConversions.this.type = self
+ val global: JavaToScala.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
@@ -104,6 +88,13 @@ trait JavaConversions { self: Universe =>
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
@@ -111,24 +102,12 @@ trait JavaConversions { self: Universe =>
*/
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)
+ (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)
+ }
}
- }
-
- /** 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
@@ -192,7 +171,7 @@ trait JavaConversions { self: Universe =>
/** The Scala type that corresponds to given Java type (to be done)
*/
- def typeToScala(tpe: jType): Type = NoType
+ def jtypeToScala(tpe: jType): Type = NoType
/** The Scala class that corresponds to given Java class without taking
* Scala pickling info into account.
@@ -214,7 +193,7 @@ trait JavaConversions { self: Universe =>
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))
+ .setInfo(jtypeToScala(jfield.getGenericType))
// todo: copy annotations
}
@@ -232,17 +211,5 @@ trait JavaConversions { self: Universe =>
*/
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/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].<init> : {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.
* </li>
* </ol>
+ * 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.
*/
/*</export> */
-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 {