Skip to content

Commit

Permalink
Merge pull request #3409 from xeno-by/ticket/6411
Browse files Browse the repository at this point in the history
SI-6411 SI-7328 value class fixes for runtime reflection
  • Loading branch information
retronym committed Feb 10, 2014
2 parents 677149b + 6c7ceb6 commit 5b3f0e6
Show file tree
Hide file tree
Showing 17 changed files with 407 additions and 108 deletions.
4 changes: 3 additions & 1 deletion src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)

def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase

def erasurePhase: Phase = if (currentRun.isDefined) currentRun.erasurePhase else NoPhase

// platform specific elements

protected class GlobalPlatform extends {
Expand Down Expand Up @@ -527,7 +529,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
} with Erasure

// phaseName = "posterasure"
object postErasure extends {
override object postErasure extends {
val global: Global.this.type = Global.this
val runsAfter = List("erasure")
val runsRightAfter = Some("erasure")
Expand Down
12 changes: 1 addition & 11 deletions src/compiler/scala/tools/nsc/transform/PostErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package transform
/** This phase maps ErasedValueTypes to the underlying unboxed representation and
* performs peephole optimizations.
*/
trait PostErasure extends InfoTransform with TypingTransformers {
trait PostErasure extends InfoTransform with TypingTransformers with scala.reflect.internal.transform.PostErasure {
val global: Global

import global._
Expand All @@ -19,16 +19,6 @@ trait PostErasure extends InfoTransform with TypingTransformers {
def newTransformer(unit: CompilationUnit): Transformer = new PostErasureTransformer(unit)
override def changesBaseClasses = false

object elimErasedValueType extends TypeMap {
def apply(tp: Type) = tp match {
case ConstantType(Constant(tp: Type)) => ConstantType(Constant(apply(tp)))
case ErasedValueType(_, underlying) => underlying
case _ => mapOver(tp)
}
}

def transformInfo(sym: Symbol, tp: Type) = elimErasedValueType(tp)

class PostErasureTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
override def transform(tree: Tree) = {
def finish(res: Tree) = logResult(s"Posterasure reduction\n Old: $tree\n New")(res)
Expand Down
7 changes: 4 additions & 3 deletions src/compiler/scala/tools/reflect/ReflectGlobal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ class ReflectGlobal(currentSettings: Settings, reporter: Reporter, override val
extends Global(currentSettings, reporter) with scala.tools.reflect.ReflectSetup with scala.reflect.runtime.SymbolTable {

override def transformedType(sym: Symbol) =
erasure.transformInfo(sym,
uncurry.transformInfo(sym,
refChecks.transformInfo(sym, sym.info)))
postErasure.transformInfo(sym,
erasure.transformInfo(sym,
uncurry.transformInfo(sym,
refChecks.transformInfo(sym, sym.info))))

override def isCompilerUniverse = true

Expand Down
3 changes: 3 additions & 0 deletions src/reflect/scala/reflect/internal/Required.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import settings.MutableSettings

trait Required { self: SymbolTable =>
def picklerPhase: Phase

def erasurePhase: Phase

def settings: MutableSettings

@deprecated("Interactive is implemented with a custom Global; this flag is ignored", "2.11.0") def forInteractive = false
Expand Down
19 changes: 19 additions & 0 deletions src/reflect/scala/reflect/internal/transform/PostErasure.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package scala.reflect
package internal
package transform

trait PostErasure {
val global: SymbolTable
import global._
import definitions._

object elimErasedValueType extends TypeMap {
def apply(tp: Type) = tp match {
case ConstantType(Constant(tp: Type)) => ConstantType(Constant(apply(tp)))
case ErasedValueType(_, underlying) => underlying
case _ => mapOver(tp)
}
}

def transformInfo(sym: Symbol, tp: Type) = elimErasedValueType(tp)
}
11 changes: 7 additions & 4 deletions src/reflect/scala/reflect/internal/transform/Transforms.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,20 @@ trait Transforms { self: SymbolTable =>
private val refChecksLazy = new Lazy(new { val global: Transforms.this.type = self } with RefChecks)
private val uncurryLazy = new Lazy(new { val global: Transforms.this.type = self } with UnCurry)
private val erasureLazy = new Lazy(new { val global: Transforms.this.type = self } with Erasure)
private val postErasureLazy = new Lazy(new { val global: Transforms.this.type = self } with PostErasure)

def refChecks = refChecksLazy.force
def uncurry = uncurryLazy.force
def erasure = erasureLazy.force
def postErasure = postErasureLazy.force

def transformedType(sym: Symbol) =
erasure.transformInfo(sym,
uncurry.transformInfo(sym,
refChecks.transformInfo(sym, sym.info)))
postErasure.transformInfo(sym,
erasure.transformInfo(sym,
uncurry.transformInfo(sym,
refChecks.transformInfo(sym, sym.info))))

def transformedType(tpe: Type) =
erasure.scalaErasure(uncurry.uncurry(tpe))
postErasure.elimErasedValueType(erasure.scalaErasure(uncurry.uncurry(tpe)))

}
240 changes: 153 additions & 87 deletions src/reflect/scala/reflect/runtime/JavaMirrors.scala

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/reflect/scala/reflect/runtime/JavaUniverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class JavaUniverse extends internal.SymbolTable with JavaUniverseForce with Refl

override def inform(msg: String): Unit = log(msg)
def picklerPhase = internal.SomePhase
def erasurePhase = internal.SomePhase

lazy val settings = new Settings
private val isLogging = sys.props contains "scala.debug.reflect"

Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/runtime/ReflectionUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import scala.reflect.internal.util.AbstractFileClassLoader

/** A few java-reflection oriented utility functions useful during reflection bootstrapping.
*/
private[scala] object ReflectionUtils {
object ReflectionUtils {
// Unwraps some chained exceptions which arise during reflective calls.
def unwrapThrowable(x: Throwable): Throwable = x match {
case _: InvocationTargetException | // thrown by reflectively invoked method or constructor
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/reflection-magicsymbols-invoke.check
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ it's important to print the list of AnyVal's members
if some of them change (possibly, adding and/or removing magic symbols), we must update this test
constructor AnyVal: ()AnyVal
method getClass: ()Class[_ <: AnyVal]
testing AnyVal.<init>: class java.lang.InstantiationException: null
testing AnyVal.<init>: class scala.ScalaReflectionException: unsupported symbol constructor AnyVal when invoking bytecodeless method mirror for scala.AnyVal.<init>(): AnyVal (bound to null)
testing AnyVal.getClass: class scala.ScalaReflectionException: expected a member of class Integer, you provided method scala.AnyVal.getClass
============
AnyRef
Expand Down
96 changes: 96 additions & 0 deletions test/files/run/t6411a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
meth = method yg_1
as seen by Scala reflection: def yg_1[T](y: Y[T]): T
as seen by Java reflection: public java.lang.Object a$.yg_1(java.lang.Object)
result = 1
meth = method yg_1
as seen by Scala reflection: def yg_1[T](y: Y[T]): T
as seen by Java reflection: public java.lang.Object a$.yg_1(java.lang.Object)
result = 1
meth = method yi_2
as seen by Scala reflection: def yi_2(y: Y[Int]): Int
as seen by Java reflection: public int a$.yi_2(java.lang.Integer)
result = 2
meth = method yi_2
as seen by Scala reflection: def yi_2(y: Y[Int]): Int
as seen by Java reflection: public int a$.yi_2(java.lang.Integer)
result = class java.lang.IllegalArgumentException: argument type mismatch
meth = method ys_3
as seen by Scala reflection: def ys_3(y: Y[String]): String
as seen by Java reflection: public java.lang.String a$.ys_3(java.lang.String)
result = class java.lang.IllegalArgumentException: argument type mismatch
meth = method ys_3
as seen by Scala reflection: def ys_3(y: Y[String]): String
as seen by Java reflection: public java.lang.String a$.ys_3(java.lang.String)
result = 3
meth = method ya_4
as seen by Scala reflection: def ya_4(ys: Array[Y[String]]): List[String]
as seen by Java reflection: public scala.collection.immutable.List a$.ya_4(Y[])
result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
meth = method ya_4
as seen by Scala reflection: def ya_4(ys: Array[Y[String]]): List[String]
as seen by Java reflection: public scala.collection.immutable.List a$.ya_4(Y[])
result = List(4)
meth = method yl_5
as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String]
as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List)
result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
meth = method yl_5
as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String]
as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List)
result = List(5)
meth = method yni_7
as seen by Scala reflection: def yni_7(y: => Y[Int]): Int
as seen by Java reflection: public int a$.yni_7(scala.Function0)
result = 7
meth = method yns_8
as seen by Scala reflection: def yns_8(y: => Y[String]): String
as seen by Java reflection: public java.lang.String a$.yns_8(scala.Function0)
result = 8
meth = method zg_1
as seen by Scala reflection: def zg_1[T](z: Z[T]): T
as seen by Java reflection: public java.lang.Object a$.zg_1(Z)
result = 1
meth = method zg_1
as seen by Scala reflection: def zg_1[T](z: Z[T]): T
as seen by Java reflection: public java.lang.Object a$.zg_1(Z)
result = 1
meth = method zi_2
as seen by Scala reflection: def zi_2(z: Z[Int]): Int
as seen by Java reflection: public int a$.zi_2(Z)
result = 2
meth = method zi_2
as seen by Scala reflection: def zi_2(z: Z[Int]): Int
as seen by Java reflection: public int a$.zi_2(Z)
result = class java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
meth = method zs_3
as seen by Scala reflection: def zs_3(z: Z[String]): String
as seen by Java reflection: public java.lang.String a$.zs_3(Z)
result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
meth = method zs_3
as seen by Scala reflection: def zs_3(z: Z[String]): String
as seen by Java reflection: public java.lang.String a$.zs_3(Z)
result = 3
meth = method za_4
as seen by Scala reflection: def za_4(zs: Array[Z[String]]): List[String]
as seen by Java reflection: public scala.collection.immutable.List a$.za_4(Z[])
result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
meth = method za_4
as seen by Scala reflection: def za_4(zs: Array[Z[String]]): List[String]
as seen by Java reflection: public scala.collection.immutable.List a$.za_4(Z[])
result = List(4)
meth = method zl_5
as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String]
as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List)
result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
meth = method zl_5
as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String]
as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List)
result = List(5)
meth = method zni_7
as seen by Scala reflection: def zni_7(z: => Z[Int]): Int
as seen by Java reflection: public int a$.zni_7(scala.Function0)
result = 7
meth = method zns_8
as seen by Scala reflection: def zns_8(z: => Z[String]): String
as seen by Java reflection: public java.lang.String a$.zns_8(scala.Function0)
result = 8
81 changes: 81 additions & 0 deletions test/files/run/t6411a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{currentMirror => cm}
import scala.language.reflectiveCalls

class Y[T](val i: T) extends AnyVal {
override def toString = s"Y($i)"
}
class Z[T](val i: T) extends AnyRef {
override def toString = s"Z($i)"
}

object a {
def yg_1[T](y: Y[T]) = y.i
def yi_2(y: Y[Int]) = y.i
def ys_3(y: Y[String]) = y.i
def ya_4(ys: Array[Y[String]]) = ys.toList.map(_.i)
def yl_5(ys: List[Y[String]]) = ys.map(_.i)
def yv_6(ys: Y[String]*) = ys.toList.map(_.i)
def yni_7(y: => Y[Int]) = y.i
def yns_8(y: => Y[String]) = y.i

def zg_1[T](z: Z[T]) = z.i
def zi_2(z: Z[Int]) = z.i
def zs_3(z: Z[String]) = z.i
def za_4(zs: Array[Z[String]]) = zs.toList.map(_.i)
def zl_5(zs: List[Z[String]]) = zs.map(_.i)
def zv_6(zs: Z[String]*) = zs.toList.map(_.i)
def zni_7(z: => Z[Int]) = z.i
def zns_8(z: => Z[String]) = z.i
}

object Test extends App {
def test(methName: String, arg: Any) = {
val moduleA = cm.reflect(a)
val msym = moduleA.symbol.typeSignature.declaration(TermName(methName)).asMethod
println(s"meth = $msym")
val mmirror = moduleA.reflectMethod(msym)
val mresult =
try { mmirror(arg) }
catch {
case ex: Exception =>
val ex1 = scala.reflect.runtime.ReflectionUtils.unwrapThrowable(ex)
s"${ex1.getClass}: ${ex1.getMessage}"
}
println(s"as seen by Scala reflection: ${msym.asInstanceOf[scala.reflect.internal.Symbols#Symbol].defString}")
println(s"as seen by Java reflection: ${mmirror.asInstanceOf[{val jmeth: java.lang.reflect.Method}].jmeth}")
println(s"result = $mresult")
}

test("yg_1", new Y(1))
test("yg_1", new Y("1"))
test("yi_2", new Y(2))
test("yi_2", new Y("2"))
test("ys_3", new Y(3))
test("ys_3", new Y("3"))
test("ya_4", Array(new Y(4)))
test("ya_4", Array(new Y("4")))
test("yl_5", List(new Y(5)))
test("yl_5", List(new Y("5")))
// FIXME: disabled because of SI-7056
// test("yv_6", new Y(6))
// test("yv_6", new Y("6"))
test("yni_7", new Y(7))
test("yns_8", new Y("8"))

test("zg_1", new Z(1))
test("zg_1", new Z("1"))
test("zi_2", new Z(2))
test("zi_2", new Z("2"))
test("zs_3", new Z(3))
test("zs_3", new Z("3"))
test("za_4", Array(new Z(4)))
test("za_4", Array(new Z("4")))
test("zl_5", List(new Z(5)))
test("zl_5", List(new Z("5")))
// FIXME: disabled because of SI-7056
// test("zv_6", new Z(6))
// test("zv_6", new Z("6"))
test("zni_7", new Z(7))
test("zns_8", new Z("8"))
}
1 change: 1 addition & 0 deletions test/files/run/t6411b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Bar(Foo(3))
12 changes: 12 additions & 0 deletions test/files/run/t6411b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.reflect.runtime.universe._

case class Foo(n: Int) extends AnyVal
case class Bar(foo: Foo)

object Test extends App {
val mirror = runtimeMirror(getClass.getClassLoader)
val cm = mirror.reflectClass(typeOf[Bar].typeSymbol.asClass)
val ctor = typeOf[Bar].declaration(nme.CONSTRUCTOR).asMethod
val ctorm = cm.reflectConstructor(ctor)
println(ctorm(Foo(3)))
}
4 changes: 4 additions & 0 deletions test/files/run/t7328.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Foo
Foo(3)
Foo(3)
Foo(5)
18 changes: 18 additions & 0 deletions test/files/run/t7328.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{currentMirror => cm}

case class Foo(x: Int) extends AnyVal
case class Bar(foo: Foo)

object Test extends App {
val foo = typeOf[Bar].declaration(TermName("foo")).asMethod
println(foo.returnType) // Foo

val bar = Bar(Foo(3))
println(bar.foo) // Foo(3)

val im = cm.reflect(bar)
println(im.reflectField(foo).get) // incorrectly gives java.lang.Integer(3) not Foo(3)
im.reflectField(foo).set(Foo(5)) // java.lang.IllegalArgumentException: Can not set int field Bar.foo to Foo
println(im.reflectMethod(foo)()) // incorrectly gives java.lang.Integer(3) not Foo(3)
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class SymbolTableForUnitTesting extends SymbolTable {

// Members declared in scala.reflect.internal.Required
def picklerPhase: scala.reflect.internal.Phase = SomePhase
def erasurePhase: scala.reflect.internal.Phase = SomePhase

// Members declared in scala.reflect.internal.SymbolTable
def currentRunId: Int = 1
Expand Down

0 comments on commit 5b3f0e6

Please sign in to comment.