diff options
author | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2010-03-23 14:38:11 +0000 |
---|---|---|
committer | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2010-03-23 14:38:11 +0000 |
commit | ef1577a9c5a9f2905e9b89b1508f97127d941bd7 (patch) | |
tree | 009b8393710f33c0c4f8d6560a36d270a3c0cab7 /src/scalap | |
parent | fb8c14ea43d273466b7d01bb00ce4185d9a91091 (diff) | |
download | scala-ef1577a9c5a9f2905e9b89b1508f97127d941bd7.tar.gz scala-ef1577a9c5a9f2905e9b89b1508f97127d941bd7.tar.bz2 scala-ef1577a9c5a9f2905e9b89b1508f97127d941bd7.zip |
Scala signature is generated as an annotation (...
Scala signature is generated as an annotation (that is accessible
through Java reflection).
- compiler generates all pickled Scala signatures as annotations to class files.
- compiler can read class files with signature as annotations or old-style signatures as attributes.
- Scalap has also been updated to new signatures (contributed by Ilya Sergey: thanks a lot).
- FJBG updated to allow entering constant pool strings as byte arrays.
- ByteCodecs decode method returns the length of the decoded array.
Review by ilyas. Already mostly reviewed by odersky.
Diffstat (limited to 'src/scalap')
3 files changed, 62 insertions, 10 deletions
diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala index d386e7a08f..7ac3955d45 100644 --- a/src/scalap/scala/tools/scalap/Main.scala +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -9,12 +9,14 @@ package scala.tools.scalap -import java.io.{File, PrintStream, OutputStreamWriter, ByteArrayOutputStream} +import java.io.{PrintStream, OutputStreamWriter, ByteArrayOutputStream} import scalax.rules.scalasig._ -import tools.nsc.io.AbstractFile +import scalax.rules.scalasig.ClassFileParser.{ConstValueIndex, Annotation} import tools.nsc.util.{ ClassPath } import tools.util.PathResolver import ClassPath.DefaultJavaContext +import tools.nsc.io.{PlainFile, AbstractFile} +import scala.reflect.generic.ByteCodecs /**The main object used to execute scalap on the command-line. * @@ -22,6 +24,9 @@ import ClassPath.DefaultJavaContext */ object Main { 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" @@ -97,16 +102,33 @@ object Main { baos.toString } - def getDecompiledScala(bytes: Array[Byte], isPackageObject: Boolean) = { + def decompileScala(bytes: Array[Byte], isPackageObject: Boolean): String = { val byteCode = ByteCode(bytes) val classFile = ClassFileParser.parse(byteCode) - val sig = classFile.attribute(SCALA_SIG).map(_.byteCode).map(ScalaSigAttributeParsers.parse) + 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 => "" + } + } - sig map (x => parseScalaSignature(x, isPackageObject)) + 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 sigString = (bytesElem.elementValue match {case ConstValueIndex(index) => constant(index)}).asInstanceOf[String] + val bytes = sigString.getBytes("UTF-8") + val length = ByteCodecs.decode(bytes) + val scalaSig = ScalaSigAttributeParsers.parse(ByteCode(bytes.take(length))) + parseScalaSignature(scalaSig, isPackageObject) + } } - def decompileScala(bytes: Array[Byte], isPackageObject: Boolean) = - getDecompiledScala(bytes, isPackageObject) foreach (Console println _) + + /**Executes scalap with the given arguments and classpath for the * class denoted by <code>classname</code>. @@ -128,7 +150,7 @@ object Main { } val bytes = cfile.toByteArray if (isScalaFile(bytes)) { - decompileScala(bytes, isPackageObjectFile(encName)) + Console.println(decompileScala(bytes, isPackageObjectFile(encName))) } else { // construct a reader for the classfile content val reader = new ByteArrayReader(cfile.toByteArray) diff --git a/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala b/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala index 54f2c70bdc..6c75c748bb 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala @@ -24,7 +24,7 @@ class InRule[In, +Out, +A, +X](rule : Rule[In, Out, A, X]) { in : In => f(rule(in))(in) } - /** Creates a rule that suceeds only if the original rule would fail on the given context. */ + /** Creates a rule that succeeds only if the original rule would fail on the given context. */ def unary_! : Rule[In, In, Unit, Nothing] = mapRule { case Success(_, _) => in : In => Failure case _ => in : In => Success(in, ()) diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala index 37bfa9cfea..8689265be2 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala @@ -84,6 +84,7 @@ trait ByteCodeReader extends RulesWithState { object ClassFileParser extends ByteCodeReader { def parse(byteCode : ByteCode) = expect(classFile)(byteCode) + def parseAnnotations(byteCode: ByteCode) = expect(annotations)(byteCode) val magicNumber = (u4 filter (_ == 0xCAFEBABE)) | error("Not a valid class file") val version = u2 ~ u2 ^^ { case minor ~ major => (major, minor) } @@ -119,9 +120,32 @@ object ClassFileParser extends ByteCodeReader { val interfaces = u2 >> u2.times + // bytes are parametrizes by the length, declared in u4 section val attribute = u2 ~ (u4 >> bytes) ^~^ Attribute + // parse attributes u2 times val attributes = u2 >> attribute.times + // parse runtime-visible annotations + abstract class ElementValue + case class AnnotationElement(elementNameIndex: Int, elementValue: ElementValue) + case class ConstValueIndex(index: Int) extends ElementValue + case class EnumConstValue(typeNameIndex: Int, constNameIndex: Int) extends ElementValue + case class ClassInfoIndex(index: Int) extends ElementValue + case class Annotation(typeIndex: Int, elementValuePairs: Seq[AnnotationElement]) extends ElementValue + case class ArrayValue(values: Seq[ElementValue]) extends ElementValue + + def element_value: Parser[ElementValue] = u1 >> { + case 'B'|'C'|'D'|'F'|'I'|'J'|'S'|'Z'|'s' => u2 ^^ ConstValueIndex + case 'e' => u2 ~ u2 ^~^ EnumConstValue + case 'c' => u2 ^^ ClassInfoIndex + case '@' => annotation //nested annotation + case '[' => u2 >> element_value.times ^^ ArrayValue + } + + val element_value_pair = u2 ~ element_value ^~^ AnnotationElement + val annotation: Parser[Annotation] = u2 ~ (u2 >> element_value_pair.times) ^~^ Annotation + val annotations = u2 >> annotation.times + val field = u2 ~ u2 ~ u2 ~ attributes ^~~~^ Field val fields = u2 >> field.times @@ -155,7 +179,13 @@ case class ClassFile( def constant(index : Int) = header.constants(index) - def attribute(name : String) = attributes.find { attrib => constant(attrib.nameIndex) == name } + def attribute(name : String) = attributes.find {attrib => constant(attrib.nameIndex) == name } + + val RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations" + def annotations = (attributes.find(attr => constant(attr.nameIndex) == RUNTIME_VISIBLE_ANNOTATIONS) + .map(attr => ClassFileParser.parseAnnotations(attr.byteCode))) + + def annotation(name: String) = annotations.flatMap(seq => seq.find(annot => constant(annot.typeIndex) == name)) } case class Attribute(nameIndex : Int, byteCode : ByteCode) |