Created
April 10, 2011 18:24
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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