Skip to content

Commit

Permalink
Merge pull request #7879 from retronym/classtag-cache
Browse files Browse the repository at this point in the history
Share instance of ClassTag in a ClassValue based cache
  • Loading branch information
retronym committed Jul 17, 2019
2 parents c50ca9a + 9f7866c commit b5029ab
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 22 deletions.
10 changes: 4 additions & 6 deletions src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,12 @@ trait StdAttachments {
* typechecks to be a macro application. Then we need to unmark it, expand it and try to treat
* its expansion as a macro impl reference.
*/
def unmarkMacroImplRef(tree: Tree): Tree = tree.removeAttachment[MacroImplRefAttachment.type](MacroImplRefAttachmentTag)
def unmarkMacroImplRef(tree: Tree): Tree = tree.removeAttachment[MacroImplRefAttachment.type]

/** Determines whether a tree should or should not be adapted,
* because someone has put MacroImplRefAttachment on it.
*/
def isMacroImplRef(tree: Tree): Boolean = tree.hasAttachment[MacroImplRefAttachment.type](MacroImplRefAttachmentTag)
private[this] val MacroImplRefAttachmentTag: reflect.ClassTag[MacroImplRefAttachment.type] = reflect.classTag[MacroImplRefAttachment.type]
def isMacroImplRef(tree: Tree): Boolean = tree.hasAttachment[MacroImplRefAttachment.type]

/** Since mkInvoke, the applyDynamic/selectDynamic/etc desugarer, is disconnected
* from typedNamedApply, the applyDynamicNamed argument rewriter, the latter
Expand All @@ -176,9 +175,8 @@ trait StdAttachments {
*/
case object DynamicRewriteAttachment
def markDynamicRewrite(tree: Tree): Tree = tree.updateAttachment(DynamicRewriteAttachment)
def unmarkDynamicRewrite(tree: Tree): Tree = tree.removeAttachment[DynamicRewriteAttachment.type](DynamicRewriteAttachmentTag)
def isDynamicRewrite(tree: Tree): Boolean = tree.attachments.get[DynamicRewriteAttachment.type](DynamicRewriteAttachmentTag).isDefined
private[this] val DynamicRewriteAttachmentTag: reflect.ClassTag[DynamicRewriteAttachment.type] = reflect.classTag[DynamicRewriteAttachment.type]
def unmarkDynamicRewrite(tree: Tree): Tree = tree.removeAttachment[DynamicRewriteAttachment.type]
def isDynamicRewrite(tree: Tree): Boolean = tree.attachments.get[DynamicRewriteAttachment.type].isDefined

/**
* Marks a tree that has been adapted by typer and sets the original tree that was in place before.
Expand Down
41 changes: 25 additions & 16 deletions src/library/scala/reflect/ClassTag.scala
Original file line number Diff line number Diff line change
Expand Up @@ -121,29 +121,38 @@ object ClassTag {
val Nothing : ClassTag[scala.Nothing] = Manifest.Nothing
val Null : ClassTag[scala.Null] = Manifest.Null

private[this] val cache = new ClassValue[ClassTag[_]] {
override def computeValue(runtimeClass: jClass[_]): ClassTag[_] = {
runtimeClass match {
case x if x.isPrimitive => primitiveClassTag(runtimeClass)
case ObjectTYPE => ClassTag.Object
case NothingTYPE => ClassTag.Nothing
case NullTYPE => ClassTag.Null
case _ => new GenericClassTag[AnyRef](runtimeClass)
}
}

private def primitiveClassTag[T](runtimeClass: Class[_]): ClassTag[_] = runtimeClass match {
case java.lang.Byte.TYPE => ClassTag.Byte
case java.lang.Short.TYPE => ClassTag.Short
case java.lang.Character.TYPE => ClassTag.Char
case java.lang.Integer.TYPE => ClassTag.Int
case java.lang.Long.TYPE => ClassTag.Long
case java.lang.Float.TYPE => ClassTag.Float
case java.lang.Double.TYPE => ClassTag.Double
case java.lang.Boolean.TYPE => ClassTag.Boolean
case java.lang.Void.TYPE => ClassTag.Unit
}
}

@SerialVersionUID(1L)
private class GenericClassTag[T](val runtimeClass: jClass[_]) extends ClassTag[T] {
override def newArray(len: Int): Array[T] = {
java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]]
}
}

def apply[T](runtimeClass1: jClass[_]): ClassTag[T] =
runtimeClass1 match {
case java.lang.Byte.TYPE => ClassTag.Byte.asInstanceOf[ClassTag[T]]
case java.lang.Short.TYPE => ClassTag.Short.asInstanceOf[ClassTag[T]]
case java.lang.Character.TYPE => ClassTag.Char.asInstanceOf[ClassTag[T]]
case java.lang.Integer.TYPE => ClassTag.Int.asInstanceOf[ClassTag[T]]
case java.lang.Long.TYPE => ClassTag.Long.asInstanceOf[ClassTag[T]]
case java.lang.Float.TYPE => ClassTag.Float.asInstanceOf[ClassTag[T]]
case java.lang.Double.TYPE => ClassTag.Double.asInstanceOf[ClassTag[T]]
case java.lang.Boolean.TYPE => ClassTag.Boolean.asInstanceOf[ClassTag[T]]
case java.lang.Void.TYPE => ClassTag.Unit.asInstanceOf[ClassTag[T]]
case ObjectTYPE => ClassTag.Object.asInstanceOf[ClassTag[T]]
case NothingTYPE => ClassTag.Nothing.asInstanceOf[ClassTag[T]]
case NullTYPE => ClassTag.Null.asInstanceOf[ClassTag[T]]
case _ => new GenericClassTag[T](runtimeClass1)
}
def apply[T](runtimeClass1: jClass[_]): ClassTag[T] = cache.get(runtimeClass1).asInstanceOf[ClassTag[T]]

def unapply[T](ctag: ClassTag[T]): Option[Class[_]] = Some(ctag.runtimeClass)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ClassTagBenchmark {
var refClassTag: ClassTag[_] = null
var otherValue: Object = null
var arraySize: Int = 100
private[this] var refClasses: Array[Class[_]] = _

@Setup def setup(): Unit = {
unitClassTag = classTag[Unit]
Expand All @@ -38,6 +39,7 @@ class ClassTagBenchmark {
doubleClassTag = classTag[Double]
refClassTag = classTag[ClassTagBenchmark]
otherValue = new Object
refClasses = Array(classOf[java.lang.Boolean], classOf[java.lang.Character], classOf[java.lang.Short], classOf[java.lang.Integer], classOf[java.lang.Long], classOf[java.lang.Float], classOf[java.lang.Double])
}

@Benchmark def primitivesNegOnRefClassTag(bh: Blackhole): Any = {
Expand Down Expand Up @@ -86,6 +88,15 @@ class ClassTagBenchmark {

@Benchmark def refClassTagUnapplyNeg2Direct(bh: Blackhole): Any = unapplyDirect(refClassTag, otherValue)

@Benchmark def lookupClassTag(bh: Blackhole): Any = {
var clss = refClasses
var i = 0
while (i < clss.length) {
bh.consume(ClassTag.apply(clss(i)))
i += 1
}
}

def unapplyDirect(ct: ClassTag[_], x: AnyRef): Option[_] = {
if (null != x && (ct.runtimeClass.isInstance(x))) Some(x)
else None
Expand Down
10 changes: 10 additions & 0 deletions test/files/run/classtags-cached.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import reflect.ClassTag

object Test {
def main(args: Array[String]): Unit = {
assert(implicitly[ClassTag[SomeClass]] eq implicitly[ClassTag[SomeClass]])
assert(implicitly[ClassTag[Array[SomeClass]]] eq implicitly[ClassTag[Array[SomeClass]]])
}
}

class SomeClass

0 comments on commit b5029ab

Please sign in to comment.