diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2013-01-31 21:09:24 +0100 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2014-01-24 23:01:20 +0300 |
commit | 8b87327d4a3e8145c5716ec4883c497d86739281 (patch) | |
tree | dcbe3893d3e2cff2c784d7622a413f520bf41ad0 /src/reflect | |
parent | f22ddce265e8622e95f5e9cab4d38168bf2c3bf8 (diff) | |
download | scala-8b87327d4a3e8145c5716ec4883c497d86739281.tar.gz scala-8b87327d4a3e8145c5716ec4883c497d86739281.tar.bz2 scala-8b87327d4a3e8145c5716ec4883c497d86739281.zip |
SI-6411 reflection is now aware of posterasure
The `transformedType` method, which is used to bring Scala types to Java
world, was written in pre-valueclass times. Therefore, this method only
called transforms from erasure, uncurry and refChecks.
Now runtime reflection becomes aware of posterasure and as a consequence
methods, which have value classes in their signatures, can be called
without having to wrap them in catch-a-crash clause.
Another facet to this fix was the realization that value classes need
to be unwrapped, e.g. C(2) needs to be transformed to just 2, when they
are used naked in method signatures (i.e. `c` in `def foo(c: C)` needs
to be unwrapped, whereas `cs: List[C]`, `cs: C*` and even `cs: Array[C]`
do not).
Diffstat (limited to 'src/reflect')
6 files changed, 68 insertions, 11 deletions
diff --git a/src/reflect/scala/reflect/internal/Required.scala b/src/reflect/scala/reflect/internal/Required.scala index 14db252a16..009bc39d4c 100644 --- a/src/reflect/scala/reflect/internal/Required.scala +++ b/src/reflect/scala/reflect/internal/Required.scala @@ -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 diff --git a/src/reflect/scala/reflect/internal/transform/PostErasure.scala b/src/reflect/scala/reflect/internal/transform/PostErasure.scala new file mode 100644 index 0000000000..f0c7d0f050 --- /dev/null +++ b/src/reflect/scala/reflect/internal/transform/PostErasure.scala @@ -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) +} diff --git a/src/reflect/scala/reflect/internal/transform/Transforms.scala b/src/reflect/scala/reflect/internal/transform/Transforms.scala index fa185db22f..296ccde443 100644 --- a/src/reflect/scala/reflect/internal/transform/Transforms.scala +++ b/src/reflect/scala/reflect/internal/transform/Transforms.scala @@ -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))) } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 7cc5176507..4a95791284 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -16,7 +16,7 @@ import java.io.IOException import scala.reflect.internal.{ MissingRequirementError, JavaAccFlags, JMethodOrConstructor } import internal.pickling.ByteCodecs import internal.pickling.UnPickler -import scala.collection.mutable.{ HashMap, ListBuffer } +import scala.collection.mutable.{ HashMap, ListBuffer, ArrayBuffer } import internal.Flags._ import ReflectionUtils._ import scala.language.existentials @@ -312,13 +312,17 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni bytecodelessMethodOwners(meth.owner) && !bytecodefulObjectMethods(meth) } + private def isByNameParam(p: Type) = isByNameParamType(p) + private def isValueClassParam(p: Type) = p.typeSymbol.isDerivedValueClass + // unlike other mirrors, method mirrors are created by a factory // that's because we want to have decent performance // therefore we move special cases into separate subclasses // rather than have them on a hot path them in a unified implementation of the `apply` method private def mkJavaMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): JavaMethodMirror = { + def existsParam(pred: Type => Boolean) = symbol.paramss.flatten.map(_.info).exists(pred) if (isBytecodelessMethod(symbol)) new JavaBytecodelessMethodMirror(receiver, symbol) - else if (symbol.paramss.flatten exists (p => isByNameParamType(p.info))) new JavaByNameMethodMirror(receiver, symbol) + else if (existsParam(isByNameParam) || existsParam(isValueClassParam)) new JavaTransformingMethodMirror(receiver, symbol) else { symbol.paramss.flatten.length match { case 0 => new JavaVanillaMethodMirror0(receiver, symbol) @@ -379,12 +383,38 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) } - private class JavaByNameMethodMirror(val receiver: Any, symbol: MethodSymbol) + // caches MethodSymbol metadata, so that we minimize the work that needs to be done during Mirror.apply + // TODO: vararg is only supported in the last parameter list (SI-6182), so we don't need to worry about the rest for now + private class MethodMetadata(symbol: MethodSymbol) { + private val params = symbol.paramss.flatten.toArray + val isByName = params.map(p => isByNameParam(p.info)) + val isValueClass = params.map(p => isValueClassParam(p.info)) + private def valueClassUnboxer(p: Symbol) = { + val fields @ (field :: _) = p.info.declarations.collect{ case ts: TermSymbol if ts.isParamAccessor && ts.isMethod => ts }.toList + assert(fields.length == 1, s"$p: $fields") + runtimeClass(p.info).getDeclaredMethod(field.name.toString) + } + val paramUnboxers = params.map(p => if (isValueClassParam(p.info)) valueClassUnboxer(p) else null) + val paramCount = params.length + } + + private class JavaTransformingMethodMirror(val receiver: Any, symbol: MethodSymbol, metadata: MethodMetadata) extends JavaMethodMirror(symbol) { - def bind(newReceiver: Any) = new JavaByNameMethodMirror(newReceiver, symbol) + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new MethodMetadata(symbol)) + override def bind(newReceiver: Any) = new JavaTransformingMethodMirror(newReceiver, symbol, metadata) + def apply(args: Any*): Any = { - val transformed = map2(args.toList, symbol.paramss.flatten)((arg, param) => if (isByNameParamType(param.info)) () => arg else arg) - jinvoke(jmeth, receiver, transformed) + import metadata._ + val args1 = new Array[Any](args.length) + var i = 0 + while (i < args1.length) { + val arg = args(i) + if (i >= paramCount) args1(i) = arg // don't transform varargs + else if (isByName(i)) args1(i) = () => arg // don't transform by-name value class params + else if (isValueClass(i)) args1(i) = paramUnboxers(i).invoke(arg) + i += 1 + } + jinvoke(jmeth, receiver, args1) } } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index cfd66744ff..f6556a442d 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -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" diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala index 813c0e1386..d642b25127 100644 --- a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala +++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala @@ -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 |