diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/CleanUp.scala | 323 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 5 |
3 files changed, 113 insertions, 217 deletions
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index c105dcbdff..fe9165203f 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -163,8 +163,6 @@ trait ScalaSettings extends AbsScalaSettings val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (scala.reflect.internal.util.Statistics.enabled = _) val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat val stopBefore = PhasesSetting ("-Ystop-before", "Stop before") - val refinementMethodDispatch - = ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.") val Ymemberpos = StringSetting ("-Yshow-member-pos", "output style", "Show start and end positions of members", "") withPostSetHook (_ => Yrangepos.value = true) val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index d498949b03..d159408f55 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -47,15 +47,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL { private var localTyper: analyzer.Typer = null - private object MethodDispatchType extends scala.Enumeration { - val NO_CACHE, MONO_CACHE, POLY_CACHE = Value - } - import MethodDispatchType.{ NO_CACHE, MONO_CACHE, POLY_CACHE } - private def dispatchType() = settings.refinementMethodDispatch.value match { - case "no-cache" => NO_CACHE - case "mono-cache" => MONO_CACHE - case "poly-cache" => POLY_CACHE - } private def typedWithPos(pos: Position)(tree: Tree) = localTyper.typedPos(pos)(tree) @@ -113,133 +104,65 @@ abstract class CleanUp extends Transform with ast.TreeDSL { def fromTypesToClassArrayLiteral(paramTypes: List[Type]): Tree = ArrayValue(TypeTree(ClassClass.tpe), paramTypes map LIT) - /* ... */ - def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = dispatchType() match { - case NO_CACHE => - - /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)": - - var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B]) - - def reflMethod$Method(forReceiver: JClass[_]): JMethod = - forReceiver.getMethod("xyz", reflParams$Cache) - - */ - - val reflParamsCacheSym: Symbol = - addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true) - - addStaticMethodToClass((_, forReceiverSym) => - gen.mkMethodCall(REF(forReceiverSym), Class_getMethod, Nil, List(LIT(method), REF(reflParamsCacheSym))) - ) - - case MONO_CACHE => - - /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)" - (but with a SoftReference wrapping reflClass$Cache, similarly in the poly Cache) : - - var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B]) - - var reflMethod$Cache: JMethod = null - - var reflClass$Cache: JClass[_] = null - - def reflMethod$Method(forReceiver: JClass[_]): JMethod = { - if (reflClass$Cache != forReceiver) { - reflMethod$Cache = forReceiver.getMethod("xyz", reflParams$Cache) - reflClass$Cache = forReceiver - } - reflMethod$Cache - } - - */ - - val reflParamsCacheSym: Symbol = - addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true) - - val reflMethodCacheSym: Symbol = - addStaticVariableToClass(nme.reflMethodCacheName, MethodClass.tpe, NULL, false) - - val reflClassCacheSym: Symbol = - addStaticVariableToClass(nme.reflClassCacheName, SoftReferenceClass.tpe, NULL, false) - - def isCacheEmpty(receiver: Symbol): Tree = - reflClassCacheSym.IS_NULL() OR (reflClassCacheSym.GET() OBJ_NE REF(receiver)) - - addStaticMethodToClass((_, forReceiverSym) => - BLOCK( - IF (isCacheEmpty(forReceiverSym)) THEN BLOCK( - REF(reflMethodCacheSym) === ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym))) , - REF(reflClassCacheSym) === gen.mkSoftRef(REF(forReceiverSym)), - UNIT - ) ENDIF, - REF(reflMethodCacheSym) - ) - ) - - case POLY_CACHE => + def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = { + /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)" + (SoftReference so that it does not interfere with classloader garbage collection, + see ticket #2365 for details): - /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)" - (SoftReference so that it does not interfere with classloader garbage collection, see ticket - #2365 for details): + var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B]) - var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B]) + var reflPoly$Cache: SoftReference[scala.runtime.MethodCache] = new SoftReference(new EmptyMethodCache()) - var reflPoly$Cache: SoftReference[scala.runtime.MethodCache] = new SoftReference(new EmptyMethodCache()) + def reflMethod$Method(forReceiver: JClass[_]): JMethod = { + var methodCache: MethodCache = reflPoly$Cache.find(forReceiver) + if (methodCache eq null) { + methodCache = new EmptyMethodCache + reflPoly$Cache = new SoftReference(methodCache) + } + var method: JMethod = methodCache.find(forReceiver) + if (method ne null) + return method + else { + method = ScalaRunTime.ensureAccessible(forReceiver.getMethod("xyz", reflParams$Cache)) + reflPoly$Cache = new SoftReference(methodCache.add(forReceiver, method)) + return method + } + } + */ - def reflMethod$Method(forReceiver: JClass[_]): JMethod = { - var methodCache: MethodCache = reflPoly$Cache.find(forReceiver) - if (methodCache eq null) { - methodCache = new EmptyMethodCache - reflPoly$Cache = new SoftReference(methodCache) - } - var method: JMethod = methodCache.find(forReceiver) - if (method ne null) - return method - else { - method = ScalaRunTime.ensureAccessible(forReceiver.getMethod("xyz", reflParams$Cache)) - reflPoly$Cache = new SoftReference(methodCache.add(forReceiver, method)) - return method - } - } + val reflParamsCacheSym: Symbol = + addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true) - */ + def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe))) + val reflPolyCacheSym: Symbol = addStaticVariableToClass(nme.reflPolyCacheName, SoftReferenceClass.tpe, mkNewPolyCache, false) - val reflParamsCacheSym: Symbol = - addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true) + def getPolyCache = gen.mkCast(fn(REF(reflPolyCacheSym), nme.get), MethodCacheClass.tpe) - def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe))) - val reflPolyCacheSym: Symbol = ( - addStaticVariableToClass(nme.reflPolyCacheName, SoftReferenceClass.tpe, mkNewPolyCache, false) - ) - def getPolyCache = gen.mkCast(fn(REF(reflPolyCacheSym), nme.get), MethodCacheClass.tpe) + addStaticMethodToClass((reflMethodSym, forReceiverSym) => { + val methodCache = reflMethodSym.newVariable(mkTerm("methodCache"), ad.pos) setInfo MethodCacheClass.tpe + val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe - addStaticMethodToClass((reflMethodSym, forReceiverSym) => { - val methodCache = reflMethodSym.newVariable(mkTerm("methodCache"), ad.pos) setInfo MethodCacheClass.tpe - val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe + BLOCK( + VAL(methodCache) === getPolyCache, + IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK( + REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)), + REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache)) + ) ENDIF, + VAL(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)), + IF (REF(methodSym) OBJ_NE NULL) . + THEN (Return(REF(methodSym))) + ELSE { + def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym))) + def cacheRHS = ((REF(methodCache) DOT methodCache_add)(REF(forReceiverSym), REF(methodSym))) BLOCK( - VAL(methodCache) === getPolyCache, - IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK( - REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)), - REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache)) - ) ENDIF, - - VAL(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)), - IF (REF(methodSym) OBJ_NE NULL) . - THEN (Return(REF(methodSym))) - ELSE { - def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym))) - def cacheRHS = ((REF(methodCache) DOT methodCache_add)(REF(forReceiverSym), REF(methodSym))) - BLOCK( - REF(methodSym) === (REF(ensureAccessibleMethod) APPLY (methodSymRHS)), - REF(reflPolyCacheSym) === gen.mkSoftRef(cacheRHS), - Return(REF(methodSym)) - ) - } + REF(methodSym) === (REF(ensureAccessibleMethod) APPLY (methodSymRHS)), + REF(reflPolyCacheSym) === gen.mkSoftRef(cacheRHS), + Return(REF(methodSym)) ) - }) - + } + ) + }) } /* ### HANDLING METHODS NORMALLY COMPILED TO OPERATORS ### */ @@ -394,99 +317,75 @@ abstract class CleanUp extends Transform with ast.TreeDSL { } } - if (settings.refinementMethodDispatch.value == "invoke-dynamic") { -/* val guardCallSite: Tree = { - val cachedClass = addStaticVariableToClass("cachedClass", definitions.ClassClass.tpe, EmptyTree) - val tmpVar = currentOwner.newVariable(ad.pos, unit.freshTermName(ad.pos, "x")).setInfo(definitions.AnyRefClass.tpe) - atPos(ad.pos)(Block(List( - ValDef(tmpVar, transform(qual))), - If(Apply(Select(gen.mkAttributedRef(cachedClass), nme.EQ), List(getClass(Ident(tmpVar)))), - Block(List(Assign(gen.mkAttributedRef(cachedClass), getClass(Ident(tmpVar)))), - treeCopy.ApplyDynamic(ad, Ident(tmpVar), transformTrees(params))), - EmptyTree))) - } - //println(guardCallSite) -*/ - localTyper.typed(treeCopy.ApplyDynamic(ad, transform(qual), transformTrees(params))) - } - else { - - /* ### BODY OF THE TRANSFORMATION -> remember we're in case ad@ApplyDynamic(qual, params) ### */ - - /* This creates the tree that does the reflective call (see general comment - * on the apply-dynamic tree for its format). This tree is simply composed - * of three successive calls, first to getClass on the callee, then to - * getMethod on the class, then to invoke on the method. - * - getMethod needs an array of classes for choosing one amongst many - * overloaded versions of the method. This is provided by paramTypeClasses - * and must be done on the static type as Scala's dispatching is static on - * the parameters. - * - invoke needs an array of AnyRefs that are the method's arguments. The - * erasure phase guarantees that any parameter passed to a dynamic apply - * is compatible (through boxing). Boxed ints et al. is what invoke expects - * when the applied method expects ints, hence no change needed there. - * - in the end, the result of invoke must be fixed, again to deal with arrays. - * This is provided by fixResult. fixResult will cast the invocation's result - * to the method's return type, which is generally ok, except when this type - * is a value type (int et al.) in which case it must cast to the boxed version - * because invoke only returns object and erasure made sure the result is - * expected to be an AnyRef. */ - val t: Tree = { - val (mparams, resType) = ad.symbol.tpe match { - case MethodType(mparams, resType) => - assert(params.length == mparams.length, ((params, mparams))) - (mparams, resType) - case tpe @ OverloadedType(pre, alts) => - unit.warning(ad.pos, s"Overloaded type reached the backend! This is a bug in scalac.\n Symbol: ${ad.symbol}\n Overloads: $tpe\n Arguments: " + ad.args.map(_.tpe)) - alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match { - case mt @ MethodType(mparams, resType) :: Nil => - unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt) - (mparams, resType) - case _ => - unit.error(ad.pos, "Cannot resolve overload.") - (Nil, NoType) - } - } - typedPos { - val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe - qual = REF(sym) - - BLOCK( - VAL(sym) === qual0, - callAsReflective(mparams map (_.tpe), resType) - ) - } + { + + /* ### BODY OF THE TRANSFORMATION -> remember we're in case ad@ApplyDynamic(qual, params) ### */ + + /* This creates the tree that does the reflective call (see general comment + * on the apply-dynamic tree for its format). This tree is simply composed + * of three successive calls, first to getClass on the callee, then to + * getMethod on the class, then to invoke on the method. + * - getMethod needs an array of classes for choosing one amongst many + * overloaded versions of the method. This is provided by paramTypeClasses + * and must be done on the static type as Scala's dispatching is static on + * the parameters. + * - invoke needs an array of AnyRefs that are the method's arguments. The + * erasure phase guarantees that any parameter passed to a dynamic apply + * is compatible (through boxing). Boxed ints et al. is what invoke expects + * when the applied method expects ints, hence no change needed there. + * - in the end, the result of invoke must be fixed, again to deal with arrays. + * This is provided by fixResult. fixResult will cast the invocation's result + * to the method's return type, which is generally ok, except when this type + * is a value type (int et al.) in which case it must cast to the boxed version + * because invoke only returns object and erasure made sure the result is + * expected to be an AnyRef. */ + val t: Tree = { + val (mparams, resType) = ad.symbol.tpe match { + case MethodType(mparams, resType) => + assert(params.length == mparams.length, ((params, mparams))) + (mparams, resType) + case tpe @ OverloadedType(pre, alts) => + unit.warning(ad.pos, s"Overloaded type reached the backend! This is a bug in scalac.\n Symbol: ${ad.symbol}\n Overloads: $tpe\n Arguments: " + ad.args.map(_.tpe)) + alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match { + case mt @ MethodType(mparams, resType) :: Nil => + unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt) + (mparams, resType) + case _ => + unit.error(ad.pos, "Cannot resolve overload.") + (Nil, NoType) + } } + typedPos { + val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe + qual = REF(sym) - /* For testing purposes, the dynamic application's condition - * can be printed-out in great detail. Remove? */ - if (settings.debug) { - def paramsToString(xs: Any*) = xs map (_.toString) mkString ", " - val mstr = ad.symbol.tpe match { - case MethodType(mparams, resType) => - """| with - | - declared parameter types: '%s' - | - passed argument types: '%s' - | - result type: '%s'""" . - stripMargin.format( - paramsToString(mparams), - paramsToString(params), - resType.toString - ) - case _ => "" - } - log( - """Dynamically application '%s.%s(%s)' %s - resulting code: '%s'""".format( - qual, ad.symbol.name, paramsToString(params), mstr, t - ) + BLOCK( + VAL(sym) === qual0, + callAsReflective(mparams map (_.tpe), resType) ) } + } - /* We return the dynamic call tree, after making sure no other - * clean-up transformation are to be applied on it. */ - transform(t) + /* For testing purposes, the dynamic application's condition + * can be printed-out in great detail. Remove? */ + if (settings.debug) { + def paramsToString(xs: Any*) = xs map (_.toString) mkString ", " + val mstr = ad.symbol.tpe match { + case MethodType(mparams, resType) => + sm"""| with + | - declared parameter types: '${paramsToString(mparams)}' + | - passed argument types: '${paramsToString(params)}' + | - result type: '${resType.toString}'""" + case _ => "" + } + log(s"""Dynamically application '$qual.${ad.symbol.name}(${paramsToString(params)})' $mstr - resulting code: '$t'""") } - /* ### END OF DYNAMIC APPLY TRANSFORM ### */ + + /* We return the dynamic call tree, after making sure no other + * clean-up transformation are to be applied on it. */ + transform(t) + /* ### END OF DYNAMIC APPLY TRANSFORM ### */ + } } override def transform(tree: Tree): Tree = tree match { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8303441890..bf5de1548d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5119,10 +5119,9 @@ trait Typers extends Adaptations with Tags { def typedApplyDynamic(tree: ApplyDynamic) = { assert(phase.erasedTypes) - val reflectiveCalls = !(settings.refinementMethodDispatch.value == "invoke-dynamic") val qual1 = typed(tree.qual, AnyRefClass.tpe) - val args1 = tree.args mapConserve (arg => if (reflectiveCalls) typed(arg, AnyRefClass.tpe) else typed(arg)) - treeCopy.ApplyDynamic(tree, qual1, args1) setType (if (reflectiveCalls) AnyRefClass.tpe else tree.symbol.info.resultType) + val args1 = tree.args mapConserve (arg => typed(arg, AnyRefClass.tpe)) + treeCopy.ApplyDynamic(tree, qual1, args1) setType AnyRefClass.tpe } def typedReferenceToBoxed(tree: ReferenceToBoxed) = { |