summaryrefslogtreecommitdiff
path: root/src/scalap
diff options
context:
space:
mode:
authorGilles Dubochet <gilles.dubochet@epfl.ch>2010-03-23 14:38:11 +0000
committerGilles Dubochet <gilles.dubochet@epfl.ch>2010-03-23 14:38:11 +0000
commitef1577a9c5a9f2905e9b89b1508f97127d941bd7 (patch)
tree009b8393710f33c0c4f8d6560a36d270a3c0cab7 /src/scalap
parentfb8c14ea43d273466b7d01bb00ce4185d9a91091 (diff)
downloadscala-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')
-rw-r--r--src/scalap/scala/tools/scalap/Main.scala38
-rw-r--r--src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala2
-rw-r--r--src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala32
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)