diff options
author | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2010-04-29 17:01:22 +0000 |
---|---|---|
committer | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2010-04-29 17:01:22 +0000 |
commit | 5b937bacd38c34718e16989f3752e1f582ae2698 (patch) | |
tree | 6b14bd7647a353d9f39f3973a6e0e3444b738066 /src | |
parent | 8eb1d0c6ac940438b5f81d832dd3d0ab26de1ff5 (diff) | |
download | scala-5b937bacd38c34718e16989f3752e1f582ae2698.tar.gz scala-5b937bacd38c34718e16989f3752e1f582ae2698.tar.bz2 scala-5b937bacd38c34718e16989f3752e1f582ae2698.zip |
Closes #3310 (very large Scala class is compile...
Closes #3310 (very large Scala class is compiled to invalid classfile
because Scala signature can't fit into constant pool). Review by dragos.
Diffstat (limited to 'src')
6 files changed, 88 insertions, 8 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index dc193b03db..2b4878a93b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -163,10 +163,10 @@ abstract class GenJVM extends SubComponent { 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))) - )) + val scalaAnnot = { + val sigBytes = ScalaSigBytes(pickle.bytes.take(pickle.writeIndex)) + AnnotationInfo(sigBytes.sigAnnot, Nil, List((nme.bytes, sigBytes))) + } pickledBytes = pickledBytes + pickle.writeIndex currentRun.symData -= sym currentRun.symData -= sym.companionSymbol @@ -416,9 +416,20 @@ abstract class GenJVM extends SubComponent { buf.putShort(cpool.addUtf8(const.symbolValue.name.toString).toShort) } - case ScalaSigBytes(bytes) => + case sb@ScalaSigBytes(bytes) if (!sb.isLong) => buf.put('s'.toByte) - buf.putShort(cpool.addUtf8(reflect.generic.ByteCodecs.encode(bytes)).toShort) + buf.putShort(cpool.addUtf8(sb.encodedBytes).toShort) + + case sb@ScalaSigBytes(bytes) if (sb.isLong) => + buf.put('['.toByte) + val stringCount = (sb.encodedBytes.length / 65534) + 1 + buf.putShort(stringCount.toShort) + for (i <- 0 until stringCount) { + buf.put('s'.toByte) + val j = i * 65535 + val string = sb.encodedBytes.slice(j, j + 65535) + buf.putShort(cpool.addUtf8(string).toShort) + } case ArrayAnnotArg(args) => buf.put('['.toByte) diff --git a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala index efb68c4873..edc87108b4 100644 --- a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala +++ b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala @@ -45,9 +45,16 @@ trait AnnotationInfos extends reflect.generic.AnnotationInfos { self: SymbolTabl /** 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 { + case class ScalaSigBytes(bytes: Array[Byte]) extends ClassfileAnnotArg { override def toString = (bytes map { byte => (byte & 0xff).toHexString }).mkString("[ ", " ", " ]") + lazy val encodedBytes = + reflect.generic.ByteCodecs.encode(bytes) + def isLong: Boolean = (encodedBytes.length > 65535) + def sigAnnot: Type = + if (this.isLong) + definitions.ScalaLongSignatureAnnotation.tpe + else + definitions.ScalaSignatureAnnotation.tpe } object ScalaSigBytes extends ScalaSigBytesExtractor diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index db48189b6b..ed26ebac03 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -243,6 +243,7 @@ trait Definitions extends reflect.generic.StandardDefinitions { def Code_lift = getMember(CodeModule, nme.lift_) lazy val ScalaSignatureAnnotation = getClass("scala.reflect.ScalaSignature") + lazy val ScalaLongSignatureAnnotation = getClass("scala.reflect.ScalaLongSignature") // invoke dynamic support lazy val LinkageModule = getModule("java.dyn.Linkage") diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index fc635874a6..7cd32ff81e 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -377,6 +377,26 @@ abstract class ClassfileParser { value } + def getBytes(indices: List[Int]): Array[Byte] = { + assert(!indices.isEmpty) + var value = values(indices.head).asInstanceOf[Array[Byte]] + if (value eq null) { + val bytesBuffer = ArrayBuffer.empty[Byte] + for (index <- indices) { + if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index) + val start = starts(index) + if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) + val len = in.getChar(start + 1) + bytesBuffer ++= (in.buf, start + 3, len) + } + val bytes = bytesBuffer.toArray + val decodedLength = reflect.generic.ByteCodecs.decode(bytes) + value = bytes.take(decodedLength) + values(indices.head) = 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) @@ -923,6 +943,24 @@ abstract class ClassfileParser { Some(ScalaSigBytes(pool.getBytes(in.nextChar))) } + def parseScalaLongSigBytes: Option[ScalaSigBytes] = try { + val tag = in.nextByte.toChar + assert(tag == ARRAY_TAG) + val stringCount = in.nextChar + val entries = + for (i <- 0 until stringCount) yield { + val stag = in.nextByte.toChar + assert(stag == STRING_TAG) + in.nextChar.toInt + } + Some(ScalaSigBytes(pool.getBytes(entries.toList))) + } + catch { + case e: Throwable => + e.printStackTrace + throw e + } + /** Parse and return a single annotation. If it is malformed, * return None. */ @@ -941,6 +979,11 @@ abstract class ClassfileParser { case Some(c) => nvpairs += ((name, c)) case None => hasError = true } + else if ((attrType == definitions.ScalaLongSignatureAnnotation.tpe) && (name == nme.bytes)) + parseScalaLongSigBytes match { + case Some(c) => nvpairs += ((name, c)) + case None => hasError = true + } else parseAnnotArg match { case Some(c) => nvpairs += ((name, c)) @@ -981,6 +1024,8 @@ abstract class ClassfileParser { parseAnnotation(in.nextChar) match { case Some(scalaSig) if (scalaSig.atp == definitions.ScalaSignatureAnnotation.tpe) => scalaSigAnnot = Some(scalaSig) + case Some(scalaSig) if (scalaSig.atp == definitions.ScalaLongSignatureAnnotation.tpe) => + scalaSigAnnot = Some(scalaSig) case Some(annot) => sym.addAnnotation(annot) case None => diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java b/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java index 4fa048177c..0e6b43f1e2 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JConstantPool.java @@ -383,6 +383,9 @@ public class JConstantPool { public int getSize() { return 1; } public void writeContentsTo(DataOutputStream stream) throws IOException { if (bytes != null) { + if (bytes.length > 65535) { + throw new IOException("String literal of length " + bytes.length + " does not fit in Classfile"); + } stream.writeShort(bytes.length); stream.write(bytes); } diff --git a/src/library/scala/reflect/ScalaLongSignature.java b/src/library/scala/reflect/ScalaLongSignature.java new file mode 100644 index 0000000000..1ffd6d2520 --- /dev/null +++ b/src/library/scala/reflect/ScalaLongSignature.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 ScalaLongSignature { + public String[] bytes(); +} |