Skip to content

Implement getClass #734

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 27, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class Compiler {
new LinkScala2ImplClasses,
new CapturedVars, // capturedVars has a transformUnit: no phases should introduce local mutable vars here
new Constructors,
new FunctionalInterfaces),
new FunctionalInterfaces,
new GetClass), // getClass transformation should be applied to specialized methods
List(new LambdaLift, // in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
new ElimStaticThis,
new Flatten,
Expand Down
49 changes: 49 additions & 0 deletions src/dotty/tools/dotc/transform/GetClass.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package dotty.tools.dotc
package transform

import ast.tpd
import core.Contexts.Context
import core.StdNames.nme
import core.Symbols.TermSymbol
import core.Phases.Phase
import TreeTransforms.{MiniPhaseTransform, TransformerInfo}

/** Rewrite `getClass` calls as follow:
*
* For every instance of primitive class C whose boxed class is called B:
* instanceC.getClass -> B.TYPE
* For every instance of non-primitive class D:
* instanceD.getClass -> instanceD.getClass
*/
class GetClass extends MiniPhaseTransform {
import tpd._

override def phaseName: String = "getClass"

override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure])

override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
import ast.Trees._

tree match {
case Apply(Select(qual, nme.getClass_), Nil) =>
val defn = ctx.definitions
val claz = qual.tpe.classSymbol
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some types don't have symbols before erasure. I'd add

def runsAfter = Set(classOf[Erasure])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that order in Compiler is enough for phases ordering. Why do we explicitly need runsAfter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that order in Compiler is enough for phases ordering

It is enough.

Why do we explicitly need runsAfter?

We have a checks, before building compiler pipeline that ensures that those runsAfter are followed. This is very handy if someone wants to play around with phase ordering, as it makes dependencies explicit.


def TYPE(module: TermSymbol) = ref(module).select(nme.TYPE_).ensureConforms(tree.tpe)
claz match {
case defn.BooleanClass => TYPE(defn.BoxedBooleanModule)
case defn.ByteClass => TYPE(defn.BoxedByteModule)
case defn.ShortClass => TYPE(defn.BoxedShortModule)
case defn.CharClass => TYPE(defn.BoxedCharModule)
case defn.IntClass => TYPE(defn.BoxedIntModule)
case defn.LongClass => TYPE(defn.BoxedLongModule)
case defn.FloatClass => TYPE(defn.BoxedFloatModule)
case defn.DoubleClass => TYPE(defn.BoxedDoubleModule)
case defn.UnitClass => TYPE(defn.BoxedVoidModule)
case _ => tree
}
case _ => tree
}
}
}
26 changes: 26 additions & 0 deletions tests/run/getclass.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Value types:
void
boolean
byte
short
char
int
long
float
double

Class types:
class SomeClass
class ValueClass
class scala.collection.immutable.$colon$colon
class scala.Tuple2

Arrays:
class [Lscala.runtime.BoxedUnit;
class [I
class [D
class [Lscala.collection.immutable.List;

Functions:
class Test$$$Lambda$1
class Test$$$Lambda$2
41 changes: 41 additions & 0 deletions tests/run/getclass.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class ValueClass(val i: Integer) extends AnyVal
class SomeClass

object Test {
def main(args: Array[String]): Unit = {
val cls: Predef.Class[_] = new SomeClass().getClass
val valCls: Predef.Class[_] = new ValueClass(1).getClass
val iCls: Class[Int] = 1.getClass
val f1: Function2[Int, Int, Unit] = (a: Int, b: Int) => println(a + b)
val f2: Function1[Int, Boolean] = (a: Int) => a % 2 == 0

println("Value types:")
println(().getClass)
println(true.getClass)
println(1.asInstanceOf[Byte].getClass)
println(1.asInstanceOf[Short].getClass)
println('a'.getClass)
println(1.getClass)
println(1L.getClass)
println(1f.getClass)
println(1d.getClass)

println("\nClass types:")
println(new SomeClass().getClass)
println(new ValueClass(1).getClass)
println(List(Array(1f)).getClass)
println(("a", Map(1 -> "b")).getClass)

println("\nArrays:")
println(Array(()).getClass)
println(Array(1).getClass)
println(Array(1d).getClass)
println(Array(List("1")).getClass)

println("\nFunctions:")
// FunctionN.getClass.toString has form of "class Test$$$Lambda$N/1349414238",
// but number (1349414238) depends on environment
println(f1.getClass.toString.takeWhile(_ != '/'))
println(f2.getClass.toString.takeWhile(_ != '/'))
}
}