Skip to content

Instantly share code, notes, and snippets.

@wfaler
Created April 10, 2011 18:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wfaler/912586 to your computer and use it in GitHub Desktop.
Save wfaler/912586 to your computer and use it in GitHub Desktop.
Scalap signature reading extracted into what will be a util class for Scala 2.9 reflection compatibility.
package com.recursivity.commons.bean.scalap
import scala.tools.scalap.scalax.rules.scalasig._
import tools.scalap._
import scalax.rules.scalasig.ClassFileParser.{ConstValueIndex, Annotation}
import reflect.generic.ByteCodecs
import java.io.{StringWriter, ByteArrayOutputStream, PrintStream}
object ScalaSigParser {
val SCALA_SIG = "ScalaSig"
val SCALA_SIG_ANNOTATION = "Lscala/reflect/ScalaSignature;"
val BYTES_VALUE = "bytes"
val versionMsg = "Scala classfile decoder " +
Properties.versionString + " -- " +
Properties.copyrightString + "\n"
var printPrivates = false
def processJavaClassFile(clazz: Classfile): String = {
println("processing class file")
val out = new StringWriter
//val out = new OutputStreamWriter(Console.out)
val writer = new JavaWriter(clazz, out)
// print the class
writer.printClass
out.flush()
return writer.toString
}
def process(classname: String): String = {
val encName = Names.encode(
if (classname == "scala.AnyRef") "java.lang.Object"
else classname)
val name = "/" + encName.replace(".", "/") + ".class"
val resource = this.getClass.getResourceAsStream(name)
val bytes = new Array[Byte](resource.available)
resource.read(bytes)
//val bytes = null//cfile.toByteArray
if (isScalaFile(bytes)) {
return decompileScala(bytes, isPackageObjectFile(classname))
} else {
// construct a reader for the classfile content
val reader = new ByteArrayReader(bytes) //cfile.toByteArray)
// parse the classfile
val clazz = new Classfile(reader)
return processJavaClassFile(clazz)
}
}
def isPackageObjectFile(s: String) = s != null && (s.endsWith(".package") || s == "package")
def decompileScala(bytes: Array[Byte], isPackageObject: Boolean): String = {
val byteCode = ByteCode(bytes)
val classFile = ClassFileParser.parse(byteCode)
classFile.attribute(SCALA_SIG).map(_.byteCode).map(ScalaSigAttributeParsers.parse) match {
// No entries in ScalaSig attribute implies that the signature is stored in the annotation
case Some(ScalaSig(_, _, entries)) if entries.length == 0 => unpickleFromAnnotation(classFile, isPackageObject)
case Some(scalaSig) => parseScalaSignature(scalaSig, isPackageObject)
case None => ""
}
}
def isScalaFile(bytes: Array[Byte]): Boolean = {
val byteCode = ByteCode(bytes)
val classFile = ClassFileParser.parse(byteCode)
classFile.attribute("ScalaSig").isDefined
}
def unpickleFromAnnotation(classFile: ClassFile, isPackageObject: Boolean): String = {
import classFile._
classFile.annotation(SCALA_SIG_ANNOTATION) match {
case None => ""
case Some(Annotation(_, elements)) =>
val bytesElem = elements.find(elem => constant(elem.elementNameIndex) == BYTES_VALUE).get
val bytes = ((bytesElem.elementValue match {
case ConstValueIndex(index) => constantWrapped(index)
})
.asInstanceOf[StringBytesPair].bytes)
val length = ByteCodecs.decode(bytes)
val scalaSig = ScalaSigAttributeParsers.parse(ByteCode(bytes.take(length)))
parseScalaSignature(scalaSig, isPackageObject)
}
}
def parseScalaSignature(scalaSig: ScalaSig, isPackageObject: Boolean) = {
val baos = new ByteArrayOutputStream
val stream = new PrintStream(baos)
val syms = scalaSig.topLevelClasses ::: scalaSig.topLevelObjects
syms.head.parent match {
//Partial match
case Some(p) if (p.name != "<empty>") => {
val path = p.path
if (!isPackageObject) {
stream.print("package ");
stream.print(path);
stream.print("\n")
} else {
val i = path.lastIndexOf(".")
if (i > 0) {
stream.print("package ");
stream.print(path.substring(0, i))
stream.print("\n")
}
}
}
case _ =>
}
// Print classes
val printer = new ScalaSigPrinter(stream, printPrivates)
for (c <- syms) {
printer.printSymbol(c)
}
baos.toString
}
def main(args: Array[String]) = {
println("processed: " + process("com.recursivity.commons.bean.scalap.TransformBean"))
}
}
class TransformBean{
var hello: String = "hello"
var int: Int = 0
var long: Long = 0
var javaLong: java.lang.Long = 0
var date: java.util.Date = new java.util.Date
var bool: Boolean = true
var javaBool: java.lang.Boolean = true
var javaInt: java.lang.Integer = 56
var javaBigDecimal: java.math.BigDecimal = new java.math.BigDecimal("45.34")
var bigDecimal: BigDecimal = new BigDecimal(javaBigDecimal)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment