diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 12 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/BytecodeTest.scala | 35 | ||||
-rw-r--r-- | test/files/run/t2464/Annotated.java | 5 | ||||
-rw-r--r-- | test/files/run/t2464/Connect.java | 20 | ||||
-rw-r--r-- | test/files/run/t2464/Test.scala | 35 |
5 files changed, 99 insertions, 8 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index fb927d15d3..1e84c73633 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -588,10 +588,14 @@ abstract class ClassfileParser { // sealed java enums if (jflags.isEnum) { val enumClass = sym.owner.linkedClassOfClass - if (!enumClass.isSealed) - enumClass setFlag (SEALED | ABSTRACT) - - enumClass addChild sym + enumClass match { + case NoSymbol => + devWarning(s"no linked class for java enum $sym in ${sym.owner}. A referencing class file might be missing an InnerClasses entry.") + case linked => + if (!linked.isSealed) + linked setFlag (SEALED | ABSTRACT) + linked addChild sym + } } } } diff --git a/src/partest/scala/tools/partest/BytecodeTest.scala b/src/partest/scala/tools/partest/BytecodeTest.scala index 2699083069..172fa29189 100644 --- a/src/partest/scala/tools/partest/BytecodeTest.scala +++ b/src/partest/scala/tools/partest/BytecodeTest.scala @@ -2,10 +2,9 @@ package scala.tools.partest import scala.tools.nsc.util.JavaClassPath import scala.collection.JavaConverters._ -import scala.tools.asm -import asm.{ ClassReader } -import asm.tree.{ClassNode, MethodNode, InsnList} -import java.io.InputStream +import scala.tools.asm.{ClassWriter, ClassReader} +import scala.tools.asm.tree.{ClassNode, MethodNode, InsnList} +import java.io.{FileOutputStream, FileInputStream, File => JFile, InputStream} import AsmNode._ /** @@ -127,3 +126,31 @@ abstract class BytecodeTest extends ASMConverters { new JavaClassPath(containers, DefaultJavaContext) } } + +object BytecodeTest { + /** Parse `file` as a class file, transforms the ASM representation with `f`, + * and overwrites the orginal file. + */ + def modifyClassFile(file: JFile)(f: ClassNode => ClassNode) { + val rfile = new reflect.io.File(file) + def readClass: ClassNode = { + val cr = new ClassReader(rfile.toByteArray()) + val cn = new ClassNode() + cr.accept(cn, 0) + cn + } + + def writeClass(cn: ClassNode) { + val writer = new ClassWriter(0) + cn.accept(writer) + val os = rfile.bufferedOutput() + try { + os.write(writer.toByteArray) + } finally { + os.close() + } + } + + writeClass(f(readClass)) + } +} diff --git a/test/files/run/t2464/Annotated.java b/test/files/run/t2464/Annotated.java new file mode 100644 index 0000000000..d022f9852c --- /dev/null +++ b/test/files/run/t2464/Annotated.java @@ -0,0 +1,5 @@ +package test; + +@Connect(loadStyle = Connect.LoadStyle.EAGER) +public class Annotated { +} diff --git a/test/files/run/t2464/Connect.java b/test/files/run/t2464/Connect.java new file mode 100644 index 0000000000..59349f94c8 --- /dev/null +++ b/test/files/run/t2464/Connect.java @@ -0,0 +1,20 @@ +package test; + +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 Connect { + + LoadStyle loadStyle() default LoadStyle.EAGER; + + public enum LoadStyle { + EAGER, + DEFERRED, + LAZY + } +} diff --git a/test/files/run/t2464/Test.scala b/test/files/run/t2464/Test.scala new file mode 100644 index 0000000000..90e1a03c17 --- /dev/null +++ b/test/files/run/t2464/Test.scala @@ -0,0 +1,35 @@ +import scala.reflect.io.Streamable +import scala.tools.asm.{ClassWriter, ClassReader} +import scala.tools.asm.tree.ClassNode +import scala.tools.partest._ +import scala.tools.partest.BytecodeTest.modifyClassFile +import java.io.{FileOutputStream, FileInputStream, File} + +object Test extends DirectTest { + def code = ??? + + def compileCode(code: String) = { + val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") + compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(code) + } + + def app = """ + object O { + new test.Annotated + } + """ + + def show(): Unit = { + compileCode(app) + modifyClassFile(new File(testOutput.toFile, "test/Annotated.class")) { + (cn: ClassNode) => + // As investigated https://issues.scala-lang.org/browse/SI-2464?focusedCommentId=64521&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-64521 + // classfiles in the wild sometimes lack the required InnerClass attribute for nested enums that + // are referenced in an annotation. I don't know what compiler or bytecode processor leaves things + // that way, but this test makes sure we don't crash. + cn.innerClasses.clear() + cn + } + compileCode(app) + } +} |