- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Compiler plugin for detecting usage of Java classes
Hi,
I'm trying to write a compiler plugin, which should basically print the Java classes used in some Scala file.
E. g. when compiling
class A {
val list = new java.util.ArrayList[Int]
System.out.println(list)
}
the plugin should print something like:
A.scala:2: new java.util.ArrayList
A.scala:3: java.lang.System.out.println
I used the basic plugin code from http://www.scala-lang.org/node/140 and as far as I have understood I need to implement the apply method to get this done.
This is the part where I am not sure how to go on. Do I need to match against something or is it enough to ask for the type of every child of unit.body? Should I work with trees or syms? Which compiler phase is the most appropriate one? (Currently the plugin runs after the typer.) Is there something like an isJava method? (If not, looking for “java.” is ok too ...)
Any suggestions?
Thanks!
Simon
I'm trying to write a compiler plugin, which should basically print the Java classes used in some Scala file.
E. g. when compiling
class A {
val list = new java.util.ArrayList[Int]
System.out.println(list)
}
the plugin should print something like:
A.scala:2: new java.util.ArrayList
A.scala:3: java.lang.System.out.println
I used the basic plugin code from http://www.scala-lang.org/node/140 and as far as I have understood I need to implement the apply method to get this done.
This is the part where I am not sure how to go on. Do I need to match against something or is it enough to ask for the type of every child of unit.body? Should I work with trees or syms? Which compiler phase is the most appropriate one? (Currently the plugin runs after the typer.) Is there something like an isJava method? (If not, looking for “java.” is ok too ...)
Any suggestions?
Thanks!
Simon










Re: Compiler plugin for detecting usage of Java classes
It doesn't look very nice, but this is what I came up with:
def apply(unit: CompilationUnit) {
for (tree <- unit.body) {
tree match {
case tree@Apply(fun, args)
if !(fun.tpe <:< definitions.ScalaObjectClass.tpe) =>
println(fun.pos.source + ":" + fun.pos.line + ":" + " " + fun.symbol.fullName)
case tree@DefDef(mods, name, tparams, vparamss, tpt, rhs)
if !(rhs.tpe <:< definitions.ScalaObjectClass.tpe) && (rhs.symbol != null && !mods.hasAccessorFlag) =>
println(rhs.pos.source + ":" + rhs.pos.line + ":" + " " + rhs.symbol.fullName)
case _ =>
}
}
}
Any suggestions?
Thanks and bye,
Simon
Re: Re: Compiler plugin for detecting usage of Java classes
On Wed, Jan 4, 2012 at 7:36 PM, Simon Ochsenreither <simon [dot] ochsenreither [at] googlemail [dot] com> wrote:
if (fun.symbol.isJavaDefined) ...
For the most part fun.tpe will be a method type, which will not inherit ScalaObject because, well, it's a method.
scala> typer typed (LIT("abc") DOT "length" APPLY ()) res4: $r.intp.global.analyzer.global.Tree = "abc".length()
scala> res4.asInstanceOf[Apply]res5: $r.intp.global.Apply = "abc".length()
scala> res5.fun.tperes6: $r.intp.global.Type = ()Int
scala> res5.fun.tpe <:< ScalaObjectClass.tperes7: Boolean = false
scala> res5.symbol res8: $r.intp.global.Symbol = method length
scala> res5.symbol.isJavaDefinedres9: Boolean = true
Re: Re: Compiler plugin for detecting usage of Java classes
Did you use the REPL :power mode for the stuff above or something different?
With your help it works pretty well now. I have hit one problem though:
I'm interested in the type argument in stuff like
trait Foo extends Bar[java.lang.Integer]
val list = List[java.math.MathContext]()
I catch it in Template/ValDef, but I have the problem that I seem to have to work with Types (foo.tpe.typeArgs) instead of Symbols, which makes it difficult to work with and to print line numbers, positions, ...
Is there something I'm missing?
This is the corresponding snippet I wrote currently:
def apply(unit: CompilationUnit) {
for (tree <- unit.body) {
tree match {
case tree@Apply(fun, args)
if fun.symbol.isJavaDefined && fun.symbol.isSourceMethod && fun.symbol.fullName != "java.lang.Object.<init>" =>
printJava(fun, "Apply ")
case tree@DefDef(mods, name, tparams, vparamss, tpt, rhs)
if isJavaAndNotAccessor(mods, rhs.symbol) =>
printJava(rhs, "DefDef")
case tree@ValDef(mods, name, tpt, rhs) =>
if (isJavaAndNotAccessor(mods, tpt.symbol))
printJava(tpt, "ValDef")
val typeArgs = tpt.tpe.typeArgs
//FIMXE: Some classes like the numeric wrappers lack the "java.lang." part. Why?
typeArgs.foreach(typeArg => /*if (typeArg.toLongString.startsWith("java"))*/ println("TpeArgs " + typeArg.toLongString))
case tree@Template(parents, self, body) =>
parents
.foreach {
p =>
if (isJavaAndNotJLObject(p.symbol)) printJava(p, "Templa")
p.tpe.typeArgs.foreach {
//FIMXE: Some classes like the numeric wrappers lack the "java.lang." part. Why?
typeArg => /*if (typeArg.toLongString.startsWith("java"))*/ println("TemplT " + typeArg.toLongString)
}
}
case _ =>
}
}
println(occurrences + " occurrences found.")
}
def isJavaAndNotAccessor(mods: Modifiers, sym: Symbol): Boolean = sym != null && sym.isJavaDefined && !mods.hasAccessorFlag
def isJavaAndNotJLObject(sym: Symbol): Boolean = sym.isJavaDefined && sym != definitions.ObjectClass
private def printJava(tree: Tree, source: String) {
val line = numbersWithThreeDigits(tree.pos.line)
println(tree.pos.source + ":" + line + ": [" + source + "] " + " " + tree.symbol.fullName)
occurrences += 1
}
var occurrences = 0
def numbersWithThreeDigits(num: Int): String = {
val l = num
if (l < 10) "00" + l
else if (l < 100) "0" + l
else "" + l
}
Maybe you have an idea.
Thanks!
Simon
Re: Re: Compiler plugin for detecting usage of Java classes
trait Bar[T]trait Foo extends Bar[java.lang.Integer]
class A { val list = List[java.math.MathContext]()}
scala> intp("bippy.A")res0: $r.intp.global.Symbol = class A
scala> res0.info.member(newTermName("list"))res3: $r.intp.global.Symbol = value list
scala> res3.tperes4: $r.intp.global.Type = => List[java.math.MathContext]
scala> res3.tpe.finalResultType.typeArgs.headres5: $r.intp.global.Type = java.math.MathContext
scala> res5.typeSymbolres6: $r.intp.global.Symbol = class MathContext
scala> intp("bippy.Foo")res12: $r.intp.global.Symbol = class Foo
scala> res12.info.baseTypeSeq.toList >bippy.Foobippy.Bar[Integer]ObjectAny
scala> res12.info.baseTypeSeq.toList flatMap (_.typeArgs) map (_.typeSymbol.fullName) res14: List[String] = List(java.lang.Integer)
scala> intp("scala.collection.mutable.StringBuilder").info.baseTypeSeq.toList flatMap (_.typeArgs) filter (_.typeSymbol.isJavaDefined) distinct res21: List[$r.intp.global.Type] = List(String)
scala> intp("scala.collection.mutable.StringBuilder").info.baseTypeSeq.toList flatMap (_.typeArgs) filterNot (_.typeSymbol.isJavaDefined) distinct res22: List[$r.intp.global.Type] = List(Char, StringBuilder, scala.collection.mutable.IndexedSeq[Char], scala.collection.mutable.Seq[Char], Int)
Re: Re: Compiler plugin for detecting usage of Java classes
Thanks again!
I think I almost got it... I'm only struggling with tree@Select(qual, name), because in e. g. java.awt.Point it matches not only java.awt.Point, but also java and java.awt. Is there a way to check if the selection is "complete"?
Bye,
Simon
Re: Re: Compiler plugin for detecting usage of Java classes
Bye
Re: Compiler plugin for detecting usage of Java classes
IIUC anything that doesn't implement the scala.ScalaObject interface was not compiled by scalac.
-0xe1a
Re: Compiler plugin for detecting usage of Java classes
Re: Compiler plugin for detecting usage of Java classes
Thanks!