summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/fjbg.jar.desired.sha12
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala78
-rw-r--r--src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala9
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala66
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java36
-rw-r--r--src/library/scala/reflect/ScalaSignature.java13
-rwxr-xr-xsrc/library/scala/reflect/generic/AnnotationInfos.scala8
-rw-r--r--src/library/scala/reflect/generic/ByteCodecs.scala7
-rw-r--r--src/partest/scala/tools/partest/nest/ConsoleRunner.scala2
-rw-r--r--src/partest/scala/tools/partest/nest/Worker.scala11
-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
15 files changed, 250 insertions, 57 deletions
diff --git a/lib/fjbg.jar.desired.sha1 b/lib/fjbg.jar.desired.sha1
index 059817251c..04db587a17 100644
--- a/lib/fjbg.jar.desired.sha1
+++ b/lib/fjbg.jar.desired.sha1
@@ -1 +1 @@
-6ef6a21997d01d64a3ff8447a0e110d04b3d6c7d ?fjbg.jar
+bfbfc87845d5bb3402bac61f03734f362f6554b6 ?fjbg.jar
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index eccce2922e..147b25a70c 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -8,7 +8,6 @@
package scala.tools.nsc
package backend.jvm
-import java.io.{ DataOutputStream, File, OutputStream }
import java.nio.ByteBuffer
import scala.collection.immutable.{Set, ListSet}
@@ -18,6 +17,8 @@ import scala.tools.nsc.symtab._
import scala.tools.nsc.symtab.classfile.ClassfileConstants._
import ch.epfl.lamp.fjbg._
+import java.io.{ByteArrayOutputStream, DataOutputStream, File, OutputStream}
+import reflect.generic.{PickleFormat, PickleBuffer}
/** This class ...
*
@@ -104,6 +105,16 @@ abstract class GenJVM extends SubComponent {
lazy val RemoteInterface = definitions.getClass("java.rmi.Remote")
lazy val RemoteException = definitions.getClass("java.rmi.RemoteException").tpe
+
+ val versionPickle = {
+ val vp = new PickleBuffer(new Array[Byte](16), -1, 0)
+ assert(vp.writeIndex == 0)
+ vp.writeNat(PickleFormat.MajorVersion)
+ vp.writeNat(PickleFormat.MinorVersion)
+ vp.writeNat(0)
+ vp
+ }
+
var clasz: IClass = _
var method: IMethod = _
var jclass: JClass = _
@@ -125,25 +136,6 @@ abstract class GenJVM extends SubComponent {
* @param sym The corresponding symbol, used for looking up pickled information
*/
def emitClass(jclass: JClass, sym: Symbol) {
- def addScalaAttr(sym: Symbol): Unit = currentRun.symData.get(sym) match {
- case Some(pickle) =>
- val scalaAttr = fjbgContext.JOtherAttribute(jclass,
- jclass,
- nme.ScalaSignatureATTR.toString,
- pickle.bytes,
- pickle.writeIndex)
- pickledBytes = pickledBytes + pickle.writeIndex
- jclass.addAttribute(scalaAttr)
- currentRun.symData -= sym
- currentRun.symData -= sym.companionSymbol
- //System.out.println("Generated ScalaSig Attr for " + sym)//debug
- case _ =>
- val markerAttr = getMarkerAttr(jclass)
- jclass.addAttribute(markerAttr)
- log("Could not find pickle information for " + sym)
- }
- if (!(jclass.getName().endsWith("$") && sym.isModuleClass))
- addScalaAttr(if (isTopLevelModule(sym)) sym.sourceModule else sym);
addInnerClasses(jclass)
val outfile = getFile(sym, jclass, ".class")
@@ -153,6 +145,43 @@ abstract class GenJVM extends SubComponent {
informProgress("wrote " + outfile)
}
+ /** Returns the ScalaSignature annotation if it must be added to this class, none otherwise; furthermore, it adds to
+ * jclass the Scala marker attribute (marking that a scala signature annotation is present). The annotation that is
+ * returned by this method must be added to the class' annotations list when generating them.
+ * @param jclass The class file that is being readied.
+ * @param sym The symbol for which the signature has been entered in the symData map. This is different than the
+ * symbol that is being generated in the case of a mirror class.
+ * @return An option that is:
+ * - defined and contains an annotation info of the ScalaSignature type, instantiated with the
+ * pickle signature for sym;
+ * - undefined if the jclass/sym couple must not contain a signature or if there was an error (see
+ * log output in debug mode). */
+ def scalaSignatureAddingMarker(jclass: JClass, sym: Symbol): Option[AnnotationInfo] =
+ if (jclass.getName().endsWith("$") && sym.isModuleClass) {
+ None
+ }
+ else {
+ val sd = currentRun.symData
+ currentRun.symData.get(sym) match {
+ case Some(pickle) =>
+ val scalaAttr =
+ fjbgContext.JOtherAttribute(jclass, jclass, nme.ScalaSignatureATTR.toString,
+ versionPickle.bytes, versionPickle.writeIndex)
+ jclass.addAttribute(scalaAttr)
+ val scalaAnnot =
+ AnnotationInfo(definitions.ScalaSignatureAnnotation.tpe, Nil, List(
+ (nme.bytes, ScalaSigBytes(pickle.bytes.take(pickle.writeIndex)))
+ ))
+ pickledBytes = pickledBytes + pickle.writeIndex
+ currentRun.symData -= sym
+ currentRun.symData -= sym.companionSymbol
+ Some(scalaAnnot)
+ case _ =>
+ log("Could not find annotation pickle information for " + sym)
+ None
+ }
+ }
+
private def getMarkerAttr(jclass: JClass): JOtherAttribute =
fjbgContext.JOtherAttribute(jclass, jclass, nme.ScalaATTR.toString, new Array[Byte](0), 0)
@@ -237,8 +266,9 @@ abstract class GenJVM extends SubComponent {
clasz.fields foreach genField
clasz.methods foreach genMethod
+ val ssa = scalaSignatureAddingMarker(jclass, c.symbol)
addGenericSignature(jclass, c.symbol, c.symbol.owner)
- addAnnotations(jclass, c.symbol.annotations)
+ addAnnotations(jclass, c.symbol.annotations ++ ssa)
emitClass(jclass, c.symbol)
if (c.symbol hasAnnotation BeanInfoAttr)
@@ -395,6 +425,10 @@ abstract class GenJVM extends SubComponent {
buf.putShort(cpool.addUtf8(const.symbolValue.name.toString).toShort)
}
+ case ScalaSigBytes(bytes) =>
+ buf.put('s'.toByte)
+ buf.putShort(cpool.addUtf8(reflect.generic.ByteCodecs.encode(bytes)).toShort)
+
case ArrayAnnotArg(args) =>
buf.put('['.toByte)
buf.putShort(args.length.toShort)
@@ -883,6 +917,8 @@ abstract class GenJVM extends SubComponent {
JClass.NO_INTERFACES,
sourceFile)
addForwarders(mirrorClass, clasz)
+ val ssa = scalaSignatureAddingMarker(mirrorClass, clasz.companionSymbol)
+ addAnnotations(mirrorClass, clasz.annotations ++ ssa)
emitClass(mirrorClass, clasz)
}
diff --git a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala
index f0265c5e5d..f32c232316 100644
--- a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala
+++ b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala
@@ -42,6 +42,15 @@ trait AnnotationInfos extends reflect.generic.AnnotationInfos { self: SymbolTabl
object ArrayAnnotArg extends ArrayAnnotArgExtractor
+ /** A specific annotation argument that encodes an array of bytes as an array of `Long`. The type of the argument
+ * declared in the annotation must be `String`. This specialised class is used to encode scala signatures for
+ * reasons of efficiency, both in term of class-file size and in term of compiler performance. */
+ case class ScalaSigBytes(bytes: Array[Byte])
+ extends ClassfileAnnotArg {
+ override def toString = (bytes map { byte => (byte & 0xff).toHexString }).mkString("[ ", " ", " ]")
+ }
+ object ScalaSigBytes extends ScalaSigBytesExtractor
+
/** Represents a nested classfile annotation */
case class NestedAnnotArg(annInfo: AnnotationInfo)
extends ClassfileAnnotArg {
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index d3b1d4dabe..2d4d01623d 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -238,6 +238,8 @@ trait Definitions extends reflect.generic.StandardDefinitions {
lazy val CodeModule = getModule(sn.Code)
def Code_lift = getMember(CodeModule, nme.lift_)
+ lazy val ScalaSignatureAnnotation = getClass("scala.reflect.ScalaSignature")
+
// invoke dynamic support
lazy val LinkageModule = getModule("java.dyn.Linkage")
lazy val Linkage_invalidateCallerClass = getMember(LinkageModule, "invalidateCallerClass")
diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
index 5ce0025de5..8fbdf680e6 100644
--- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala
+++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
@@ -263,6 +263,7 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable =>
val assume_ = newTermName("assume")
val asInstanceOf_ = newTermName("asInstanceOf")
val box = newTermName("box")
+ val bytes = newTermName("bytes")
val canEqual_ = newTermName("canEqual")
val checkInitialized = newTermName("checkInitialized")
val classOf = newTermName("classOf")
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index e8b2b84653..aec2d9dda8 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -15,6 +15,7 @@ import scala.collection.immutable.{Map, ListMap}
import scala.collection.mutable.{ListBuffer, ArrayBuffer}
import scala.tools.nsc.io.AbstractFile
import scala.annotation.switch
+import reflect.generic.PickleBuffer
/** This abstract class implements a class file parser.
*
@@ -35,6 +36,7 @@ abstract class ClassfileParser {
protected var staticDefs: Scope = _ // the scope of all static definitions
protected var pool: ConstantPool = _ // the classfile's constant pool
protected var isScala: Boolean = _ // does class file describe a scala class?
+ protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation?
protected var isScalaRaw: Boolean = _ // this class file is a scala class with no pickled info
protected var hasMeta: Boolean = _ // does class file contain jaco meta attribute?s
protected var busy: Option[Symbol] = None // lock to detect recursive reads
@@ -353,6 +355,22 @@ abstract class ClassfileParser {
}
}
+ def getBytes(index: Int): Array[Byte] = {
+ if (index <= 0 || len <= index) errorBadIndex(index)
+ var value = values(index).asInstanceOf[Array[Byte]]
+ if (value eq null) {
+ val start = starts(index)
+ if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start)
+ val len = in.getChar(start + 1)
+ val bytes = new Array[Byte](len)
+ Array.copy(in.buf, start + 3, bytes, 0, len)
+ val decodedLength = reflect.generic.ByteCodecs.decode(bytes)
+ value = bytes.take(decodedLength)
+ values(index) = value
+ }
+ value
+ }
+
/** Throws an exception signaling a bad constant index. */
private def errorBadIndex(index: Int) =
throw new RuntimeException("bad constant pool index: " + index + " at pos: " + in.bp)
@@ -786,9 +804,12 @@ abstract class ClassfileParser {
if (c1 ne null) sym.setInfo(ConstantType(c1))
else println("failure to convert " + c + " to " + symtype); //debug
case nme.ScalaSignatureATTR =>
- unpickler.unpickle(in.buf, in.bp, clazz, staticModule, in.file.toString())
+ if (!isScalaAnnot) {
+ if (settings.debug.value)
+ global.inform("warning: symbol " + sym.fullName + " has pickled signature in attribute")
+ unpickler.unpickle(in.buf, in.bp, clazz, staticModule, in.file.toString())
+ }
in.skip(attrLen)
- this.isScala = true
case nme.ScalaATTR =>
isScalaRaw = true
case nme.JacoMetaATTR =>
@@ -801,9 +822,19 @@ abstract class ClassfileParser {
in.skip(attrLen)
// Java annotatinos on classes / methods / fields with RetentionPolicy.RUNTIME
case nme.RuntimeAnnotationATTR =>
- if (!isScala) {
- // no need to read annotations if isScala, ClassfileAnnotations are pickled
+ if (isScalaAnnot || !isScala) {
parseAnnotations(attrLen)
+ if (isScalaAnnot)
+ (sym.rawAnnotations find { annot =>
+ annot.asInstanceOf[AnnotationInfo].atp == definitions.ScalaSignatureAnnotation.tpe
+ }) match {
+ case Some(san: AnnotationInfo) =>
+ val bytes =
+ san.assocs.find({ _._1 == nme.bytes }).get._2.asInstanceOf[ScalaSigBytes].bytes
+ unpickler.unpickle(bytes, 0, clazz, staticModule, in.file.toString())
+ case None =>
+ throw new RuntimeException("Scala class file does not contain Scala annotation")
+ }
if (settings.debug.value)
global.inform("" + sym + "; annotations = " + sym.annotations)
} else
@@ -862,6 +893,12 @@ abstract class ClassfileParser {
}
}
+ def parseScalaSigBytes: Option[ScalaSigBytes] = {
+ val tag = in.nextByte.toChar
+ assert(tag == STRING_TAG)
+ Some(ScalaSigBytes(pool.getBytes(in.nextChar)))
+ }
+
/** Parse and return a single annotation. If it is malformed,
* return None.
*/
@@ -872,10 +909,19 @@ abstract class ClassfileParser {
var hasError = false
for (i <- 0 until nargs) {
val name = pool.getName(in.nextChar)
- parseAnnotArg match {
- case Some(c) => nvpairs += ((name, c))
- case None => hasError = true
- }
+ // The "bytes: String" argument of the ScalaSignature attribute is parsed specially so that it is
+ // available as an array of bytes (the pickled Scala signature) instead of as a string. The pickled signature
+ // is encoded as a string because of limitations in the Java class file format.
+ if ((attrType == definitions.ScalaSignatureAnnotation.tpe) && (name == nme.bytes))
+ parseScalaSigBytes match {
+ case Some(c) => nvpairs += ((name, c))
+ case None => hasError = true
+ }
+ else
+ parseAnnotArg match {
+ case Some(c) => nvpairs += ((name, c))
+ case None => hasError = true
+ }
}
if (hasError) None
else Some(AnnotationInfo(attrType, List(), nvpairs.toList))
@@ -985,6 +1031,10 @@ abstract class ClassfileParser {
in.skip(attrLen)
case nme.ScalaSignatureATTR =>
isScala = true
+ val pbuf = new PickleBuffer(in.buf, in.bp, in.bp + attrLen)
+ pbuf.readNat; pbuf.readNat;
+ if (pbuf.readNat == 0) // a scala signature attribute with no entries means that the actual scala signature
+ isScalaAnnot = true // is in a ScalaSignature annotation.
in.skip(attrLen)
case nme.ScalaATTR =>
isScalaRaw = true
diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java b/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java
index 911acd18da..4fa048177c 100644
--- a/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java
+++ b/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java
@@ -187,6 +187,10 @@ public class JConstantPool {
return addEntry(new Utf8Entry(value));
}
+ public int addUtf8(byte[] value) {
+ return addEntry(new Utf8Entry(value));
+ }
+
public String lookupUtf8(int index) {
Utf8Entry entry = (Utf8Entry)lookupEntry(index);
return entry.getValue();
@@ -344,22 +348,46 @@ public class JConstantPool {
public class Utf8Entry extends ChildlessEntry implements Entry {
private final String value;
- public Utf8Entry(String value) { this.value = value.intern(); }
+ private final byte[] bytes;
+ public Utf8Entry(String value) {
+ this.value = value.intern();
+ this.bytes = null;
+ }
public Utf8Entry(DataInputStream stream) throws IOException {
this(stream.readUTF());
}
+ public Utf8Entry(byte[] bytes) {
+ this.bytes = bytes;
+ this.value = null;
+ }
- public int hashCode() { return value.hashCode(); }
+ public int hashCode() {
+ if (bytes != null) return bytes.hashCode();
+ return value.hashCode();
+ }
public boolean equals(Object o) {
- return o instanceof Utf8Entry && ((Utf8Entry)o).value == value;
+ boolean isEqual = o instanceof Utf8Entry;
+ if (bytes != null) {
+ isEqual = isEqual && ((Utf8Entry)o).bytes == bytes;
+ }
+ else {
+ isEqual = isEqual && ((Utf8Entry)o).value == value;
+ }
+ return isEqual;
}
public int getTag() { return CONSTANT_Utf8; }
public String getValue() { return value; }
+ public byte[] getBytes() { return bytes; }
public int getSize() { return 1; }
public void writeContentsTo(DataOutputStream stream) throws IOException {
- stream.writeUTF(value);
+ if (bytes != null) {
+ stream.writeShort(bytes.length);
+ stream.write(bytes);
+ }
+ else
+ stream.writeUTF(value);
}
}
diff --git a/src/library/scala/reflect/ScalaSignature.java b/src/library/scala/reflect/ScalaSignature.java
new file mode 100644
index 0000000000..d1cdbc0589
--- /dev/null
+++ b/src/library/scala/reflect/ScalaSignature.java
@@ -0,0 +1,13 @@
+package scala.reflect;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface ScalaSignature {
+ public String bytes();
+}
diff --git a/src/library/scala/reflect/generic/AnnotationInfos.scala b/src/library/scala/reflect/generic/AnnotationInfos.scala
index 6239ca189c..cc6c909a45 100755
--- a/src/library/scala/reflect/generic/AnnotationInfos.scala
+++ b/src/library/scala/reflect/generic/AnnotationInfos.scala
@@ -20,6 +20,9 @@ trait AnnotationInfos { self: Universe =>
type ArrayAnnotArg <: ClassfileAnnotArg
val ArrayAnnotArg: ArrayAnnotArgExtractor
+ type ScalaSigBytes <: ClassfileAnnotArg
+ val ScalaSigBytes: ScalaSigBytesExtractor
+
type NestedAnnotArg <: ClassfileAnnotArg
val NestedAnnotArg: NestedAnnotArgExtractor
@@ -33,6 +36,11 @@ trait AnnotationInfos { self: Universe =>
def unapply(arg: ArrayAnnotArg): Option[Array[ClassfileAnnotArg]]
}
+ abstract class ScalaSigBytesExtractor {
+ def apply(bytes: Array[Byte]): ScalaSigBytes
+ def unapply(arg: ScalaSigBytes): Option[Array[Byte]]
+ }
+
abstract class NestedAnnotArgExtractor {
def apply(anninfo: AnnotationInfo): NestedAnnotArg
def unapply(arg: NestedAnnotArg): Option[AnnotationInfo]
diff --git a/src/library/scala/reflect/generic/ByteCodecs.scala b/src/library/scala/reflect/generic/ByteCodecs.scala
index 1f42008837..fd2e326e19 100644
--- a/src/library/scala/reflect/generic/ByteCodecs.scala
+++ b/src/library/scala/reflect/generic/ByteCodecs.scala
@@ -122,7 +122,7 @@ object ByteCodecs {
@deprecated("use 2-argument version instead")
def decode7to8(src: Array[Byte], srclen: Int, dstlen: Int) { decode7to8(src, srclen) }
- def decode7to8(src: Array[Byte], srclen: Int) {
+ def decode7to8(src: Array[Byte], srclen: Int): Int = {
var i = 0
var j = 0
val dstlen = (srclen * 7 + 7) / 8
@@ -185,6 +185,7 @@ object ByteCodecs {
}
if (j < dstlen) src(j) = out.toByte
}
+ dstlen
}
def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs))
@@ -192,8 +193,8 @@ object ByteCodecs {
@deprecated("use 1-argument version instead")
def decode(xs: Array[Byte], dstlen: Int) { decode(xs) }
- /** Destructively decode array xs */
- def decode(xs: Array[Byte]) {
+ /** Destructively decode array xs and returns the length of the decoded array */
+ def decode(xs: Array[Byte]): Int = {
val len = regenerateZero(xs)
decode7to8(xs, len)
}
diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala
index f7eca614ac..eae79f23af 100644
--- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala
+++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala
@@ -36,7 +36,7 @@ class ConsoleRunner extends DirectRunner {
TestSet("shootout", pathFilter, "Testing shootout tests"),
TestSet("script", pathFilter, "Testing script tests"),
TestSet("scalacheck", pathFilter, "Testing ScalaCheck tests"),
- TestSet("scalap", pathFilter, "Run scalap decompiler tests")
+ TestSet("scalap", _.isDirectory, "Run scalap decompiler tests")
)
}
diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala
index 213123f711..cf4a8238e9 100644
--- a/src/partest/scala/tools/partest/nest/Worker.scala
+++ b/src/partest/scala/tools/partest/nest/Worker.scala
@@ -820,14 +820,6 @@ class Worker(val fileManager: FileManager) extends Actor {
case "scalap" => {
- def decompileFile(clazz: Class[_], packObj: Boolean) = {
- val byteCode = ByteCode.forClass(clazz)
- val classFile = ClassFileParser.parse(byteCode)
- val Some(sig) = classFile.attribute("ScalaSig").map(_.byteCode).map(ScalaSigAttributeParsers.parse)
- import scala.tools.scalap.Main._
- parseScalaSignature(sig, packObj)
- }
-
runInContext(file, kind, (logFile: File, outDir: File) => {
val sourceDir = file.getParentFile
val sourceDirName = sourceDir.getName
@@ -854,7 +846,8 @@ class Worker(val fileManager: FileManager) extends Actor {
val loader = new URLClassLoader(Array(url), getClass.getClassLoader)
val clazz = loader.loadClass(className)
- val result = decompileFile(clazz, isPackageObject)
+ val byteCode = ByteCode.forClass(clazz)
+ val result = scala.tools.scalap.Main.decompileScala(byteCode.bytes, isPackageObject)
try {
val fstream = new FileWriter(logFile);
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)