From 3a8d9fb6be1f11554401d305ef218ea4e25ffa06 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 12 May 2014 22:53:31 +0200 Subject: SI-8582 Pending test for InnerClasses bug in GenBCode As seen in a runtime reflection failure in Slick during a GenBCode enabled run of our beloved Community Build. --- test/files/run/t8582.check | 2 ++ test/files/run/t8582.scala | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/files/run/t8582.check create mode 100644 test/files/run/t8582.scala (limited to 'test/files/run') diff --git a/test/files/run/t8582.check b/test/files/run/t8582.check new file mode 100644 index 0000000000..7e96756986 --- /dev/null +++ b/test/files/run/t8582.check @@ -0,0 +1,2 @@ +class p1.p2.Singleton$Singleton$ +List(class p1.p2.Singleton$Singleton$Singleton$) diff --git a/test/files/run/t8582.scala b/test/files/run/t8582.scala new file mode 100644 index 0000000000..844ab8ac14 --- /dev/null +++ b/test/files/run/t8582.scala @@ -0,0 +1,16 @@ +package p1 { + package p2 { + object Singleton { + object Singleton { + object Singleton + } + } + } +} + + +object Test extends App { + import p1.p2._ + println(Singleton.Singleton.getClass) + println(Singleton.Singleton.getClass.getDeclaredClasses.toList) +} -- cgit v1.2.3 From e948073ae22167082ee672d8ac21507f7b3fa9f7 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 13 May 2014 12:31:26 +0200 Subject: SI-8582 emit InnerClasses attribute in GenBCode I removed the `-bcode` test since we have a build that passes `-Ybackend:GenBCode` to all tests. Short intro do the [`InnerClass` attribute][1]: - A class needs one `InnerClass` attribute for each of its nested classes - A class needs the `InnerClass` attribute for all (nested) classes that are mentioned in its constant pool The attribute for a nested class `A$B$C` consists of the long name of the outer class `A$B`, the short name of the inner class `C`, and an access flag set describig the visibility. The attribute seems to be used for reflection. [1]: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.6 --- .../tools/nsc/backend/jvm/BCodeSkelBuilder.scala | 3 +- test/files/run/t8582.check | 46 +++++++++++++- test/files/run/t8582.scala | 71 +++++++++++++++++++++- test/pending/run/t8582-bcode.check | 2 - test/pending/run/t8582-bcode.flags | 1 - test/pending/run/t8582-bcode.scala | 16 ----- 6 files changed, 114 insertions(+), 25 deletions(-) delete mode 100644 test/pending/run/t8582-bcode.check delete mode 100644 test/pending/run/t8582-bcode.flags delete mode 100644 test/pending/run/t8582-bcode.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index dae53bc0e5..ee9be5b11c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -116,12 +116,13 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { addClassFields() innerClassBufferASM ++= trackMemberClasses(claszSymbol, Nil) - gen(cd.impl) + addInnerClassesASM(cnode, innerClassBufferASM.toList) if (AsmUtils.traceClassEnabled && cnode.name.contains(AsmUtils.traceClassPattern)) AsmUtils.traceClass(cnode) + cnode.innerClasses assert(cd.symbol == claszSymbol, "Someone messed up BCodePhase.claszSymbol during genPlainClass().") } // end of method genPlainClass() diff --git a/test/files/run/t8582.check b/test/files/run/t8582.check index 7e96756986..564f482ff8 100644 --- a/test/files/run/t8582.check +++ b/test/files/run/t8582.check @@ -1,2 +1,44 @@ -class p1.p2.Singleton$Singleton$ -List(class p1.p2.Singleton$Singleton$Singleton$) +getClass on module gives module class + class p1.p2.Singleton$Singleton$ + +Nested module classes are found through reflection + p1.p2.Singleton$Singleton$: List(class p1.p2.Singleton$Singleton$Singleton$) + +Reflection can find direct nested classes (A1-B1-C1) + A1: List(class A1$B1) + A1$B1: List(class A1$B1$C1) + A1$B1$C1: List() + +Reflection can find direct nested classes (A2-B2-C2) + A2: List(class A2$B2) + A2$B2: List(class A2$B2$C2) + A2$B2$C2: List() + +Mirror classes have the same InnerClass attributes as the corresponding module class: + className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9] +Module class + className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9] + +An outer class has a InnerClass attribute for direct nested classes + className[A1$B1] outerClassName[A1] innerName[B1] access[1] +A nested class has an InnerClass attribute for itself (and also for its nested classes) + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] +C1 is a nested class, so it has an InnerClass attribute for itself. +Because that attribute leads to an entry for B1 in the constant pool, C1 needs an InnerClass attribute for B1. + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] + +The BeanInfo class has the same InnerClass attributes as the corresponding bean + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] + +Class A2 mentions class C2 in the constant pool (due to method f), therefore it needs an InnerClass attribute for C1 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] +B2 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] +C2 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] diff --git a/test/files/run/t8582.scala b/test/files/run/t8582.scala index 844ab8ac14..8a57ef7952 100644 --- a/test/files/run/t8582.scala +++ b/test/files/run/t8582.scala @@ -1,3 +1,6 @@ +import scala.tools.partest.BytecodeTest +import scala.collection.JavaConverters._ + package p1 { package p2 { object Singleton { @@ -8,9 +11,71 @@ package p1 { } } +class A1 { + class B1 { + @scala.beans.BeanInfo + class C1 + } +} + +class A2 { + class B2 { + class C2 + } + def f: B2#C2 = null +} + -object Test extends App { +object Test extends BytecodeTest { import p1.p2._ - println(Singleton.Singleton.getClass) - println(Singleton.Singleton.getClass.getDeclaredClasses.toList) + + def nested(c: Class[_]) = s" ${c.getName}: ${c.getDeclaredClasses.toList}" + + def nprintln(s: String) = println("\n"+s) + def printInner(cname: String): Unit = { + val cnode = loadClassNode(cname) + println(cnode.innerClasses.asScala.toList.map(i => s"className[${i.name}] outerClassName[${i.outerName}] innerName[${i.innerName}] access[${i.access}]").mkString(" ", "\n ", "")) + } + + def show() { + + println("getClass on module gives module class") + println(" " + Singleton.Singleton.getClass) + + nprintln("Nested module classes are found through reflection") + println(nested(Singleton.Singleton.getClass)) + + nprintln("Reflection can find direct nested classes (A1-B1-C1)") + println(nested(classOf[A1])) + println(nested(classOf[A1#B1])) + println(nested(classOf[A1#B1#C1])) + + nprintln("Reflection can find direct nested classes (A2-B2-C2)") + println(nested(classOf[A2])) + println(nested(classOf[A2#B2])) + println(nested(classOf[A2#B2#C2])) + + nprintln("Mirror classes have the same InnerClass attributes as the corresponding module class:") + printInner("p1.p2.Singleton") // mirror class + println("Module class") + printInner("p1.p2.Singleton$") + + nprintln("An outer class has a InnerClass attribute for direct nested classes") + printInner("A1") + println("A nested class has an InnerClass attribute for itself (and also for its nested classes)") + printInner("A1$B1") + println("C1 is a nested class, so it has an InnerClass attribute for itself.\n"+ + "Because that attribute leads to an entry for B1 in the constant pool, C1 needs an InnerClass attribute for B1.") + printInner("A1$B1$C1") + + nprintln("The BeanInfo class has the same InnerClass attributes as the corresponding bean") + printInner("A1$B1$C1BeanInfo") + + nprintln("Class A2 mentions class C2 in the constant pool (due to method f), therefore it needs an InnerClass attribute for C1") + printInner("A2") + println("B2") + printInner("A2$B2") + println("C2") + printInner("A2$B2$C2") + } } diff --git a/test/pending/run/t8582-bcode.check b/test/pending/run/t8582-bcode.check deleted file mode 100644 index 7e96756986..0000000000 --- a/test/pending/run/t8582-bcode.check +++ /dev/null @@ -1,2 +0,0 @@ -class p1.p2.Singleton$Singleton$ -List(class p1.p2.Singleton$Singleton$Singleton$) diff --git a/test/pending/run/t8582-bcode.flags b/test/pending/run/t8582-bcode.flags deleted file mode 100644 index c30091d3de..0000000000 --- a/test/pending/run/t8582-bcode.flags +++ /dev/null @@ -1 +0,0 @@ --Ybackend:GenBCode diff --git a/test/pending/run/t8582-bcode.scala b/test/pending/run/t8582-bcode.scala deleted file mode 100644 index 844ab8ac14..0000000000 --- a/test/pending/run/t8582-bcode.scala +++ /dev/null @@ -1,16 +0,0 @@ -package p1 { - package p2 { - object Singleton { - object Singleton { - object Singleton - } - } - } -} - - -object Test extends App { - import p1.p2._ - println(Singleton.Singleton.getClass) - println(Singleton.Singleton.getClass.getDeclaredClasses.toList) -} -- cgit v1.2.3 From fa2204e1749b21aa838350f321d5d85644be4ecf Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 13 May 2014 17:39:46 +0200 Subject: Move t8582 to test/files/jvm --- test/files/jvm/t8582.check | 44 +++++++++++++++++++++++++ test/files/jvm/t8582.scala | 81 ++++++++++++++++++++++++++++++++++++++++++++++ test/files/run/t8582.check | 44 ------------------------- test/files/run/t8582.scala | 81 ---------------------------------------------- 4 files changed, 125 insertions(+), 125 deletions(-) create mode 100644 test/files/jvm/t8582.check create mode 100644 test/files/jvm/t8582.scala delete mode 100644 test/files/run/t8582.check delete mode 100644 test/files/run/t8582.scala (limited to 'test/files/run') diff --git a/test/files/jvm/t8582.check b/test/files/jvm/t8582.check new file mode 100644 index 0000000000..564f482ff8 --- /dev/null +++ b/test/files/jvm/t8582.check @@ -0,0 +1,44 @@ +getClass on module gives module class + class p1.p2.Singleton$Singleton$ + +Nested module classes are found through reflection + p1.p2.Singleton$Singleton$: List(class p1.p2.Singleton$Singleton$Singleton$) + +Reflection can find direct nested classes (A1-B1-C1) + A1: List(class A1$B1) + A1$B1: List(class A1$B1$C1) + A1$B1$C1: List() + +Reflection can find direct nested classes (A2-B2-C2) + A2: List(class A2$B2) + A2$B2: List(class A2$B2$C2) + A2$B2$C2: List() + +Mirror classes have the same InnerClass attributes as the corresponding module class: + className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9] +Module class + className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9] + +An outer class has a InnerClass attribute for direct nested classes + className[A1$B1] outerClassName[A1] innerName[B1] access[1] +A nested class has an InnerClass attribute for itself (and also for its nested classes) + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] +C1 is a nested class, so it has an InnerClass attribute for itself. +Because that attribute leads to an entry for B1 in the constant pool, C1 needs an InnerClass attribute for B1. + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] + +The BeanInfo class has the same InnerClass attributes as the corresponding bean + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] + +Class A2 mentions class C2 in the constant pool (due to method f), therefore it needs an InnerClass attribute for C1 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] +B2 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] +C2 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] diff --git a/test/files/jvm/t8582.scala b/test/files/jvm/t8582.scala new file mode 100644 index 0000000000..8a57ef7952 --- /dev/null +++ b/test/files/jvm/t8582.scala @@ -0,0 +1,81 @@ +import scala.tools.partest.BytecodeTest +import scala.collection.JavaConverters._ + +package p1 { + package p2 { + object Singleton { + object Singleton { + object Singleton + } + } + } +} + +class A1 { + class B1 { + @scala.beans.BeanInfo + class C1 + } +} + +class A2 { + class B2 { + class C2 + } + def f: B2#C2 = null +} + + +object Test extends BytecodeTest { + import p1.p2._ + + def nested(c: Class[_]) = s" ${c.getName}: ${c.getDeclaredClasses.toList}" + + def nprintln(s: String) = println("\n"+s) + def printInner(cname: String): Unit = { + val cnode = loadClassNode(cname) + println(cnode.innerClasses.asScala.toList.map(i => s"className[${i.name}] outerClassName[${i.outerName}] innerName[${i.innerName}] access[${i.access}]").mkString(" ", "\n ", "")) + } + + def show() { + + println("getClass on module gives module class") + println(" " + Singleton.Singleton.getClass) + + nprintln("Nested module classes are found through reflection") + println(nested(Singleton.Singleton.getClass)) + + nprintln("Reflection can find direct nested classes (A1-B1-C1)") + println(nested(classOf[A1])) + println(nested(classOf[A1#B1])) + println(nested(classOf[A1#B1#C1])) + + nprintln("Reflection can find direct nested classes (A2-B2-C2)") + println(nested(classOf[A2])) + println(nested(classOf[A2#B2])) + println(nested(classOf[A2#B2#C2])) + + nprintln("Mirror classes have the same InnerClass attributes as the corresponding module class:") + printInner("p1.p2.Singleton") // mirror class + println("Module class") + printInner("p1.p2.Singleton$") + + nprintln("An outer class has a InnerClass attribute for direct nested classes") + printInner("A1") + println("A nested class has an InnerClass attribute for itself (and also for its nested classes)") + printInner("A1$B1") + println("C1 is a nested class, so it has an InnerClass attribute for itself.\n"+ + "Because that attribute leads to an entry for B1 in the constant pool, C1 needs an InnerClass attribute for B1.") + printInner("A1$B1$C1") + + nprintln("The BeanInfo class has the same InnerClass attributes as the corresponding bean") + printInner("A1$B1$C1BeanInfo") + + nprintln("Class A2 mentions class C2 in the constant pool (due to method f), therefore it needs an InnerClass attribute for C1") + printInner("A2") + println("B2") + printInner("A2$B2") + println("C2") + printInner("A2$B2$C2") + } +} diff --git a/test/files/run/t8582.check b/test/files/run/t8582.check deleted file mode 100644 index 564f482ff8..0000000000 --- a/test/files/run/t8582.check +++ /dev/null @@ -1,44 +0,0 @@ -getClass on module gives module class - class p1.p2.Singleton$Singleton$ - -Nested module classes are found through reflection - p1.p2.Singleton$Singleton$: List(class p1.p2.Singleton$Singleton$Singleton$) - -Reflection can find direct nested classes (A1-B1-C1) - A1: List(class A1$B1) - A1$B1: List(class A1$B1$C1) - A1$B1$C1: List() - -Reflection can find direct nested classes (A2-B2-C2) - A2: List(class A2$B2) - A2$B2: List(class A2$B2$C2) - A2$B2$C2: List() - -Mirror classes have the same InnerClass attributes as the corresponding module class: - className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9] -Module class - className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9] - -An outer class has a InnerClass attribute for direct nested classes - className[A1$B1] outerClassName[A1] innerName[B1] access[1] -A nested class has an InnerClass attribute for itself (and also for its nested classes) - className[A1$B1] outerClassName[A1] innerName[B1] access[1] - className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] -C1 is a nested class, so it has an InnerClass attribute for itself. -Because that attribute leads to an entry for B1 in the constant pool, C1 needs an InnerClass attribute for B1. - className[A1$B1] outerClassName[A1] innerName[B1] access[1] - className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] - -The BeanInfo class has the same InnerClass attributes as the corresponding bean - className[A1$B1] outerClassName[A1] innerName[B1] access[1] - className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] - -Class A2 mentions class C2 in the constant pool (due to method f), therefore it needs an InnerClass attribute for C1 - className[A2$B2] outerClassName[A2] innerName[B2] access[1] - className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] -B2 - className[A2$B2] outerClassName[A2] innerName[B2] access[1] - className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] -C2 - className[A2$B2] outerClassName[A2] innerName[B2] access[1] - className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] diff --git a/test/files/run/t8582.scala b/test/files/run/t8582.scala deleted file mode 100644 index 8a57ef7952..0000000000 --- a/test/files/run/t8582.scala +++ /dev/null @@ -1,81 +0,0 @@ -import scala.tools.partest.BytecodeTest -import scala.collection.JavaConverters._ - -package p1 { - package p2 { - object Singleton { - object Singleton { - object Singleton - } - } - } -} - -class A1 { - class B1 { - @scala.beans.BeanInfo - class C1 - } -} - -class A2 { - class B2 { - class C2 - } - def f: B2#C2 = null -} - - -object Test extends BytecodeTest { - import p1.p2._ - - def nested(c: Class[_]) = s" ${c.getName}: ${c.getDeclaredClasses.toList}" - - def nprintln(s: String) = println("\n"+s) - def printInner(cname: String): Unit = { - val cnode = loadClassNode(cname) - println(cnode.innerClasses.asScala.toList.map(i => s"className[${i.name}] outerClassName[${i.outerName}] innerName[${i.innerName}] access[${i.access}]").mkString(" ", "\n ", "")) - } - - def show() { - - println("getClass on module gives module class") - println(" " + Singleton.Singleton.getClass) - - nprintln("Nested module classes are found through reflection") - println(nested(Singleton.Singleton.getClass)) - - nprintln("Reflection can find direct nested classes (A1-B1-C1)") - println(nested(classOf[A1])) - println(nested(classOf[A1#B1])) - println(nested(classOf[A1#B1#C1])) - - nprintln("Reflection can find direct nested classes (A2-B2-C2)") - println(nested(classOf[A2])) - println(nested(classOf[A2#B2])) - println(nested(classOf[A2#B2#C2])) - - nprintln("Mirror classes have the same InnerClass attributes as the corresponding module class:") - printInner("p1.p2.Singleton") // mirror class - println("Module class") - printInner("p1.p2.Singleton$") - - nprintln("An outer class has a InnerClass attribute for direct nested classes") - printInner("A1") - println("A nested class has an InnerClass attribute for itself (and also for its nested classes)") - printInner("A1$B1") - println("C1 is a nested class, so it has an InnerClass attribute for itself.\n"+ - "Because that attribute leads to an entry for B1 in the constant pool, C1 needs an InnerClass attribute for B1.") - printInner("A1$B1$C1") - - nprintln("The BeanInfo class has the same InnerClass attributes as the corresponding bean") - printInner("A1$B1$C1BeanInfo") - - nprintln("Class A2 mentions class C2 in the constant pool (due to method f), therefore it needs an InnerClass attribute for C1") - printInner("A2") - println("B2") - printInner("A2$B2") - println("C2") - printInner("A2$B2$C2") - } -} -- cgit v1.2.3 From f9abdcef6c9b8d96e1aaf98942938e2e875285d0 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 10 May 2014 10:49:36 +0200 Subject: SI-8574 Copy @SerialVersionUID, etc, to specialized subclasses The test case demonstrates that this is important for serialization and for strictfp. (Although the latter is still pretty broken, see SI-7954.) Now that the synthetic subclass of `Tuple2[Int, Int]` also has the `@deprecatedInheritance` annotation, I had to change the spot that issues this warning to be silent after the typer phase. Otherwise, we get two warnings in `run/t3888.scala`. This also remedies double warnings that were incurred in `neg/t6162-inheritance`. --- .../tools/nsc/transform/SpecializeTypes.scala | 1 + .../scala/tools/nsc/typechecker/Typers.scala | 2 +- test/files/neg/t6162-inheritance.check | 8 +------ test/files/run/t8574.scala | 27 ++++++++++++++++++++++ 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 test/files/run/t8574.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 02e55241b3..908aa69310 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -538,6 +538,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { bytecodeClazz.info val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE) + sClass.setAnnotations(clazz.annotations) // SI-8574 important that the subclass picks up @SerialVersionUID, @strictfp, etc. def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long, newName: Name = null) = member.cloneSymbol(sClass, flagFn(member.flags | SPECIALIZED), newName) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index bf98c0e3dc..9fe693ce2a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1687,7 +1687,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val sameSourceFile = context.unit.source.file == psym.sourceFile - if (psym.hasDeprecatedInheritanceAnnotation && !sameSourceFile) { + if (!isPastTyper && psym.hasDeprecatedInheritanceAnnotation && !sameSourceFile) { val suffix = psym.deprecatedInheritanceMessage map (": " + _) getOrElse "" val msg = s"inheritance from ${psym.fullLocationString} is deprecated$suffix" unit.deprecationWarning(parent.pos, msg) diff --git a/test/files/neg/t6162-inheritance.check b/test/files/neg/t6162-inheritance.check index 13c78030d9..c9f4ddaec1 100644 --- a/test/files/neg/t6162-inheritance.check +++ b/test/files/neg/t6162-inheritance.check @@ -7,12 +7,6 @@ object SubT extends T usage.scala:8: warning: inheritance from trait S in package t6126 is deprecated new S { ^ -usage.scala:3: warning: inheritance from class Foo in package t6126 is deprecated: `Foo` will be made final in a future version. -class SubFoo extends Foo - ^ -usage.scala:5: warning: inheritance from trait T in package t6126 is deprecated -object SubT extends T - ^ error: No warnings can be incurred under -Xfatal-warnings. -5 warnings found +three warnings found one error found diff --git a/test/files/run/t8574.scala b/test/files/run/t8574.scala new file mode 100644 index 0000000000..8c23ada482 --- /dev/null +++ b/test/files/run/t8574.scala @@ -0,0 +1,27 @@ +import annotation._ + +@SerialVersionUID(42) @strictfp class Foo[@specialized(Int) T] extends Serializable { + def foo(t: T) = t +} + +object Test extends App { + def checkUID(cls: Class[_], expected: Long) = { + val actual = java.io.ObjectStreamClass.lookup(cls).getSerialVersionUID + assert(actual == expected, s"$actual != expected for ${cls}") + } + def checkStrictFp(cls: Class[_]) = { + import java.lang.reflect._ + for (m <- cls.getDeclaredMethods) { + val isStrict = Modifier.isStrict(m.getModifiers) + assert(isStrict, cls) + } + } + def check(x: AnyRef) { + checkUID(x.getClass, 42) + checkStrictFp(x.getClass) + } + + check(new Foo[String]) + check(new Foo[Int]) +} + -- cgit v1.2.3 From ee611cd76c29fedd416162e482c7ab3f15b831ca Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 10:24:43 +0200 Subject: SI-8601 Don't treat int/long division, or arraylength, as dead-code `{i, l}div` and `{i, l}rem` throw an `ArithmeticException` if the divisor is 0. `arraylength` throws a `NullPointerException` on a null reference. JVM Spec: > The only integer operations that can throw an exception are the > integer divide instructions (idiv and ldiv) and the integer > remainder instructions (irem and lrem), which throw an > ArithmeticException if the divisor is zero. > The Java virtual machine's floating-point operators do not throw > runtime exceptions > If the arrayref is null, the arraylength instruction throws a > NullPointerException. I checked the other primitives in `ICode` to see if anything else should be considered as live code. Pure: // jvm : {i, l, f, d}neg case class Negation(kind: TypeKind) extends Primitive // jvm : if{eq, ne, lt, ge, le, gt}, if{null, nonnull} // if_icmp{eq, ne, lt, ge, le, gt}, if_acmp{eq,ne} case class Test(op: TestOp, kind: TypeKind, zero: Boolean) extends Primitive // jvm : lcmp, {f, d}cmp{l, g} case class Comparison(op: ComparisonOp, kind: TypeKind) extends Primitive Impure: {i, l}{div, rem}, otherwise pure // jvm : {i, l, f, d}{add, sub, mul, div, rem} case class Arithmetic(op: ArithmeticOp, kind: TypeKind) extends Primitive Pure (overflow is silent, NaN.toInt is defined): // jvm : {i, l}{and, or, xor} case class Logical(op: LogicalOp, kind: TypeKind) extends Primitive // jvm : {i, l}{shl, ushl, shr} case class Shift(op: ShiftOp, kind: TypeKind) extends Primitive // jvm : i2{l, f, d}, l2{i, f, d}, f2{i, l, d}, d2{i, l, f}, i2{b, c, s} case class Conversion(src: TypeKind, dst: TypeKind) extends Primitive Impure! May NPE! // jvm : arraylength case class ArrayLength(kind: TypeKind) extends Primitive Pure (we know that StringBuilder.{, append, toString} are pure and `append` is null safe.) // jvm : It should call the appropiate 'append' method on StringBuffer case class StringConcat(el: TypeKind) extends Primitive // jvm: it should create a new StringBuffer case object StartConcat extends Primitive // jvm: convert StringBuffer to a String case object EndConcat extends Primitive --- .../scala/tools/nsc/backend/opt/DeadCodeElimination.scala | 2 ++ test/files/run/t8601.flags | 1 + test/files/run/t8601.scala | 15 +++++++++++++++ test/files/run/t8601b.flags | 1 + test/files/run/t8601b.scala | 9 +++++++++ 5 files changed, 28 insertions(+) create mode 100644 test/files/run/t8601.flags create mode 100644 test/files/run/t8601.scala create mode 100644 test/files/run/t8601b.flags create mode 100644 test/files/run/t8601b.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 90c37ba0b3..3c983e6fdf 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -193,6 +193,8 @@ abstract class DeadCodeElimination extends SubComponent { moveToWorkListIf(necessary) case LOAD_MODULE(sym) if isLoadNeeded(sym) => moveToWorkList() // SI-4859 Module initialization might side-effect. + case CALL_PRIMITIVE(Arithmetic(DIV | REM, INT | LONG) | ArrayLength(_)) => + moveToWorkList() // SI-8601 Might divide by zero case _ => () moveToWorkListIf(cond = false) } diff --git a/test/files/run/t8601.flags b/test/files/run/t8601.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/run/t8601.scala b/test/files/run/t8601.scala new file mode 100644 index 0000000000..e1afc23cc4 --- /dev/null +++ b/test/files/run/t8601.scala @@ -0,0 +1,15 @@ +object Test { + def idiv(x: Int): Unit = x / 0 + def ldiv(x: Long): Unit = x / 0 + def irem(x: Int): Unit = x % 0 + def lrem(x: Long): Unit = x % 0 + + def check(x: => Any) = try { x; sys.error("failed to throw divide by zero!") } catch { case _: ArithmeticException => } + + def main(args: Array[String]) { + check(idiv(1)) + check(ldiv(1L)) + check(irem(1)) + check(lrem(1L)) + } +} diff --git a/test/files/run/t8601b.flags b/test/files/run/t8601b.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601b.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala new file mode 100644 index 0000000000..42b562af96 --- /dev/null +++ b/test/files/run/t8601b.scala @@ -0,0 +1,9 @@ +object Test { + def len(x: Array[String]): Unit = x.length + + def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + + def main(args: Array[String]) { + check(len(null)) + } +} -- cgit v1.2.3 From 0b432f9cd22b6e9770852e5b331a15f0534a312c Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 12:01:17 +0200 Subject: SI-8601 Avoid over-eager optimization of LOAD_FIELD It can NPE or trigger static class initilization, we can't elimiate it without changing semantics. --- .../scala/tools/nsc/backend/opt/ClosureElimination.scala | 9 ++++----- .../scala/tools/nsc/backend/opt/DeadCodeElimination.scala | 2 +- test/files/run/t8601c.flags | 1 + test/files/run/t8601c.scala | 12 ++++++++++++ 4 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 test/files/run/t8601c.flags create mode 100644 test/files/run/t8601c.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index c49f23852f..0b943360de 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -56,11 +56,10 @@ abstract class ClosureElimination extends SubComponent { case (BOX(t1), UNBOX(t2)) if (t1 == t2) => Some(Nil) - case (LOAD_FIELD(sym, isStatic), DROP(_)) if !sym.hasAnnotation(definitions.VolatileAttr) => - if (isStatic) - Some(Nil) - else - Some(DROP(REFERENCE(definitions.ObjectClass)) :: Nil) + // Can't eliminate (LOAD_FIELD, DROP) without eliding side effects: + // - static class initialization + // - NPE if the receiver is null + // We could replace the LOAD/DROP with a null check to preserve semantics. case _ => None } diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 3c983e6fdf..64aed19bf0 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -167,7 +167,7 @@ abstract class DeadCodeElimination extends SubComponent { set += idx localStores(key) = set - case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | + case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | LOAD_FIELD(_, _) | // Why LOAD_FIELD? It can NPE! THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) => moveToWorkList() diff --git a/test/files/run/t8601c.flags b/test/files/run/t8601c.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601c.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/run/t8601c.scala b/test/files/run/t8601c.scala new file mode 100644 index 0000000000..c487d6825e --- /dev/null +++ b/test/files/run/t8601c.scala @@ -0,0 +1,12 @@ +object Test { + def loadField(x: scala.runtime.IntRef): Unit = x.elem + def storeField(x: scala.runtime.IntRef): Unit = x.elem = 42 + + def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + + def main(args: Array[String]) { + check(loadField(null)) // bug: did not NPE under -Ydead-code + check(storeField(null)) + + } +} -- cgit v1.2.3 From 99b4ef8d8472f154d73160f5fe72daf081abb24e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 12:11:29 +0200 Subject: Add a test for array load --- test/files/run/t8601b.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'test/files/run') diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala index 42b562af96..c01caa57d0 100644 --- a/test/files/run/t8601b.scala +++ b/test/files/run/t8601b.scala @@ -1,9 +1,11 @@ object Test { def len(x: Array[String]): Unit = x.length + def load(x: Array[String]): Unit = x(0) def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } def main(args: Array[String]) { - check(len(null)) + check(len(null)) // bug: did not NPE + check(load(null)) } } -- cgit v1.2.3 From 70b912a87433c9589af33e4f8b33dca39abb66e5 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 12:17:02 +0200 Subject: SI-8601 Don't treat newarray as dead code Otherwise we lose the side effect of a `NegativeArraySizeException`. --- src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala | 2 +- test/files/run/t8601b.scala | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 64aed19bf0..245894a4af 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -169,7 +169,7 @@ abstract class DeadCodeElimination extends SubComponent { case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | LOAD_FIELD(_, _) | // Why LOAD_FIELD? It can NPE! THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | - LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) => + LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) | CREATE_ARRAY(_, _) => moveToWorkList() case CALL_METHOD(m1, _) if isSideEffecting(m1) => diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala index c01caa57d0..9c37ce33d6 100644 --- a/test/files/run/t8601b.scala +++ b/test/files/run/t8601b.scala @@ -1,11 +1,14 @@ object Test { def len(x: Array[String]): Unit = x.length def load(x: Array[String]): Unit = x(0) + def newarray(i: Int): Unit = new Array[Int](i) def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + def checkNegSize(x: => Any) = try { x; sys.error("failed to throw NegativeArraySizeException!") } catch { case _: NegativeArraySizeException => } def main(args: Array[String]) { check(len(null)) // bug: did not NPE check(load(null)) + checkNegSize(newarray(-1)) } } -- cgit v1.2.3 From dcade51d751b389fb5137040f7e1006b4bc633c6 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 12:21:45 +0200 Subject: SI-8601 Test that `null.synchronized` NPEs under -optimize As part of my sweep through the side-effecting byte code instructions. --- test/files/run/t8601d.flags | 1 + test/files/run/t8601d.scala | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 test/files/run/t8601d.flags create mode 100644 test/files/run/t8601d.scala (limited to 'test/files/run') diff --git a/test/files/run/t8601d.flags b/test/files/run/t8601d.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601d.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/run/t8601d.scala b/test/files/run/t8601d.scala new file mode 100644 index 0000000000..ac89963d67 --- /dev/null +++ b/test/files/run/t8601d.scala @@ -0,0 +1,8 @@ +object Test { + def monitor(x: AnyRef): Unit = {x.synchronized(()); ()} + def check(x: => Any) = try { x; sys.error("failed to throw NPE") } catch { case _: NullPointerException => } + + def main(args: Array[String]) { + check(monitor(null)) + } +} -- cgit v1.2.3 From 0e87b12bcf68d9f52d2dee92ff19d04019641110 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 19 May 2014 12:56:07 +0200 Subject: Revert "SI-8601 Test that `null.synchronized` NPEs under -optimize" This reverts commit dcade51d751b389fb5137040f7e1006b4bc633c6. --- test/files/run/t8601d.flags | 1 - test/files/run/t8601d.scala | 8 -------- 2 files changed, 9 deletions(-) delete mode 100644 test/files/run/t8601d.flags delete mode 100644 test/files/run/t8601d.scala (limited to 'test/files/run') diff --git a/test/files/run/t8601d.flags b/test/files/run/t8601d.flags deleted file mode 100644 index 1182725e86..0000000000 --- a/test/files/run/t8601d.flags +++ /dev/null @@ -1 +0,0 @@ --optimize \ No newline at end of file diff --git a/test/files/run/t8601d.scala b/test/files/run/t8601d.scala deleted file mode 100644 index ac89963d67..0000000000 --- a/test/files/run/t8601d.scala +++ /dev/null @@ -1,8 +0,0 @@ -object Test { - def monitor(x: AnyRef): Unit = {x.synchronized(()); ()} - def check(x: => Any) = try { x; sys.error("failed to throw NPE") } catch { case _: NullPointerException => } - - def main(args: Array[String]) { - check(monitor(null)) - } -} -- cgit v1.2.3 From c3d61574c908d7fee5d16b300449c2958adad9fb Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 19 May 2014 12:56:09 +0200 Subject: Revert "SI-8601 Don't treat newarray as dead code" This reverts commit 70b912a87433c9589af33e4f8b33dca39abb66e5. --- src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala | 2 +- test/files/run/t8601b.scala | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 245894a4af..64aed19bf0 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -169,7 +169,7 @@ abstract class DeadCodeElimination extends SubComponent { case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | LOAD_FIELD(_, _) | // Why LOAD_FIELD? It can NPE! THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | - LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) | CREATE_ARRAY(_, _) => + LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) => moveToWorkList() case CALL_METHOD(m1, _) if isSideEffecting(m1) => diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala index 9c37ce33d6..c01caa57d0 100644 --- a/test/files/run/t8601b.scala +++ b/test/files/run/t8601b.scala @@ -1,14 +1,11 @@ object Test { def len(x: Array[String]): Unit = x.length def load(x: Array[String]): Unit = x(0) - def newarray(i: Int): Unit = new Array[Int](i) def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } - def checkNegSize(x: => Any) = try { x; sys.error("failed to throw NegativeArraySizeException!") } catch { case _: NegativeArraySizeException => } def main(args: Array[String]) { check(len(null)) // bug: did not NPE check(load(null)) - checkNegSize(newarray(-1)) } } -- cgit v1.2.3 From b11a27e3e655105ec6f7640934072ba7966c88a3 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 19 May 2014 12:56:10 +0200 Subject: Revert "Add a test for array load" This reverts commit 99b4ef8d8472f154d73160f5fe72daf081abb24e. --- test/files/run/t8601b.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'test/files/run') diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala index c01caa57d0..42b562af96 100644 --- a/test/files/run/t8601b.scala +++ b/test/files/run/t8601b.scala @@ -1,11 +1,9 @@ object Test { def len(x: Array[String]): Unit = x.length - def load(x: Array[String]): Unit = x(0) def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } def main(args: Array[String]) { - check(len(null)) // bug: did not NPE - check(load(null)) + check(len(null)) } } -- cgit v1.2.3 From f0eb8d93d51b110f11786a5f3664cde9db66efcc Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 19 May 2014 12:56:11 +0200 Subject: Revert "SI-8601 Avoid over-eager optimization of LOAD_FIELD" This reverts commit 0b432f9cd22b6e9770852e5b331a15f0534a312c. --- .../scala/tools/nsc/backend/opt/ClosureElimination.scala | 9 +++++---- .../scala/tools/nsc/backend/opt/DeadCodeElimination.scala | 2 +- test/files/run/t8601c.flags | 1 - test/files/run/t8601c.scala | 12 ------------ 4 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 test/files/run/t8601c.flags delete mode 100644 test/files/run/t8601c.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index 0b943360de..c49f23852f 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -56,10 +56,11 @@ abstract class ClosureElimination extends SubComponent { case (BOX(t1), UNBOX(t2)) if (t1 == t2) => Some(Nil) - // Can't eliminate (LOAD_FIELD, DROP) without eliding side effects: - // - static class initialization - // - NPE if the receiver is null - // We could replace the LOAD/DROP with a null check to preserve semantics. + case (LOAD_FIELD(sym, isStatic), DROP(_)) if !sym.hasAnnotation(definitions.VolatileAttr) => + if (isStatic) + Some(Nil) + else + Some(DROP(REFERENCE(definitions.ObjectClass)) :: Nil) case _ => None } diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 64aed19bf0..3c983e6fdf 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -167,7 +167,7 @@ abstract class DeadCodeElimination extends SubComponent { set += idx localStores(key) = set - case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | LOAD_FIELD(_, _) | // Why LOAD_FIELD? It can NPE! + case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) => moveToWorkList() diff --git a/test/files/run/t8601c.flags b/test/files/run/t8601c.flags deleted file mode 100644 index 1182725e86..0000000000 --- a/test/files/run/t8601c.flags +++ /dev/null @@ -1 +0,0 @@ --optimize \ No newline at end of file diff --git a/test/files/run/t8601c.scala b/test/files/run/t8601c.scala deleted file mode 100644 index c487d6825e..0000000000 --- a/test/files/run/t8601c.scala +++ /dev/null @@ -1,12 +0,0 @@ -object Test { - def loadField(x: scala.runtime.IntRef): Unit = x.elem - def storeField(x: scala.runtime.IntRef): Unit = x.elem = 42 - - def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } - - def main(args: Array[String]) { - check(loadField(null)) // bug: did not NPE under -Ydead-code - check(storeField(null)) - - } -} -- cgit v1.2.3 From 81309e7e0fb568b3ba12df9631c607232b68960b Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 19 May 2014 12:56:12 +0200 Subject: Revert "SI-8601 Don't treat int/long division, or arraylength, as dead-code" This reverts commit ee611cd76c29fedd416162e482c7ab3f15b831ca. --- .../scala/tools/nsc/backend/opt/DeadCodeElimination.scala | 2 -- test/files/run/t8601.flags | 1 - test/files/run/t8601.scala | 15 --------------- test/files/run/t8601b.flags | 1 - test/files/run/t8601b.scala | 9 --------- 5 files changed, 28 deletions(-) delete mode 100644 test/files/run/t8601.flags delete mode 100644 test/files/run/t8601.scala delete mode 100644 test/files/run/t8601b.flags delete mode 100644 test/files/run/t8601b.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 3c983e6fdf..90c37ba0b3 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -193,8 +193,6 @@ abstract class DeadCodeElimination extends SubComponent { moveToWorkListIf(necessary) case LOAD_MODULE(sym) if isLoadNeeded(sym) => moveToWorkList() // SI-4859 Module initialization might side-effect. - case CALL_PRIMITIVE(Arithmetic(DIV | REM, INT | LONG) | ArrayLength(_)) => - moveToWorkList() // SI-8601 Might divide by zero case _ => () moveToWorkListIf(cond = false) } diff --git a/test/files/run/t8601.flags b/test/files/run/t8601.flags deleted file mode 100644 index 1182725e86..0000000000 --- a/test/files/run/t8601.flags +++ /dev/null @@ -1 +0,0 @@ --optimize \ No newline at end of file diff --git a/test/files/run/t8601.scala b/test/files/run/t8601.scala deleted file mode 100644 index e1afc23cc4..0000000000 --- a/test/files/run/t8601.scala +++ /dev/null @@ -1,15 +0,0 @@ -object Test { - def idiv(x: Int): Unit = x / 0 - def ldiv(x: Long): Unit = x / 0 - def irem(x: Int): Unit = x % 0 - def lrem(x: Long): Unit = x % 0 - - def check(x: => Any) = try { x; sys.error("failed to throw divide by zero!") } catch { case _: ArithmeticException => } - - def main(args: Array[String]) { - check(idiv(1)) - check(ldiv(1L)) - check(irem(1)) - check(lrem(1L)) - } -} diff --git a/test/files/run/t8601b.flags b/test/files/run/t8601b.flags deleted file mode 100644 index 1182725e86..0000000000 --- a/test/files/run/t8601b.flags +++ /dev/null @@ -1 +0,0 @@ --optimize \ No newline at end of file diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala deleted file mode 100644 index 42b562af96..0000000000 --- a/test/files/run/t8601b.scala +++ /dev/null @@ -1,9 +0,0 @@ -object Test { - def len(x: Array[String]): Unit = x.length - - def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } - - def main(args: Array[String]) { - check(len(null)) - } -} -- cgit v1.2.3 From 5b1d43d8ffda4b5881df7ce6634a7adebbb02f21 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 10:24:43 +0200 Subject: SI-8601 Don't treat int/long division, or arraylength, as dead-code `{i, l}div` and `{i, l}rem` throw an `ArithmeticException` if the divisor is 0. `arraylength` throws a `NullPointerException` on a null reference. JVM Spec: > The only integer operations that can throw an exception are the > integer divide instructions (idiv and ldiv) and the integer > remainder instructions (irem and lrem), which throw an > ArithmeticException if the divisor is zero. > The Java virtual machine's floating-point operators do not throw > runtime exceptions > If the arrayref is null, the arraylength instruction throws a > NullPointerException. I checked the other primitives in `ICode` to see if anything else should be considered as live code. Pure: // jvm : {i, l, f, d}neg case class Negation(kind: TypeKind) extends Primitive // jvm : if{eq, ne, lt, ge, le, gt}, if{null, nonnull} // if_icmp{eq, ne, lt, ge, le, gt}, if_acmp{eq,ne} case class Test(op: TestOp, kind: TypeKind, zero: Boolean) extends Primitive // jvm : lcmp, {f, d}cmp{l, g} case class Comparison(op: ComparisonOp, kind: TypeKind) extends Primitive Impure: {i, l}{div, rem}, otherwise pure // jvm : {i, l, f, d}{add, sub, mul, div, rem} case class Arithmetic(op: ArithmeticOp, kind: TypeKind) extends Primitive Pure (overflow is silent, NaN.toInt is defined): // jvm : {i, l}{and, or, xor} case class Logical(op: LogicalOp, kind: TypeKind) extends Primitive // jvm : {i, l}{shl, ushl, shr} case class Shift(op: ShiftOp, kind: TypeKind) extends Primitive // jvm : i2{l, f, d}, l2{i, f, d}, f2{i, l, d}, d2{i, l, f}, i2{b, c, s} case class Conversion(src: TypeKind, dst: TypeKind) extends Primitive Impure! May NPE! // jvm : arraylength case class ArrayLength(kind: TypeKind) extends Primitive Pure (we know that StringBuilder.{, append, toString} are pure and `append` is null safe.) // jvm : It should call the appropiate 'append' method on StringBuffer case class StringConcat(el: TypeKind) extends Primitive // jvm: it should create a new StringBuffer case object StartConcat extends Primitive // jvm: convert StringBuffer to a String case object EndConcat extends Primitive --- .../scala/tools/nsc/backend/opt/DeadCodeElimination.scala | 2 ++ test/files/run/t8601.flags | 1 + test/files/run/t8601.scala | 15 +++++++++++++++ test/files/run/t8601b.flags | 1 + test/files/run/t8601b.scala | 9 +++++++++ 5 files changed, 28 insertions(+) create mode 100644 test/files/run/t8601.flags create mode 100644 test/files/run/t8601.scala create mode 100644 test/files/run/t8601b.flags create mode 100644 test/files/run/t8601b.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 90c37ba0b3..3c983e6fdf 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -193,6 +193,8 @@ abstract class DeadCodeElimination extends SubComponent { moveToWorkListIf(necessary) case LOAD_MODULE(sym) if isLoadNeeded(sym) => moveToWorkList() // SI-4859 Module initialization might side-effect. + case CALL_PRIMITIVE(Arithmetic(DIV | REM, INT | LONG) | ArrayLength(_)) => + moveToWorkList() // SI-8601 Might divide by zero case _ => () moveToWorkListIf(cond = false) } diff --git a/test/files/run/t8601.flags b/test/files/run/t8601.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/run/t8601.scala b/test/files/run/t8601.scala new file mode 100644 index 0000000000..e1afc23cc4 --- /dev/null +++ b/test/files/run/t8601.scala @@ -0,0 +1,15 @@ +object Test { + def idiv(x: Int): Unit = x / 0 + def ldiv(x: Long): Unit = x / 0 + def irem(x: Int): Unit = x % 0 + def lrem(x: Long): Unit = x % 0 + + def check(x: => Any) = try { x; sys.error("failed to throw divide by zero!") } catch { case _: ArithmeticException => } + + def main(args: Array[String]) { + check(idiv(1)) + check(ldiv(1L)) + check(irem(1)) + check(lrem(1L)) + } +} diff --git a/test/files/run/t8601b.flags b/test/files/run/t8601b.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601b.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala new file mode 100644 index 0000000000..42b562af96 --- /dev/null +++ b/test/files/run/t8601b.scala @@ -0,0 +1,9 @@ +object Test { + def len(x: Array[String]): Unit = x.length + + def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + + def main(args: Array[String]) { + check(len(null)) + } +} -- cgit v1.2.3 From ccc5eef051c8588c1fe4029e832d5b6387976aa6 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 12:01:17 +0200 Subject: SI-8601 Avoid over-eager optimization of LOAD_FIELD It can NPE or trigger static class initilization, we can't elimiate it without changing semantics. To make sure we don't thwart closure elimination, I've allowed DCE to eliminate a non-static LOAD_FIELD of a member of a closure class. It would be more general to track nullity of the reciever (e.g, `this` or `new Foo` cannot be null), but that would require more infrastructure in this phase. I've added a test for closure inlining based on a a suggestion by @dragos. This actually passes if we remove the (LOAD_FIELD, DROP) peephole optimization for `closelim` altogether. But I chose to adapt that optimization (only allow it for non-static, closure fields), rather then remove it alogether, in the interests of treading lightly. --- .../tools/nsc/backend/opt/ClosureElimination.scala | 7 ++---- .../nsc/backend/opt/DeadCodeElimination.scala | 5 +++++ test/files/run/t8601-closure-elim.flags | 1 + test/files/run/t8601-closure-elim.scala | 26 ++++++++++++++++++++++ test/files/run/t8601c.flags | 1 + test/files/run/t8601c.scala | 12 ++++++++++ 6 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 test/files/run/t8601-closure-elim.flags create mode 100644 test/files/run/t8601-closure-elim.scala create mode 100644 test/files/run/t8601c.flags create mode 100644 test/files/run/t8601c.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index c49f23852f..a866173a88 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -56,11 +56,8 @@ abstract class ClosureElimination extends SubComponent { case (BOX(t1), UNBOX(t2)) if (t1 == t2) => Some(Nil) - case (LOAD_FIELD(sym, isStatic), DROP(_)) if !sym.hasAnnotation(definitions.VolatileAttr) => - if (isStatic) - Some(Nil) - else - Some(DROP(REFERENCE(definitions.ObjectClass)) :: Nil) + case (LOAD_FIELD(sym, /* isStatic */false), DROP(_)) if !sym.hasAnnotation(definitions.VolatileAttr) && inliner.isClosureClass(sym.owner) => + Some(DROP(REFERENCE(definitions.ObjectClass)) :: Nil) case _ => None } diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 3c983e6fdf..18bf63e237 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -172,6 +172,11 @@ abstract class DeadCodeElimination extends SubComponent { LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) => moveToWorkList() + case LOAD_FIELD(sym, isStatic) if isStatic || !inliner.isClosureClass(sym.owner) => + // static load may trigger static initization. + // non-static load can throw NPE (but we know closure fields can't be accessed via a + // null reference. + moveToWorkList() case CALL_METHOD(m1, _) if isSideEffecting(m1) => moveToWorkList() diff --git a/test/files/run/t8601-closure-elim.flags b/test/files/run/t8601-closure-elim.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/run/t8601-closure-elim.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/run/t8601-closure-elim.scala b/test/files/run/t8601-closure-elim.scala new file mode 100644 index 0000000000..2c5b03af77 --- /dev/null +++ b/test/files/run/t8601-closure-elim.scala @@ -0,0 +1,26 @@ +import scala.tools.partest.BytecodeTest +import scala.tools.asm +import scala.tools.asm.util._ +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + val nullChecks = Set(asm.Opcodes.NEW) + + def show: Unit = { + def test(methodName: String) { + val classNode = loadClassNode("Foo") + val methodNode = getMethod(classNode, "b") + val ops = methodNode.instructions.iterator.asScala.map(_.getOpcode).toList + assert(!ops.contains(asm.Opcodes.NEW), ops)// should be allocation free if the closure is eliminiated + } + test("b") + } +} + +class Foo { + @inline final def a(x: Int => Int) = x(1) + final def b { + val delta = 0 + a(x => delta + 1) + } +} diff --git a/test/files/run/t8601c.flags b/test/files/run/t8601c.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601c.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/run/t8601c.scala b/test/files/run/t8601c.scala new file mode 100644 index 0000000000..c487d6825e --- /dev/null +++ b/test/files/run/t8601c.scala @@ -0,0 +1,12 @@ +object Test { + def loadField(x: scala.runtime.IntRef): Unit = x.elem + def storeField(x: scala.runtime.IntRef): Unit = x.elem = 42 + + def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + + def main(args: Array[String]) { + check(loadField(null)) // bug: did not NPE under -Ydead-code + check(storeField(null)) + + } +} -- cgit v1.2.3 From a9a3d7c6493f69a099c4ec3179b9bc144d324413 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 12:11:29 +0200 Subject: Add a test for array load --- test/files/run/t8601b.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'test/files/run') diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala index 42b562af96..c01caa57d0 100644 --- a/test/files/run/t8601b.scala +++ b/test/files/run/t8601b.scala @@ -1,9 +1,11 @@ object Test { def len(x: Array[String]): Unit = x.length + def load(x: Array[String]): Unit = x(0) def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } def main(args: Array[String]) { - check(len(null)) + check(len(null)) // bug: did not NPE + check(load(null)) } } -- cgit v1.2.3 From ae0fe9e507b12dc07f9cac2d6774a7bb270e9998 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 12:17:02 +0200 Subject: SI-8601 Don't treat newarray as dead code Otherwise we lose the side effect of a `NegativeArraySizeException`. --- src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala | 2 +- test/files/run/t8601b.scala | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 18bf63e237..4b419b210c 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -169,7 +169,7 @@ abstract class DeadCodeElimination extends SubComponent { case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | - LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) => + LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) | CREATE_ARRAY(_, _) => moveToWorkList() case LOAD_FIELD(sym, isStatic) if isStatic || !inliner.isClosureClass(sym.owner) => diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala index c01caa57d0..9c37ce33d6 100644 --- a/test/files/run/t8601b.scala +++ b/test/files/run/t8601b.scala @@ -1,11 +1,14 @@ object Test { def len(x: Array[String]): Unit = x.length def load(x: Array[String]): Unit = x(0) + def newarray(i: Int): Unit = new Array[Int](i) def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + def checkNegSize(x: => Any) = try { x; sys.error("failed to throw NegativeArraySizeException!") } catch { case _: NegativeArraySizeException => } def main(args: Array[String]) { check(len(null)) // bug: did not NPE check(load(null)) + checkNegSize(newarray(-1)) } } -- cgit v1.2.3 From 51c60a93009c4470343888e424f58229ecf52ec6 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 May 2014 12:21:45 +0200 Subject: SI-8601 Test that `null.synchronized` NPEs under -optimize As part of my sweep through the side-effecting byte code instructions. --- test/files/run/t8601d.flags | 1 + test/files/run/t8601d.scala | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 test/files/run/t8601d.flags create mode 100644 test/files/run/t8601d.scala (limited to 'test/files/run') diff --git a/test/files/run/t8601d.flags b/test/files/run/t8601d.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601d.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/run/t8601d.scala b/test/files/run/t8601d.scala new file mode 100644 index 0000000000..ac89963d67 --- /dev/null +++ b/test/files/run/t8601d.scala @@ -0,0 +1,8 @@ +object Test { + def monitor(x: AnyRef): Unit = {x.synchronized(()); ()} + def check(x: => Any) = try { x; sys.error("failed to throw NPE") } catch { case _: NullPointerException => } + + def main(args: Array[String]) { + check(monitor(null)) + } +} -- cgit v1.2.3 From 7892bbf38fb77e7d2f1361515f14280b3a513c96 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 20 May 2014 12:31:00 +0200 Subject: SI-8601 Test that static LOAD_FIELD is not eliminated This test fails under 2.11.0, and works now that DCE treats static loads as useful instructions. --- test/files/run/t8601e.flags | 1 + test/files/run/t8601e/StaticInit.class | Bin 0 -> 417 bytes test/files/run/t8601e/StaticInit.java | 8 ++++++++ test/files/run/t8601e/Test.scala | 12 ++++++++++++ 4 files changed, 21 insertions(+) create mode 100644 test/files/run/t8601e.flags create mode 100644 test/files/run/t8601e/StaticInit.class create mode 100644 test/files/run/t8601e/StaticInit.java create mode 100644 test/files/run/t8601e/Test.scala (limited to 'test/files/run') diff --git a/test/files/run/t8601e.flags b/test/files/run/t8601e.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/run/t8601e.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/run/t8601e/StaticInit.class b/test/files/run/t8601e/StaticInit.class new file mode 100644 index 0000000000..99a0e2a643 Binary files /dev/null and b/test/files/run/t8601e/StaticInit.class differ diff --git a/test/files/run/t8601e/StaticInit.java b/test/files/run/t8601e/StaticInit.java new file mode 100644 index 0000000000..7543ed98b8 --- /dev/null +++ b/test/files/run/t8601e/StaticInit.java @@ -0,0 +1,8 @@ +public class StaticInit { + static { + if ("".isEmpty()) { + throw new RuntimeException(); + } + } + public static int fld = 42; +} diff --git a/test/files/run/t8601e/Test.scala b/test/files/run/t8601e/Test.scala new file mode 100644 index 0000000000..838114f6a7 --- /dev/null +++ b/test/files/run/t8601e/Test.scala @@ -0,0 +1,12 @@ +class C { + def foo: Unit = {StaticInit.fld} +} + +object Test extends App { + try { + new C().foo + sys.error("StaticInit. was not run!") + } catch { + case t: ExceptionInInitializerError => + } +} -- cgit v1.2.3 From 2cd8f4535a82ea0df919c06fd1ea73ee02f9bad9 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 14 May 2014 09:03:05 +0200 Subject: Upgrade ASM to 5.0.2 This commit is a squashed version of all commits in PR #3747. For future upgrades, consult the README and check the commits in https://github.com/scala/scala/pull/3747/commits There's one bug in ASM 5.0.2 that breaks scalac: http://forge.ow2.org/tracker/?func=detail&aid=317200&group_id=23&atid=100023 This bug is fixed in ASM trunk, the patch has been merged into this commit. A future upgrade of ASM should contain the fix. --- src/asm/README | 30 ++ src/asm/scala/tools/asm/AnnotationVisitor.java | 8 +- src/asm/scala/tools/asm/AnnotationWriter.java | 55 ++- src/asm/scala/tools/asm/ByteVector.java | 99 +++-- src/asm/scala/tools/asm/ClassReader.java | 366 +++++++++++++++-- src/asm/scala/tools/asm/ClassVisitor.java | 46 ++- src/asm/scala/tools/asm/ClassWriter.java | 124 +++++- src/asm/scala/tools/asm/Context.java | 35 ++ src/asm/scala/tools/asm/CustomAttr.java | 2 +- src/asm/scala/tools/asm/FieldVisitor.java | 41 +- src/asm/scala/tools/asm/FieldWriter.java | 58 ++- src/asm/scala/tools/asm/Frame.java | 23 +- src/asm/scala/tools/asm/Handle.java | 13 +- src/asm/scala/tools/asm/Item.java | 3 +- src/asm/scala/tools/asm/MethodVisitor.java | 254 +++++++++++- src/asm/scala/tools/asm/MethodWriter.java | 338 ++++++++++++--- src/asm/scala/tools/asm/Opcodes.java | 7 +- src/asm/scala/tools/asm/Type.java | 11 +- src/asm/scala/tools/asm/TypePath.java | 193 +++++++++ src/asm/scala/tools/asm/TypeReference.java | 452 +++++++++++++++++++++ .../tools/asm/signature/SignatureVisitor.java | 7 +- .../scala/tools/asm/signature/SignatureWriter.java | 2 +- src/asm/scala/tools/asm/tree/AbstractInsnNode.java | 78 ++++ src/asm/scala/tools/asm/tree/AnnotationNode.java | 14 +- src/asm/scala/tools/asm/tree/ClassNode.java | 80 +++- src/asm/scala/tools/asm/tree/FieldInsnNode.java | 8 +- src/asm/scala/tools/asm/tree/FieldNode.java | 76 +++- src/asm/scala/tools/asm/tree/IincInsnNode.java | 3 +- src/asm/scala/tools/asm/tree/InsnList.java | 20 +- src/asm/scala/tools/asm/tree/InsnNode.java | 3 +- src/asm/scala/tools/asm/tree/IntInsnNode.java | 3 +- .../tools/asm/tree/InvokeDynamicInsnNode.java | 4 +- src/asm/scala/tools/asm/tree/JumpInsnNode.java | 4 +- src/asm/scala/tools/asm/tree/LdcInsnNode.java | 3 +- .../asm/tree/LocalVariableAnnotationNode.java | 157 +++++++ .../scala/tools/asm/tree/LookupSwitchInsnNode.java | 3 +- src/asm/scala/tools/asm/tree/MethodInsnNode.java | 35 +- src/asm/scala/tools/asm/tree/MethodNode.java | 260 +++++++++++- .../tools/asm/tree/MultiANewArrayInsnNode.java | 3 +- src/asm/scala/tools/asm/tree/ParameterNode.java | 76 ++++ .../scala/tools/asm/tree/TableSwitchInsnNode.java | 3 +- .../scala/tools/asm/tree/TryCatchBlockNode.java | 59 +++ .../scala/tools/asm/tree/TypeAnnotationNode.java | 100 +++++ src/asm/scala/tools/asm/tree/TypeInsnNode.java | 3 +- src/asm/scala/tools/asm/tree/VarInsnNode.java | 3 +- .../tools/asm/tree/analysis/AnalyzerException.java | 1 + .../tools/asm/tree/analysis/BasicInterpreter.java | 2 +- .../tools/asm/tree/analysis/BasicVerifier.java | 2 +- src/asm/scala/tools/asm/tree/analysis/Frame.java | 9 + .../tools/asm/tree/analysis/SimpleVerifier.java | 2 +- .../tools/asm/tree/analysis/SourceInterpreter.java | 2 +- src/asm/scala/tools/asm/util/ASMifier.java | 141 ++++++- .../tools/asm/util/CheckAnnotationAdapter.java | 4 +- .../scala/tools/asm/util/CheckClassAdapter.java | 119 +++++- .../scala/tools/asm/util/CheckFieldAdapter.java | 26 +- .../scala/tools/asm/util/CheckMethodAdapter.java | 159 +++++++- .../tools/asm/util/CheckSignatureAdapter.java | 4 +- src/asm/scala/tools/asm/util/Printer.java | 95 ++++- src/asm/scala/tools/asm/util/Textifier.java | 279 ++++++++++++- .../tools/asm/util/TraceAnnotationVisitor.java | 2 +- .../scala/tools/asm/util/TraceClassVisitor.java | 13 +- .../scala/tools/asm/util/TraceFieldVisitor.java | 13 +- .../scala/tools/asm/util/TraceMethodVisitor.java | 77 +++- .../tools/asm/util/TraceSignatureVisitor.java | 4 +- .../scala/tools/nsc/backend/jvm/BCodeHelpers.scala | 7 +- .../tools/nsc/backend/jvm/BCodeIdiomatic.scala | 8 +- .../tools/nsc/backend/jvm/BCodeSkelBuilder.scala | 6 +- .../scala/tools/nsc/backend/jvm/GenASM.scala | 17 +- .../tools/partest/javaagent/ProfilerVisitor.java | 2 +- test/files/run/classfile-format-51.scala | 14 +- test/files/run/large_class.check | 3 + test/files/run/large_class.scala | 27 ++ 72 files changed, 3883 insertions(+), 320 deletions(-) create mode 100644 src/asm/README create mode 100644 src/asm/scala/tools/asm/TypePath.java create mode 100644 src/asm/scala/tools/asm/TypeReference.java create mode 100644 src/asm/scala/tools/asm/tree/LocalVariableAnnotationNode.java create mode 100644 src/asm/scala/tools/asm/tree/ParameterNode.java create mode 100644 src/asm/scala/tools/asm/tree/TypeAnnotationNode.java create mode 100644 test/files/run/large_class.check create mode 100644 test/files/run/large_class.scala (limited to 'test/files/run') diff --git a/src/asm/README b/src/asm/README new file mode 100644 index 0000000000..3ceac88098 --- /dev/null +++ b/src/asm/README @@ -0,0 +1,30 @@ +Version 5.0.2, SVN r1741, tags/ASM_5_0_2 + +Git SVN repo: https://github.com/lrytz/asm + - git svn howto: https://github.com/lrytz/asm/issues/1 + +Upgrading ASM +------------- + +Start by deleting all source files in src/asm/ and copy the ones from the latest ASM release. + +Excluded Files (don't copy): + - package.html files + - org/objectweb/asm/commons + - org/objectweb/asm/optimizer + - org/objectweb/asm/xml + +Re-packaging and cosmetic changes: + - convert line endings (there are some CRLF) + find src/asm/scala/tools/asm -name '*.java' | xargs dos2unix + - change package clauses + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/package org\.objectweb\.asm/package scala.tools.asm/' + - update imports + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/import org\.objectweb\.asm/import scala.tools.asm/' + - update @links, @associates + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/@link org\.objectweb\.asm/@link scala.tools.asm/' + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/@associates org\.objectweb\.asm/@associates scala.tools.asm/' + - remove trailing whitespace + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/[ ]*$//' + +Actual changes: check the git log for [asm-cherry-pick] after the previous upgrade. diff --git a/src/asm/scala/tools/asm/AnnotationVisitor.java b/src/asm/scala/tools/asm/AnnotationVisitor.java index c806ca71e8..abcaf1d6d1 100644 --- a/src/asm/scala/tools/asm/AnnotationVisitor.java +++ b/src/asm/scala/tools/asm/AnnotationVisitor.java @@ -41,7 +41,7 @@ public abstract class AnnotationVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -56,7 +56,7 @@ public abstract class AnnotationVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public AnnotationVisitor(final int api) { this(api, null); @@ -67,13 +67,13 @@ public abstract class AnnotationVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param av * the annotation visitor to which this visitor must delegate * method calls. May be null. */ public AnnotationVisitor(final int api, final AnnotationVisitor av) { - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } this.api = api; diff --git a/src/asm/scala/tools/asm/AnnotationWriter.java b/src/asm/scala/tools/asm/AnnotationWriter.java index 8eb5b2ef48..6de74ce041 100644 --- a/src/asm/scala/tools/asm/AnnotationWriter.java +++ b/src/asm/scala/tools/asm/AnnotationWriter.java @@ -104,7 +104,7 @@ final class AnnotationWriter extends AnnotationVisitor { */ AnnotationWriter(final ClassWriter cw, final boolean named, final ByteVector bv, final ByteVector parent, final int offset) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); this.cw = cw; this.named = named; this.bv = bv; @@ -315,4 +315,57 @@ final class AnnotationWriter extends AnnotationVisitor { } } } + + /** + * Puts the given type reference and type path into the given bytevector. + * LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported. + * + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param out + * where the type reference and type path must be put. + */ + static void putTarget(int typeRef, TypePath typePath, ByteVector out) { + switch (typeRef >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + out.putShort(typeRef >>> 16); + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + out.putByte(typeRef >>> 24); + break; + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + out.putInt(typeRef); + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8); + break; + } + if (typePath == null) { + out.putByte(0); + } else { + int length = typePath.b[typePath.offset] * 2 + 1; + out.putByteArray(typePath.b, typePath.offset, length); + } + } } diff --git a/src/asm/scala/tools/asm/ByteVector.java b/src/asm/scala/tools/asm/ByteVector.java index 2bc63eb384..3bca7af12a 100644 --- a/src/asm/scala/tools/asm/ByteVector.java +++ b/src/asm/scala/tools/asm/ByteVector.java @@ -204,11 +204,14 @@ public class ByteVector { * automatically enlarged if necessary. * * @param s - * a String. + * a String whose UTF8 encoded length must be less than 65536. * @return this byte vector. */ public ByteVector putUTF8(final String s) { int charLength = s.length(); + if (charLength > 65535) { + throw new IllegalArgumentException(); + } int len = length; if (len + 2 + charLength > data.length) { enlarge(2 + charLength); @@ -227,38 +230,68 @@ public class ByteVector { if (c >= '\001' && c <= '\177') { data[len++] = (byte) c; } else { - int byteLength = i; - for (int j = i; j < charLength; ++j) { - c = s.charAt(j); - if (c >= '\001' && c <= '\177') { - byteLength++; - } else if (c > '\u07FF') { - byteLength += 3; - } else { - byteLength += 2; - } - } - data[length] = (byte) (byteLength >>> 8); - data[length + 1] = (byte) byteLength; - if (length + 2 + byteLength > data.length) { - length = len; - enlarge(2 + byteLength); - data = this.data; - } - for (int j = i; j < charLength; ++j) { - c = s.charAt(j); - if (c >= '\001' && c <= '\177') { - data[len++] = (byte) c; - } else if (c > '\u07FF') { - data[len++] = (byte) (0xE0 | c >> 12 & 0xF); - data[len++] = (byte) (0x80 | c >> 6 & 0x3F); - data[len++] = (byte) (0x80 | c & 0x3F); - } else { - data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); - data[len++] = (byte) (0x80 | c & 0x3F); - } - } - break; + length = len; + return encodeUTF8(s, i, 65535); + } + } + length = len; + return this; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is + * automatically enlarged if necessary. The string length is encoded in two + * bytes before the encoded characters, if there is space for that (i.e. if + * this.length - i - 2 >= 0). + * + * @param s + * the String to encode. + * @param i + * the index of the first character to encode. The previous + * characters are supposed to have already been encoded, using + * only one byte per character. + * @param maxByteLength + * the maximum byte length of the encoded string, including the + * already encoded characters. + * @return this byte vector. + */ + ByteVector encodeUTF8(final String s, int i, int maxByteLength) { + int charLength = s.length(); + int byteLength = i; + char c; + for (int j = i; j < charLength; ++j) { + c = s.charAt(j); + if (c >= '\001' && c <= '\177') { + byteLength++; + } else if (c > '\u07FF') { + byteLength += 3; + } else { + byteLength += 2; + } + } + if (byteLength > maxByteLength) { + throw new IllegalArgumentException(); + } + int start = length - i - 2; + if (start >= 0) { + data[start] = (byte) (byteLength >>> 8); + data[start + 1] = (byte) byteLength; + } + if (length + byteLength - i > data.length) { + enlarge(byteLength - i); + } + int len = length; + for (int j = i; j < charLength; ++j) { + c = s.charAt(j); + if (c >= '\001' && c <= '\177') { + data[len++] = (byte) c; + } else if (c > '\u07FF') { + data[len++] = (byte) (0xE0 | c >> 12 & 0xF); + data[len++] = (byte) (0x80 | c >> 6 & 0x3F); + data[len++] = (byte) (0x80 | c & 0x3F); + } else { + data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); + data[len++] = (byte) (0x80 | c & 0x3F); } } length = len; diff --git a/src/asm/scala/tools/asm/ClassReader.java b/src/asm/scala/tools/asm/ClassReader.java index cc655c1b62..8b0e12cb04 100644 --- a/src/asm/scala/tools/asm/ClassReader.java +++ b/src/asm/scala/tools/asm/ClassReader.java @@ -166,7 +166,7 @@ public class ClassReader { public ClassReader(final byte[] b, final int off, final int len) { this.b = b; // checks the class version - if (readShort(off + 6) > Opcodes.V1_7) { + if (readShort(off + 6) > Opcodes.V1_8) { throw new IllegalArgumentException(); } // parses the constant pool @@ -557,6 +557,8 @@ public class ClassReader { String enclosingDesc = null; int anns = 0; int ianns = 0; + int tanns = 0; + int itanns = 0; int innerClasses = 0; Attribute attributes = null; @@ -581,6 +583,9 @@ public class ClassReader { } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; } else if ("Deprecated".equals(attrName)) { access |= Opcodes.ACC_DEPRECATED; } else if ("Synthetic".equals(attrName)) { @@ -592,6 +597,9 @@ public class ClassReader { } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; } else if ("BootstrapMethods".equals(attrName)) { int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { @@ -626,7 +634,7 @@ public class ClassReader { enclosingDesc); } - // visits the class annotations + // visits the class annotations and type annotations if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, @@ -639,6 +647,22 @@ public class ClassReader { classVisitor.visitAnnotation(readUTF8(v, c), false)); } } + if (ANNOTATIONS && tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + classVisitor.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (ANNOTATIONS && itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + classVisitor.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } // visits the attributes while (attributes != null) { @@ -697,6 +721,8 @@ public class ClassReader { String signature = null; int anns = 0; int ianns = 0; + int tanns = 0; + int itanns = 0; Object value = null; Attribute attributes = null; @@ -717,9 +743,15 @@ public class ClassReader { } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; } else { Attribute attr = readAttribute(context.attrs, attrName, u + 8, readInt(u + 4), c, -1, null); @@ -739,7 +771,7 @@ public class ClassReader { return u; } - // visits the field annotations + // visits the field annotations and type annotations if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, @@ -752,6 +784,22 @@ public class ClassReader { fv.visitAnnotation(readUTF8(v, c), false)); } } + if (ANNOTATIONS && tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + fv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (ANNOTATIONS && itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + fv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } // visits the field attributes while (attributes != null) { @@ -782,9 +830,9 @@ public class ClassReader { final Context context, int u) { // reads the method declaration char[] c = context.buffer; - int access = readUnsignedShort(u); - String name = readUTF8(u + 2, c); - String desc = readUTF8(u + 4, c); + context.access = readUnsignedShort(u); + context.name = readUTF8(u + 2, c); + context.desc = readUTF8(u + 4, c); u += 6; // reads the method attributes @@ -792,8 +840,11 @@ public class ClassReader { int exception = 0; String[] exceptions = null; String signature = null; + int methodParameters = 0; int anns = 0; int ianns = 0; + int tanns = 0; + int itanns = 0; int dann = 0; int mpanns = 0; int impanns = 0; @@ -818,24 +869,32 @@ public class ClassReader { } else if (SIGNATURES && "Signature".equals(attrName)) { signature = readUTF8(u + 8, c); } else if ("Deprecated".equals(attrName)) { - access |= Opcodes.ACC_DEPRECATED; + context.access |= Opcodes.ACC_DEPRECATED; } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { dann = u + 8; } else if ("Synthetic".equals(attrName)) { - access |= Opcodes.ACC_SYNTHETIC + context.access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleParameterAnnotations".equals(attrName)) { mpanns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleParameterAnnotations".equals(attrName)) { impanns = u + 8; + } else if ("MethodParameters".equals(attrName)) { + methodParameters = u + 8; } else { Attribute attr = readAttribute(context.attrs, attrName, u + 8, readInt(u + 4), c, -1, null); @@ -849,8 +908,8 @@ public class ClassReader { u += 2; // visits the method declaration - MethodVisitor mv = classVisitor.visitMethod(access, name, desc, - signature, exceptions); + MethodVisitor mv = classVisitor.visitMethod(context.access, + context.name, context.desc, signature, exceptions); if (mv == null) { return u; } @@ -894,6 +953,13 @@ public class ClassReader { } } + // visit the method parameters + if (methodParameters != 0) { + for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) { + mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2)); + } + } + // visits the method annotations if (ANNOTATIONS && dann != 0) { AnnotationVisitor dv = mv.visitAnnotationDefault(); @@ -914,11 +980,27 @@ public class ClassReader { mv.visitAnnotation(readUTF8(v, c), false)); } } + if (ANNOTATIONS && tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + mv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (ANNOTATIONS && itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + mv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } if (ANNOTATIONS && mpanns != 0) { - readParameterAnnotations(mpanns, desc, c, true, mv); + readParameterAnnotations(mv, context, mpanns, true); } if (ANNOTATIONS && impanns != 0) { - readParameterAnnotations(impanns, desc, c, false, mv); + readParameterAnnotations(mv, context, impanns, false); } // visits the method attributes @@ -931,9 +1013,6 @@ public class ClassReader { // visits the method code if (code != 0) { - context.access = access; - context.name = name; - context.desc = desc; mv.visitCode(); readCode(mv, context, code); } @@ -966,7 +1045,7 @@ public class ClassReader { // reads the bytecode to find the labels int codeStart = u; int codeEnd = u + codeLength; - Label[] labels = new Label[codeLength + 2]; + Label[] labels = context.labels = new Label[codeLength + 2]; readLabel(codeLength + 1, labels); while (u < codeEnd) { int offset = u - codeStart; @@ -1049,6 +1128,12 @@ public class ClassReader { u += 2; // reads the code attributes + int[] tanns = null; // start index of each visible type annotation + int[] itanns = null; // start index of each invisible type annotation + int tann = 0; // current index in tanns array + int itann = 0; // current index in itanns array + int ntoff = -1; // next visible type annotation code offset + int nitoff = -1; // next invisible type annotation code offset int varTable = 0; int varTypeTable = 0; boolean zip = true; @@ -1089,6 +1174,16 @@ public class ClassReader { v += 4; } } + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = readTypeAnnotations(mv, context, u + 8, true); + ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1 + : readUnsignedShort(tanns[0] + 1); + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = readTypeAnnotations(mv, context, u + 8, false); + nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1 + : readUnsignedShort(itanns[0] + 1); } else if (FRAMES && "StackMapTable".equals(attrName)) { if ((context.flags & SKIP_FRAMES) == 0) { stackMap = u + 10; @@ -1211,7 +1306,7 @@ public class ClassReader { } } if (frameCount > 0) { - stackMap = readFrame(stackMap, zip, unzip, labels, frame); + stackMap = readFrame(stackMap, zip, unzip, frame); --frameCount; } else { frame = null; @@ -1310,6 +1405,7 @@ public class ClassReader { case ClassWriter.FIELDORMETH_INSN: case ClassWriter.ITFMETH_INSN: { int cpIndex = items[readUnsignedShort(u + 1)]; + boolean itf = b[cpIndex - 1] == ClassWriter.IMETH; String iowner = readClass(cpIndex, c); cpIndex = items[readUnsignedShort(cpIndex + 2)]; String iname = readUTF8(cpIndex, c); @@ -1317,7 +1413,7 @@ public class ClassReader { if (opcode < Opcodes.INVOKEVIRTUAL) { mv.visitFieldInsn(opcode, iowner, iname, idesc); } else { - mv.visitMethodInsn(opcode, iowner, iname, idesc); + mv.visitMethodInsn(opcode, iowner, iname, idesc, itf); } if (opcode == Opcodes.INVOKEINTERFACE) { u += 5; @@ -1358,6 +1454,29 @@ public class ClassReader { u += 4; break; } + + // visit the instruction annotations, if any + while (tanns != null && tann < tanns.length && ntoff <= offset) { + if (ntoff == offset) { + int v = readAnnotationTarget(context, tanns[tann]); + readAnnotationValues(v + 2, c, true, + mv.visitInsnAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1 + : readUnsignedShort(tanns[tann] + 1); + } + while (itanns != null && itann < itanns.length && nitoff <= offset) { + if (nitoff == offset) { + int v = readAnnotationTarget(context, itanns[itann]); + readAnnotationValues(v + 2, c, true, + mv.visitInsnAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + nitoff = ++itann >= itanns.length + || readByte(itanns[itann]) < 0x43 ? -1 + : readUnsignedShort(itanns[itann] + 1); + } } if (labels[codeLength] != null) { mv.visitLabel(labels[codeLength]); @@ -1397,6 +1516,32 @@ public class ClassReader { } } + // visits the local variables type annotations + if (tanns != null) { + for (int i = 0; i < tanns.length; ++i) { + if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) { + int v = readAnnotationTarget(context, tanns[i]); + v = readAnnotationValues(v + 2, c, true, + mv.visitLocalVariableAnnotation(context.typeRef, + context.typePath, context.start, + context.end, context.index, readUTF8(v, c), + true)); + } + } + } + if (itanns != null) { + for (int i = 0; i < itanns.length; ++i) { + if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) { + int v = readAnnotationTarget(context, itanns[i]); + v = readAnnotationValues(v + 2, c, true, + mv.visitLocalVariableAnnotation(context.typeRef, + context.typePath, context.start, + context.end, context.index, readUTF8(v, c), + false)); + } + } + } + // visits the code attributes while (attributes != null) { Attribute attr = attributes.next; @@ -1409,25 +1554,176 @@ public class ClassReader { mv.visitMaxs(maxStack, maxLocals); } + /** + * Parses a type annotation table to find the labels, and to visit the try + * catch block annotations. + * + * @param u + * the start offset of a type annotation table. + * @param mv + * the method visitor to be used to visit the try catch block + * annotations. + * @param context + * information about the class being parsed. + * @param visible + * if the type annotation table to parse contains runtime visible + * annotations. + * @return the start offset of each type annotation in the parsed table. + */ + private int[] readTypeAnnotations(final MethodVisitor mv, + final Context context, int u, boolean visible) { + char[] c = context.buffer; + int[] offsets = new int[readUnsignedShort(u)]; + u += 2; + for (int i = 0; i < offsets.length; ++i) { + offsets[i] = u; + int target = readInt(u); + switch (target >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + u += 2; + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + u += 1; + break; + case 0x40: // LOCAL_VARIABLE + case 0x41: // RESOURCE_VARIABLE + for (int j = readUnsignedShort(u + 1); j > 0; --j) { + int start = readUnsignedShort(u + 3); + int length = readUnsignedShort(u + 5); + readLabel(start, context.labels); + readLabel(start + length, context.labels); + u += 6; + } + u += 3; + break; + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + u += 4; + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + u += 3; + break; + } + int pathLength = readByte(u); + if ((target >>> 24) == 0x42) { + TypePath path = pathLength == 0 ? null : new TypePath(b, u); + u += 1 + 2 * pathLength; + u = readAnnotationValues(u + 2, c, true, + mv.visitTryCatchAnnotation(target, path, + readUTF8(u, c), visible)); + } else { + u = readAnnotationValues(u + 3 + 2 * pathLength, c, true, null); + } + } + return offsets; + } + + /** + * Parses the header of a type annotation to extract its target_type and + * target_path (the result is stored in the given context), and returns the + * start offset of the rest of the type_annotation structure (i.e. the + * offset to the type_index field, which is followed by + * num_element_value_pairs and then the name,value pairs). + * + * @param context + * information about the class being parsed. This is where the + * extracted target_type and target_path must be stored. + * @param u + * the start offset of a type_annotation structure. + * @return the start offset of the rest of the type_annotation structure. + */ + private int readAnnotationTarget(final Context context, int u) { + int target = readInt(u); + switch (target >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + target &= 0xFFFF0000; + u += 2; + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + target &= 0xFF000000; + u += 1; + break; + case 0x40: // LOCAL_VARIABLE + case 0x41: { // RESOURCE_VARIABLE + target &= 0xFF000000; + int n = readUnsignedShort(u + 1); + context.start = new Label[n]; + context.end = new Label[n]; + context.index = new int[n]; + u += 3; + for (int i = 0; i < n; ++i) { + int start = readUnsignedShort(u); + int length = readUnsignedShort(u + 2); + context.start[i] = readLabel(start, context.labels); + context.end[i] = readLabel(start + length, context.labels); + context.index[i] = readUnsignedShort(u + 4); + u += 6; + } + break; + } + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + target &= 0xFF0000FF; + u += 4; + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + target &= (target >>> 24) < 0x43 ? 0xFFFFFF00 : 0xFF000000; + u += 3; + break; + } + int pathLength = readByte(u); + context.typeRef = target; + context.typePath = pathLength == 0 ? null : new TypePath(b, u); + return u + 1 + 2 * pathLength; + } + /** * Reads parameter annotations and makes the given visitor visit them. * + * @param mv + * the visitor that must visit the annotations. + * @param context + * information about the class being parsed. * @param v * start offset in {@link #b b} of the annotations to be read. - * @param desc - * the method descriptor. - * @param buf - * buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or {@link #readConst - * readConst}. * @param visible * true if the annotations to be read are visible at * runtime. - * @param mv - * the visitor that must visit the annotations. */ - private void readParameterAnnotations(int v, final String desc, - final char[] buf, final boolean visible, final MethodVisitor mv) { + private void readParameterAnnotations(final MethodVisitor mv, + final Context context, int v, final boolean visible) { int i; int n = b[v++] & 0xFF; // workaround for a bug in javac (javac compiler generates a parameter @@ -1436,7 +1732,7 @@ public class ClassReader { // equal to the number of parameters in the method descriptor - which // includes the synthetic parameters added by the compiler). This work- // around supposes that the synthetic parameters are the first ones. - int synthetics = Type.getArgumentTypes(desc).length - n; + int synthetics = Type.getArgumentTypes(context.desc).length - n; AnnotationVisitor av; for (i = 0; i < synthetics; ++i) { // virtual annotation to detect synthetic parameters in MethodWriter @@ -1445,12 +1741,13 @@ public class ClassReader { av.visitEnd(); } } + char[] c = context.buffer; for (; i < n + synthetics; ++i) { int j = readUnsignedShort(v); v += 2; for (; j > 0; --j) { - av = mv.visitParameterAnnotation(i, readUTF8(v, buf), visible); - v = readAnnotationValues(v + 2, buf, true, av); + av = mv.visitParameterAnnotation(i, readUTF8(v, c), visible); + v = readAnnotationValues(v + 2, c, true, av); } } } @@ -1729,17 +2026,14 @@ public class ClassReader { * if the stack map frame at stackMap is compressed or not. * @param unzip * if the stack map frame must be uncompressed. - * @param labels - * the labels of the method currently being parsed, indexed by - * their offset. A new label for the parsed stack map frame is - * stored in this array if it does not already exist. * @param frame * where the parsed stack map frame must be stored. * @return the offset of the first byte following the parsed frame. */ private int readFrame(int stackMap, boolean zip, boolean unzip, - Label[] labels, Context frame) { + Context frame) { char[] c = frame.buffer; + Label[] labels = frame.labels; int tag; int delta; if (zip) { diff --git a/src/asm/scala/tools/asm/ClassVisitor.java b/src/asm/scala/tools/asm/ClassVisitor.java index 3fc364d5e5..48dc2ca6ae 100644 --- a/src/asm/scala/tools/asm/ClassVisitor.java +++ b/src/asm/scala/tools/asm/ClassVisitor.java @@ -33,8 +33,9 @@ package scala.tools.asm; * A visitor to visit a Java class. The methods of this class must be called in * the following order: visit [ visitSource ] [ * visitOuterClass ] ( visitAnnotation | - * visitAttribute )* ( visitInnerClass | visitField | - * visitMethod )* visitEnd. + * visitTypeAnnotation | visitAttribute )* ( + * visitInnerClass | visitField | visitMethod )* + * visitEnd. * * @author Eric Bruneton */ @@ -42,7 +43,7 @@ public abstract class ClassVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -57,7 +58,7 @@ public abstract class ClassVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public ClassVisitor(final int api) { this(api, null); @@ -68,13 +69,13 @@ public abstract class ClassVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param cv * the class visitor to which this visitor must delegate method * calls. May be null. */ public ClassVisitor(final int api, final ClassVisitor cv) { - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } this.api = api; @@ -168,6 +169,39 @@ public abstract class ClassVisitor { return null; } + /** + * Visits an annotation on a type in the class signature. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#CLASS_TYPE_PARAMETER + * CLASS_TYPE_PARAMETER}, + * {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND + * CLASS_TYPE_PARAMETER_BOUND} or + * {@link TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. See + * {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (cv != null) { + return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + /** * Visits a non standard attribute of the class. * diff --git a/src/asm/scala/tools/asm/ClassWriter.java b/src/asm/scala/tools/asm/ClassWriter.java index 93ed7313c7..5c2de3f982 100644 --- a/src/asm/scala/tools/asm/ClassWriter.java +++ b/src/asm/scala/tools/asm/ClassWriter.java @@ -416,6 +416,16 @@ public class ClassWriter extends ClassVisitor { */ private AnnotationWriter ianns; + /** + * The runtime visible type annotations of this class. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this class. + */ + private AnnotationWriter itanns; + /** * The non standard attributes of this class. */ @@ -477,12 +487,12 @@ public class ClassWriter extends ClassVisitor { * true if the maximum stack size and number of local variables * must be automatically computed. */ - private final boolean computeMaxs; + private boolean computeMaxs; /** * true if the stack map frames must be recomputed from scratch. */ - private final boolean computeFrames; + private boolean computeFrames; /** * true if the stack map tables of this class are invalid. The @@ -595,7 +605,7 @@ public class ClassWriter extends ClassVisitor { * {@link #COMPUTE_FRAMES}. */ public ClassWriter(final int flags) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); index = 1; pool = new ByteVector(); items = new Item[256]; @@ -677,7 +687,8 @@ public class ClassWriter extends ClassVisitor { sourceFile = newUTF8(file); } if (debug != null) { - sourceDebug = new ByteVector().putUTF8(debug); + sourceDebug = new ByteVector().encodeUTF8(debug, 0, + Integer.MAX_VALUE); } } @@ -710,6 +721,29 @@ public class ClassWriter extends ClassVisitor { return aw; } + @Override + public final AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + @Override public final void visitAttribute(final Attribute attr) { attr.next = attrs; @@ -722,11 +756,29 @@ public class ClassWriter extends ClassVisitor { if (innerClasses == null) { innerClasses = new ByteVector(); } - ++innerClassesCount; - innerClasses.putShort(name == null ? 0 : newClass(name)); - innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); - innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); - innerClasses.putShort(access); + // Sec. 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the + // constant_pool table which represents a class or interface C that is + // not a package member must have exactly one corresponding entry in the + // classes array". To avoid duplicates we keep track in the intVal field + // of the Item of each CONSTANT_Class_info entry C whether an inner + // class entry has already been added for C (this field is unused for + // class entries, and changing its value does not change the hashcode + // and equality tests). If so we store the index of this inner class + // entry (plus one) in intVal. This hack allows duplicate detection in + // O(1) time. + Item nameItem = newClassItem(name); + if (nameItem.intVal == 0) { + ++innerClassesCount; + innerClasses.putShort(nameItem.index); + innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); + innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); + innerClasses.putShort(access); + nameItem.intVal = innerClassesCount; + } else { + // Compare the inner classes entry nameItem.intVal - 1 with the + // arguments of this method and throw an exception if there is a + // difference? + } } @Override @@ -795,7 +847,7 @@ public class ClassWriter extends ClassVisitor { } if (sourceDebug != null) { ++attributeCount; - size += sourceDebug.length + 4; + size += sourceDebug.length + 6; newUTF8("SourceDebugExtension"); } if (enclosingMethodOwner != 0) { @@ -831,6 +883,16 @@ public class ClassWriter extends ClassVisitor { size += 8 + ianns.getSize(); newUTF8("RuntimeInvisibleAnnotations"); } + if (ClassReader.ANNOTATIONS && tanns != null) { + ++attributeCount; + size += 8 + tanns.getSize(); + newUTF8("RuntimeVisibleTypeAnnotations"); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + ++attributeCount; + size += 8 + itanns.getSize(); + newUTF8("RuntimeInvisibleTypeAnnotations"); + } if (attrs != null) { attributeCount += attrs.getCount(); size += attrs.getSize(this, null, 0, -1, -1); @@ -874,9 +936,9 @@ public class ClassWriter extends ClassVisitor { out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); } if (sourceDebug != null) { - int len = sourceDebug.length - 2; + int len = sourceDebug.length; out.putShort(newUTF8("SourceDebugExtension")).putInt(len); - out.putByteArray(sourceDebug.data, 2, len); + out.putByteArray(sourceDebug.data, 0, len); } if (enclosingMethodOwner != 0) { out.putShort(newUTF8("EnclosingMethod")).putInt(4); @@ -904,13 +966,34 @@ public class ClassWriter extends ClassVisitor { out.putShort(newUTF8("RuntimeInvisibleAnnotations")); ianns.put(out); } + if (ClassReader.ANNOTATIONS && tanns != null) { + out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } if (attrs != null) { attrs.put(this, null, 0, -1, -1, out); } if (invalidFrames) { - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES); - return cw.toByteArray(); + anns = null; + ianns = null; + attrs = null; + innerClassesCount = 0; + innerClasses = null; + bootstrapMethodsCount = 0; + bootstrapMethods = null; + firstField = null; + lastField = null; + firstMethod = null; + lastMethod = null; + computeMaxs = false; + computeFrames = true; + invalidFrames = false; + new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES); + return toByteArray(); } return out.data; } @@ -1577,7 +1660,7 @@ public class ClassWriter extends ClassVisitor { /** * Returns the common super type of the two given types. The default - * implementation of this method loads the two given classes and uses + * implementation of this method loads the two given classes and uses * the java.lang.Class methods to find the common super class. It can be * overridden to compute this common super type in other ways, in particular * without actually loading any class, or to take into account the class @@ -1663,6 +1746,15 @@ public class ClassWriter extends ClassVisitor { items[index] = i; } + /** + * Find item that whose index is `index`. + */ + public Item findItemByIndex(int index) { + int i = 0; + while (i < items.length && (items[i] == null || items[i].index != index)) i++; + return items[i]; + } + /** * Puts one byte and two shorts into the constant pool. * diff --git a/src/asm/scala/tools/asm/Context.java b/src/asm/scala/tools/asm/Context.java index 7b3a2ad9dd..24546969e3 100644 --- a/src/asm/scala/tools/asm/Context.java +++ b/src/asm/scala/tools/asm/Context.java @@ -72,11 +72,46 @@ class Context { */ String desc; + /** + * The label objects, indexed by bytecode offset, of the method currently + * being parsed (only bytecode offsets for which a label is needed have a + * non null associated Label object). + */ + Label[] labels; + + /** + * The target of the type annotation currently being parsed. + */ + int typeRef; + + /** + * The path of the type annotation currently being parsed. + */ + TypePath typePath; + /** * The offset of the latest stack map frame that has been parsed. */ int offset; + /** + * The labels corresponding to the start of the local variable ranges in the + * local variable type annotation currently being parsed. + */ + Label[] start; + + /** + * The labels corresponding to the end of the local variable ranges in the + * local variable type annotation currently being parsed. + */ + Label[] end; + + /** + * The local variable indices for each local variable range in the local + * variable type annotation currently being parsed. + */ + int[] index; + /** * The encoding of the latest stack map frame that has been parsed. */ diff --git a/src/asm/scala/tools/asm/CustomAttr.java b/src/asm/scala/tools/asm/CustomAttr.java index 22b5d287b7..5ecfd283d0 100644 --- a/src/asm/scala/tools/asm/CustomAttr.java +++ b/src/asm/scala/tools/asm/CustomAttr.java @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL + * Copyright 2005-2012 LAMP/EPFL */ package scala.tools.asm; diff --git a/src/asm/scala/tools/asm/FieldVisitor.java b/src/asm/scala/tools/asm/FieldVisitor.java index 9171f331e5..708c1d322e 100644 --- a/src/asm/scala/tools/asm/FieldVisitor.java +++ b/src/asm/scala/tools/asm/FieldVisitor.java @@ -31,8 +31,8 @@ package scala.tools.asm; /** * A visitor to visit a Java field. The methods of this class must be called in - * the following order: ( visitAnnotation | visitAttribute )* - * visitEnd. + * the following order: ( visitAnnotation | + * visitTypeAnnotation | visitAttribute )* visitEnd. * * @author Eric Bruneton */ @@ -40,7 +40,7 @@ public abstract class FieldVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -55,7 +55,7 @@ public abstract class FieldVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public FieldVisitor(final int api) { this(api, null); @@ -66,13 +66,13 @@ public abstract class FieldVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param fv * the field visitor to which this visitor must delegate method * calls. May be null. */ public FieldVisitor(final int api, final FieldVisitor fv) { - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } this.api = api; @@ -96,6 +96,35 @@ public abstract class FieldVisitor { return null; } + /** + * Visits an annotation on the type of the field. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#FIELD FIELD}. See + * {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (fv != null) { + return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + /** * Visits a non standard attribute of the field. * diff --git a/src/asm/scala/tools/asm/FieldWriter.java b/src/asm/scala/tools/asm/FieldWriter.java index 02c6059b91..e640a8d406 100644 --- a/src/asm/scala/tools/asm/FieldWriter.java +++ b/src/asm/scala/tools/asm/FieldWriter.java @@ -80,6 +80,17 @@ final class FieldWriter extends FieldVisitor { */ private AnnotationWriter ianns; + /** + * The runtime visible type annotations of this field. May be null. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this field. May be + * null. + */ + private AnnotationWriter itanns; + /** * The non standard attributes of this field. May be null. */ @@ -107,7 +118,7 @@ final class FieldWriter extends FieldVisitor { */ FieldWriter(final ClassWriter cw, final int access, final String name, final String desc, final String signature, final Object value) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); if (cw.firstField == null) { cw.firstField = this; } else { @@ -150,6 +161,29 @@ final class FieldWriter extends FieldVisitor { return aw; } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + @Override public void visitAttribute(final Attribute attr) { attr.next = attrs; @@ -198,6 +232,14 @@ final class FieldWriter extends FieldVisitor { cw.newUTF8("RuntimeInvisibleAnnotations"); size += 8 + ianns.getSize(); } + if (ClassReader.ANNOTATIONS && tanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + tanns.getSize(); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + itanns.getSize(); + } if (attrs != null) { size += attrs.getSize(cw, null, 0, -1, -1); } @@ -237,6 +279,12 @@ final class FieldWriter extends FieldVisitor { if (ClassReader.ANNOTATIONS && ianns != null) { ++attributeCount; } + if (ClassReader.ANNOTATIONS && tanns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && itanns != null) { + ++attributeCount; + } if (attrs != null) { attributeCount += attrs.getCount(); } @@ -266,6 +314,14 @@ final class FieldWriter extends FieldVisitor { out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); ianns.put(out); } + if (ClassReader.ANNOTATIONS && tanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } if (attrs != null) { attrs.put(cw, null, 0, -1, -1, out); } diff --git a/src/asm/scala/tools/asm/Frame.java b/src/asm/scala/tools/asm/Frame.java index bcc3e8450b..85ad3269ab 100644 --- a/src/asm/scala/tools/asm/Frame.java +++ b/src/asm/scala/tools/asm/Frame.java @@ -70,8 +70,8 @@ final class Frame { * stack types. VALUE depends on KIND. For LOCAL types, it is an index in * the input local variable types. For STACK types, it is a position * relatively to the top of input frame stack. For BASE types, it is either - * one of the constants defined in FrameVisitor, or for OBJECT and - * UNINITIALIZED types, a tag and an index in the type table. + * one of the constants defined below, or for OBJECT and UNINITIALIZED + * types, a tag and an index in the type table. * * Output frames can contain types of any kind and with a positive or * negative dimension (and even unassigned types, represented by 0 - which @@ -1417,6 +1417,7 @@ final class Frame { // if t is the NULL type, merge(u,t)=u, so there is no change return false; } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) { + // if t and u have the same dimension and same base kind if ((u & BASE_KIND) == OBJECT) { // if t is also a reference type, and if u and t have the // same dimension merge(u,t) = dim(t) | common parent of the @@ -1425,13 +1426,21 @@ final class Frame { | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); } else { // if u and t are array types, but not with the same element - // type, merge(u,t)=java/lang/Object - v = OBJECT | cw.addType("java/lang/Object"); + // type, merge(u,t) = dim(u) - 1 | java/lang/Object + int vdim = ELEMENT_OF + (u & DIM); + v = vdim | OBJECT | cw.addType("java/lang/Object"); } } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) { - // if t is any other reference or array type, - // merge(u,t)=java/lang/Object - v = OBJECT | cw.addType("java/lang/Object"); + // if t is any other reference or array type, the merged type + // is min(udim, tdim) | java/lang/Object, where udim is the + // array dimension of u, minus 1 if u is an array type with a + // primitive element type (and similarly for tdim). + int tdim = (((t & DIM) == 0 || (t & BASE_KIND) == OBJECT) ? 0 + : ELEMENT_OF) + (t & DIM); + int udim = (((u & DIM) == 0 || (u & BASE_KIND) == OBJECT) ? 0 + : ELEMENT_OF) + (u & DIM); + v = Math.min(tdim, udim) | OBJECT + | cw.addType("java/lang/Object"); } else { // if t is any other type, merge(u,t)=TOP v = TOP; diff --git a/src/asm/scala/tools/asm/Handle.java b/src/asm/scala/tools/asm/Handle.java index 5dd06a54b9..cf12bb7613 100644 --- a/src/asm/scala/tools/asm/Handle.java +++ b/src/asm/scala/tools/asm/Handle.java @@ -49,7 +49,8 @@ public final class Handle { final int tag; /** - * The internal name of the field or method designed by this handle. + * The internal name of the class that owns the field or method designated + * by this handle. */ final String owner; @@ -76,8 +77,8 @@ public final class Handle { * {@link Opcodes#H_NEWINVOKESPECIAL} or * {@link Opcodes#H_INVOKEINTERFACE}. * @param owner - * the internal name of the field or method designed by this - * handle. + * the internal name of the class that owns the field or method + * designated by this handle. * @param name * the name of the field or method designated by this handle. * @param desc @@ -106,9 +107,11 @@ public final class Handle { } /** - * Returns the internal name of the field or method designed by this handle. + * Returns the internal name of the class that owns the field or method + * designated by this handle. * - * @return the internal name of the field or method designed by this handle. + * @return the internal name of the class that owns the field or method + * designated by this handle. */ public String getOwner() { return owner; diff --git a/src/asm/scala/tools/asm/Item.java b/src/asm/scala/tools/asm/Item.java index 94195a1082..4693f5ae99 100644 --- a/src/asm/scala/tools/asm/Item.java +++ b/src/asm/scala/tools/asm/Item.java @@ -208,9 +208,10 @@ final class Item { this.strVal2 = strVal2; this.strVal3 = strVal3; switch (type) { + case ClassWriter.CLASS: + this.intVal = 0; // intVal of a class must be zero, see visitInnerClass case ClassWriter.UTF8: case ClassWriter.STR: - case ClassWriter.CLASS: case ClassWriter.MTYPE: case ClassWriter.TYPE_NORMAL: hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); diff --git a/src/asm/scala/tools/asm/MethodVisitor.java b/src/asm/scala/tools/asm/MethodVisitor.java index e43ca97823..bddc325020 100644 --- a/src/asm/scala/tools/asm/MethodVisitor.java +++ b/src/asm/scala/tools/asm/MethodVisitor.java @@ -31,18 +31,24 @@ package scala.tools.asm; /** * A visitor to visit a Java method. The methods of this class must be called in - * the following order: [ visitAnnotationDefault ] ( - * visitAnnotation | visitParameterAnnotation | - * visitAttribute )* [ visitCode ( visitFrame | - * visitXInsn | visitLabel | - * visitTryCatchBlock | visitLocalVariable | + * the following order: ( visitParameter )* [ + * visitAnnotationDefault ] ( visitAnnotation | + * visitTypeAnnotation | visitAttribute )* [ + * visitCode ( visitFrame | visitXInsn | + * visitLabel | visitInsnAnnotation | + * visitTryCatchBlock | visitTryCatchBlockAnnotation | + * visitLocalVariable | visitLocalVariableAnnotation | * visitLineNumber )* visitMaxs ] visitEnd. In - * addition, the visitXInsn and visitLabel methods - * must be called in the sequential order of the bytecode instructions of the - * visited code, visitTryCatchBlock must be called before the - * labels passed as arguments have been visited, and the - * visitLocalVariable and visitLineNumber methods must be - * called after the labels passed as arguments have been visited. + * addition, the visitXInsn and visitLabel methods must + * be called in the sequential order of the bytecode instructions of the visited + * code, visitInsnAnnotation must be called after the annotated + * instruction, visitTryCatchBlock must be called before the + * labels passed as arguments have been visited, + * visitTryCatchBlockAnnotation must be called after the + * corresponding try catch block has been visited, and the + * visitLocalVariable, visitLocalVariableAnnotation and + * visitLineNumber methods must be called after the labels + * passed as arguments have been visited. * * @author Eric Bruneton */ @@ -50,7 +56,7 @@ public abstract class MethodVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -65,7 +71,7 @@ public abstract class MethodVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public MethodVisitor(final int api) { this(api, null); @@ -76,13 +82,13 @@ public abstract class MethodVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param mv * the method visitor to which this visitor must delegate method * calls. May be null. */ public MethodVisitor(final int api, final MethodVisitor mv) { - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } this.api = api; @@ -90,9 +96,28 @@ public abstract class MethodVisitor { } // ------------------------------------------------------------------------- - // Annotations and non standard attributes + // Parameters, annotations and non standard attributes // ------------------------------------------------------------------------- + /** + * Visits a parameter of this method. + * + * @param name + * parameter name or null if none is provided. + * @param access + * the parameter's access flags, only ACC_FINAL, + * ACC_SYNTHETIC or/and ACC_MANDATED are + * allowed (see {@link Opcodes}). + */ + public void visitParameter(String name, int access) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + mv.visitParameter(name, access); + } + } + /** * Visits the default value of this annotation interface method. * @@ -127,6 +152,42 @@ public abstract class MethodVisitor { return null; } + /** + * Visits an annotation on a type in the method signature. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#METHOD_TYPE_PARAMETER + * METHOD_TYPE_PARAMETER}, + * {@link TypeReference#METHOD_TYPE_PARAMETER_BOUND + * METHOD_TYPE_PARAMETER_BOUND}, + * {@link TypeReference#METHOD_RETURN METHOD_RETURN}, + * {@link TypeReference#METHOD_RECEIVER METHOD_RECEIVER}, + * {@link TypeReference#METHOD_FORMAL_PARAMETER + * METHOD_FORMAL_PARAMETER} or {@link TypeReference#THROWS + * THROWS}. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + /** * Visits an annotation of a parameter this method. * @@ -201,9 +262,11 @@ public abstract class MethodVisitor { *
  • {@link Opcodes#F_CHOP} representing frame with current locals are the * same as the locals in the previous frame, except that the last 1-3 locals * are absent and with the empty stack (nLocals is 1, 2 or 3).
  • - *
  • {@link Opcodes#F_FULL} representing complete frame data.
  • + *
  • {@link Opcodes#F_FULL} representing complete frame data.
  • + * + * * - *
    + *
    * In both cases the first frame, corresponding to the method's parameters * and access flags, is implicit and must not be visited. Also, it is * illegal to visit two or more frames for the same code location (i.e., at @@ -376,13 +439,52 @@ public abstract class MethodVisitor { * @param desc * the method's descriptor (see {@link Type Type}). */ + @Deprecated public void visitMethodInsn(int opcode, String owner, String name, String desc) { + if (api >= Opcodes.ASM5) { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + visitMethodInsn(opcode, owner, name, desc, itf); + return; + } if (mv != null) { mv.visitMethodInsn(opcode, owner, name, desc); } } + /** + * Visits a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link Type#getInternalName() getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + * @param itf + * if the method's owner class is an interface. + */ + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + if (itf != (opcode == Opcodes.INVOKEINTERFACE)) { + throw new IllegalArgumentException( + "INVOKESPECIAL/STATIC on interfaces require ASM 5"); + } + visitMethodInsn(opcode, owner, name, desc); + return; + } + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + /** * Visits an invokedynamic instruction. * @@ -558,6 +660,48 @@ public abstract class MethodVisitor { } } + /** + * Visits an annotation on an instruction. This method must be called just + * after the annotated instruction. It can be called several times + * for the same instruction. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#INSTANCEOF INSTANCEOF}, + * {@link TypeReference#NEW NEW}, + * {@link TypeReference#CONSTRUCTOR_REFERENCE + * CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE + * METHOD_REFERENCE}, {@link TypeReference#CAST CAST}, + * {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitInsnAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + // ------------------------------------------------------------------------- // Exceptions table entries, debug information, max stack and max locals // ------------------------------------------------------------------------- @@ -586,6 +730,38 @@ public abstract class MethodVisitor { } } + /** + * Visits an annotation on an exception handler type. This method must be + * called after the {@link #visitTryCatchBlock} for the annotated + * exception handler. It can be called several times for the same exception + * handler. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#EXCEPTION_PARAMETER + * EXCEPTION_PARAMETER}. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + /** * Visits a local variable declaration. * @@ -616,6 +792,48 @@ public abstract class MethodVisitor { } } + /** + * Visits an annotation on a local variable type. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#LOCAL_VARIABLE + * LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE + * RESOURCE_VARIABLE}. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param start + * the fist instructions corresponding to the continuous ranges + * that make the scope of this local variable (inclusive). + * @param end + * the last instructions corresponding to the continuous ranges + * that make the scope of this local variable (exclusive). This + * array must have the same size as the 'start' array. + * @param index + * the local variable's index in each range. This array must have + * the same size as the 'start' array. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitLocalVariableAnnotation(typeRef, typePath, start, + end, index, desc, visible); + } + return null; + } + /** * Visits a line number declaration. * diff --git a/src/asm/scala/tools/asm/MethodWriter.java b/src/asm/scala/tools/asm/MethodWriter.java index 87acab17c9..0c4130e499 100644 --- a/src/asm/scala/tools/asm/MethodWriter.java +++ b/src/asm/scala/tools/asm/MethodWriter.java @@ -191,6 +191,18 @@ class MethodWriter extends MethodVisitor { */ private AnnotationWriter ianns; + /** + * The runtime visible type annotations of this method. May be null + * . + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this method. May be + * null. + */ + private AnnotationWriter itanns; + /** * The runtime visible parameter annotations of this method. May be * null. @@ -282,6 +294,16 @@ class MethodWriter extends MethodVisitor { */ private Handler lastHandler; + /** + * Number of entries in the MethodParameters attribute. + */ + private int methodParametersCount; + + /** + * The MethodParameters attribute. + */ + private ByteVector methodParameters; + /** * Number of entries in the LocalVariableTable attribute. */ @@ -312,6 +334,21 @@ class MethodWriter extends MethodVisitor { */ private ByteVector lineNumber; + /** + * The start offset of the last visited instruction. + */ + private int lastCodeOffset; + + /** + * The runtime visible type annotations of the code. May be null. + */ + private AnnotationWriter ctanns; + + /** + * The runtime invisible type annotations of the code. May be null. + */ + private AnnotationWriter ictanns; + /** * The non standard attributes of the method's code. */ @@ -416,7 +453,7 @@ class MethodWriter extends MethodVisitor { final String desc, final String signature, final String[] exceptions, final boolean computeMaxs, final boolean computeFrames) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); if (cw.firstMethod == null) { cw.firstMethod = this; } else { @@ -461,6 +498,16 @@ class MethodWriter extends MethodVisitor { // Implementation of the MethodVisitor abstract class // ------------------------------------------------------------------------ + @Override + public void visitParameter(String name, int access) { + if (methodParameters == null) { + methodParameters = new ByteVector(); + } + ++methodParametersCount; + methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name)) + .putShort(access); + } + @Override public AnnotationVisitor visitAnnotationDefault() { if (!ClassReader.ANNOTATIONS) { @@ -490,6 +537,29 @@ class MethodWriter extends MethodVisitor { return aw; } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + @Override public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { @@ -642,6 +712,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitInsn(final int opcode) { + lastCodeOffset = code.length; // adds the instruction to the bytecode of the method code.putByte(opcode); // update currentBlock @@ -667,6 +738,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitIntInsn(final int opcode, final int operand) { + lastCodeOffset = code.length; // Label currentBlock = this.currentBlock; if (currentBlock != null) { if (compute == FRAMES) { @@ -691,6 +763,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitVarInsn(final int opcode, final int var) { + lastCodeOffset = code.length; // Label currentBlock = this.currentBlock; if (currentBlock != null) { if (compute == FRAMES) { @@ -749,6 +822,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitTypeInsn(final int opcode, final String type) { + lastCodeOffset = code.length; Item i = cw.newClassItem(type); // Label currentBlock = this.currentBlock; if (currentBlock != null) { @@ -771,6 +845,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { + lastCodeOffset = code.length; Item i = cw.newFieldItem(owner, name, desc); // Label currentBlock = this.currentBlock; if (currentBlock != null) { @@ -808,8 +883,8 @@ class MethodWriter extends MethodVisitor { @Override public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { - boolean itf = opcode == Opcodes.INVOKEINTERFACE; + final String name, final String desc, final boolean itf) { + lastCodeOffset = code.length; Item i = cw.newMethodItem(owner, name, desc, itf); int argSize = i.intVal; // Label currentBlock = this.currentBlock; @@ -847,7 +922,7 @@ class MethodWriter extends MethodVisitor { } } // adds the instruction to the bytecode of the method - if (itf) { + if (opcode == Opcodes.INVOKEINTERFACE) { if (argSize == 0) { argSize = Type.getArgumentsAndReturnSizes(desc); i.intVal = argSize; @@ -861,6 +936,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitInvokeDynamicInsn(final String name, final String desc, final Handle bsm, final Object... bsmArgs) { + lastCodeOffset = code.length; Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs); int argSize = i.intVal; // Label currentBlock = this.currentBlock; @@ -900,6 +976,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitJumpInsn(final int opcode, final Label label) { + lastCodeOffset = code.length; Label nextInsn = null; // Label currentBlock = this.currentBlock; if (currentBlock != null) { @@ -1045,6 +1122,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitLdcInsn(final Object cst) { + lastCodeOffset = code.length; Item i = cw.newConstItem(cst); // Label currentBlock = this.currentBlock; if (currentBlock != null) { @@ -1078,6 +1156,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitIincInsn(final int var, final int increment) { + lastCodeOffset = code.length; if (currentBlock != null) { if (compute == FRAMES) { currentBlock.frame.execute(Opcodes.IINC, var, null, null); @@ -1102,6 +1181,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { + lastCodeOffset = code.length; // adds the instruction to the bytecode of the method int source = code.length; code.putByte(Opcodes.TABLESWITCH); @@ -1118,6 +1198,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { + lastCodeOffset = code.length; // adds the instruction to the bytecode of the method int source = code.length; code.putByte(Opcodes.LOOKUPSWITCH); @@ -1160,6 +1241,7 @@ class MethodWriter extends MethodVisitor { @Override public void visitMultiANewArrayInsn(final String desc, final int dims) { + lastCodeOffset = code.length; Item i = cw.newClassItem(desc); // Label currentBlock = this.currentBlock; if (currentBlock != null) { @@ -1175,6 +1257,30 @@ class MethodWriter extends MethodVisitor { code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); } + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8); + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -1193,6 +1299,29 @@ class MethodWriter extends MethodVisitor { lastHandler = h; } + @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -1225,6 +1354,41 @@ class MethodWriter extends MethodVisitor { } } + @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + bv.putByte(typeRef >>> 24).putShort(start.length); + for (int i = 0; i < start.length; ++i) { + bv.putShort(start[i].position) + .putShort(end[i].position - start[i].position) + .putShort(index[i]); + } + if (typePath == null) { + bv.putByte(0); + } else { + int length = typePath.b[typePath.offset] * 2 + 1; + bv.putByteArray(typePath.b, typePath.offset, length); + } + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + @Override public void visitLineNumber(final int line, final Label start) { if (lineNumber == null) { @@ -1237,6 +1401,14 @@ class MethodWriter extends MethodVisitor { @Override public void visitMaxs(final int maxStack, final int maxLocals) { + if (resize) { + // replaces the temporary jump opcodes introduced by Label.resolve. + if (ClassReader.RESIZE) { + resizeInstructions(); + } else { + throw new RuntimeException("Method code too large!"); + } + } if (ClassReader.FRAMES && compute == FRAMES) { // completes the control flow graph with exception handler blocks Handler handler = firstHandler; @@ -1858,22 +2030,12 @@ class MethodWriter extends MethodVisitor { if (classReaderOffset != 0) { return 6 + classReaderLength; } - if (resize) { - // replaces the temporary jump opcodes introduced by Label.resolve. - if (ClassReader.RESIZE) { - resizeInstructions(); - } else { - throw new RuntimeException("Method code too large!"); - } - } int size = 8; if (code.length > 0) { if (code.length > 65536) { String nameString = ""; - int i = 0; - // find item that corresponds to the index of our name - while (i < cw.items.length && (cw.items[i] == null || cw.items[i].index != name)) i++; - if (cw.items[i] != null) nameString = cw.items[i].strVal1 +"'s "; + Item nameItem = cw.findItemByIndex(name); + if (nameItem != null) nameString = nameItem.strVal1 +"'s "; throw new RuntimeException("Method "+ nameString +"code too large!"); } cw.newUTF8("Code"); @@ -1895,6 +2057,14 @@ class MethodWriter extends MethodVisitor { cw.newUTF8(zip ? "StackMapTable" : "StackMap"); size += 8 + stackMap.length; } + if (ClassReader.ANNOTATIONS && ctanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + ctanns.getSize(); + } + if (ClassReader.ANNOTATIONS && ictanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + ictanns.getSize(); + } if (cattrs != null) { size += cattrs.getSize(cw, code.data, code.length, maxStack, maxLocals); @@ -1920,6 +2090,10 @@ class MethodWriter extends MethodVisitor { cw.newUTF8(signature); size += 8; } + if (methodParameters != null) { + cw.newUTF8("MethodParameters"); + size += 7 + methodParameters.length; + } if (ClassReader.ANNOTATIONS && annd != null) { cw.newUTF8("AnnotationDefault"); size += 6 + annd.length; @@ -1932,6 +2106,14 @@ class MethodWriter extends MethodVisitor { cw.newUTF8("RuntimeInvisibleAnnotations"); size += 8 + ianns.getSize(); } + if (ClassReader.ANNOTATIONS && tanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + tanns.getSize(); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + itanns.getSize(); + } if (ClassReader.ANNOTATIONS && panns != null) { cw.newUTF8("RuntimeVisibleParameterAnnotations"); size += 7 + 2 * (panns.length - synthetics); @@ -1988,6 +2170,9 @@ class MethodWriter extends MethodVisitor { if (ClassReader.SIGNATURES && signature != null) { ++attributeCount; } + if (methodParameters != null) { + ++attributeCount; + } if (ClassReader.ANNOTATIONS && annd != null) { ++attributeCount; } @@ -1997,6 +2182,12 @@ class MethodWriter extends MethodVisitor { if (ClassReader.ANNOTATIONS && ianns != null) { ++attributeCount; } + if (ClassReader.ANNOTATIONS && tanns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && itanns != null) { + ++attributeCount; + } if (ClassReader.ANNOTATIONS && panns != null) { ++attributeCount; } @@ -2021,6 +2212,12 @@ class MethodWriter extends MethodVisitor { if (stackMap != null) { size += 8 + stackMap.length; } + if (ClassReader.ANNOTATIONS && ctanns != null) { + size += 8 + ctanns.getSize(); + } + if (ClassReader.ANNOTATIONS && ictanns != null) { + size += 8 + ictanns.getSize(); + } if (cattrs != null) { size += cattrs.getSize(cw, code.data, code.length, maxStack, maxLocals); @@ -2050,6 +2247,12 @@ class MethodWriter extends MethodVisitor { if (stackMap != null) { ++attributeCount; } + if (ClassReader.ANNOTATIONS && ctanns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && ictanns != null) { + ++attributeCount; + } if (cattrs != null) { attributeCount += cattrs.getCount(); } @@ -2075,6 +2278,14 @@ class MethodWriter extends MethodVisitor { out.putInt(stackMap.length + 2).putShort(frameCount); out.putByteArray(stackMap.data, 0, stackMap.length); } + if (ClassReader.ANNOTATIONS && ctanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + ctanns.put(out); + } + if (ClassReader.ANNOTATIONS && ictanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + ictanns.put(out); + } if (cattrs != null) { cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); } @@ -2100,6 +2311,12 @@ class MethodWriter extends MethodVisitor { out.putShort(cw.newUTF8("Signature")).putInt(2) .putShort(cw.newUTF8(signature)); } + if (methodParameters != null) { + out.putShort(cw.newUTF8("MethodParameters")); + out.putInt(methodParameters.length + 1).putByte( + methodParametersCount); + out.putByteArray(methodParameters.data, 0, methodParameters.length); + } if (ClassReader.ANNOTATIONS && annd != null) { out.putShort(cw.newUTF8("AnnotationDefault")); out.putInt(annd.length); @@ -2113,6 +2330,14 @@ class MethodWriter extends MethodVisitor { out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); ianns.put(out); } + if (ClassReader.ANNOTATIONS && tanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } if (ClassReader.ANNOTATIONS && panns != null) { out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); AnnotationWriter.put(panns, synthetics, out); @@ -2464,49 +2689,50 @@ class MethodWriter extends MethodVisitor { } } - // recomputes the stack map frames - if (frameCount > 0) { - if (compute == FRAMES) { - frameCount = 0; - stackMap = null; - previousFrame = null; - frame = null; - Frame f = new Frame(); - f.owner = labels; - Type[] args = Type.getArgumentTypes(descriptor); - f.initInputFrame(cw, access, args, maxLocals); - visitFrame(f); - Label l = labels; - while (l != null) { - /* - * here we need the original label position. getNewOffset - * must therefore never have been called for this label. - */ - u = l.position - 3; - if ((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) { - getNewOffset(allIndexes, allSizes, l); - // TODO update offsets in UNINITIALIZED values - visitFrame(l.frame); - } - l = l.successor; - } - } else { + // updates the stack map frame labels + if (compute == FRAMES) { + Label l = labels; + while (l != null) { /* - * Resizing an existing stack map frame table is really hard. - * Not only the table must be parsed to update the offets, but - * new frames may be needed for jump instructions that were - * inserted by this method. And updating the offsets or - * inserting frames can change the format of the following - * frames, in case of packed frames. In practice the whole table - * must be recomputed. For this the frames are marked as - * potentially invalid. This will cause the whole class to be - * reread and rewritten with the COMPUTE_FRAMES option (see the - * ClassWriter.toByteArray method). This is not very efficient - * but is much easier and requires much less code than any other - * method I can think of. + * Detects the labels that are just after an IF instruction that + * has been resized with the IFNOT GOTO_W pattern. These labels + * are now the target of a jump instruction (the IFNOT + * instruction). Note that we need the original label position + * here. getNewOffset must therefore never have been called for + * this label. */ - cw.invalidFrames = true; + u = l.position - 3; + if (u >= 0 && resize[u]) { + l.status |= Label.TARGET; + } + getNewOffset(allIndexes, allSizes, l); + l = l.successor; } + // Update the offsets in the uninitialized types + for (i = 0; i < cw.typeTable.length; ++i) { + Item item = cw.typeTable[i]; + if (item != null && item.type == ClassWriter.TYPE_UNINIT) { + item.intVal = getNewOffset(allIndexes, allSizes, 0, + item.intVal); + } + } + // The stack map frames are not serialized yet, so we don't need + // to update them. They will be serialized in visitMaxs. + } else if (frameCount > 0) { + /* + * Resizing an existing stack map frame table is really hard. Not + * only the table must be parsed to update the offets, but new + * frames may be needed for jump instructions that were inserted by + * this method. And updating the offsets or inserting frames can + * change the format of the following frames, in case of packed + * frames. In practice the whole table must be recomputed. For this + * the frames are marked as potentially invalid. This will cause the + * whole class to be reread and rewritten with the COMPUTE_FRAMES + * option (see the ClassWriter.toByteArray method). This is not very + * efficient but is much easier and requires much less code than any + * other method I can think of. + */ + cw.invalidFrames = true; } // updates the exception handler block labels Handler h = firstHandler; diff --git a/src/asm/scala/tools/asm/Opcodes.java b/src/asm/scala/tools/asm/Opcodes.java index 809e5ae590..24eaffa717 100644 --- a/src/asm/scala/tools/asm/Opcodes.java +++ b/src/asm/scala/tools/asm/Opcodes.java @@ -46,6 +46,7 @@ public interface Opcodes { // ASM API versions int ASM4 = 4 << 16 | 0 << 8 | 0; + int ASM5 = 5 << 16 | 0 << 8 | 0; // versions @@ -56,6 +57,7 @@ public interface Opcodes { int V1_5 = 0 << 16 | 49; int V1_6 = 0 << 16 | 50; int V1_7 = 0 << 16 | 51; + int V1_8 = 0 << 16 | 52; // access flags @@ -63,7 +65,7 @@ public interface Opcodes { int ACC_PRIVATE = 0x0002; // class, field, method int ACC_PROTECTED = 0x0004; // class, field, method int ACC_STATIC = 0x0008; // field, method - int ACC_FINAL = 0x0010; // class, field, method + int ACC_FINAL = 0x0010; // class, field, method, parameter int ACC_SUPER = 0x0020; // class int ACC_SYNCHRONIZED = 0x0020; // method int ACC_VOLATILE = 0x0040; // field @@ -74,9 +76,10 @@ public interface Opcodes { int ACC_INTERFACE = 0x0200; // class int ACC_ABSTRACT = 0x0400; // class, method int ACC_STRICT = 0x0800; // method - int ACC_SYNTHETIC = 0x1000; // class, field, method + int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter int ACC_ANNOTATION = 0x2000; // class int ACC_ENUM = 0x4000; // class(?) field inner + int ACC_MANDATED = 0x8000; // parameter // ASM specific pseudo access flags diff --git a/src/asm/scala/tools/asm/Type.java b/src/asm/scala/tools/asm/Type.java index 7821a492e6..7887080dee 100644 --- a/src/asm/scala/tools/asm/Type.java +++ b/src/asm/scala/tools/asm/Type.java @@ -401,8 +401,8 @@ public class Type { * @return the size of the arguments of the method (plus one for the * implicit this argument), argSize, and the size of its return * value, retSize, packed into a single int i = - * (argSize << 2) | retSize (argSize is therefore equal to - * i >> 2, and retSize to i & 0x03). + * (argSize << 2) | retSize (argSize is therefore equal to + * i >> 2, and retSize to i & 0x03). */ public static int getArgumentsAndReturnSizes(final String desc) { int n = 1; @@ -606,9 +606,10 @@ public class Type { * * @return the size of the arguments (plus one for the implicit this * argument), argSize, and the size of the return value, retSize, - * packed into a single int i = (argSize << 2) | retSize - * (argSize is therefore equal to i >> 2, and retSize to - * i & 0x03). + * packed into a single + * int i = (argSize << 2) | retSize + * (argSize is therefore equal to i >> 2, + * and retSize to i & 0x03). */ public int getArgumentsAndReturnSizes() { return getArgumentsAndReturnSizes(getDescriptor()); diff --git a/src/asm/scala/tools/asm/TypePath.java b/src/asm/scala/tools/asm/TypePath.java new file mode 100644 index 0000000000..d4c6f0d857 --- /dev/null +++ b/src/asm/scala/tools/asm/TypePath.java @@ -0,0 +1,193 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2013 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package scala.tools.asm; + +/** + * The path to a type argument, wildcard bound, array element type, or static + * inner type within an enclosing type. + * + * @author Eric Bruneton + */ +public class TypePath { + + /** + * A type path step that steps into the element type of an array type. See + * {@link #getStep getStep}. + */ + public final static int ARRAY_ELEMENT = 0; + + /** + * A type path step that steps into the nested type of a class type. See + * {@link #getStep getStep}. + */ + public final static int INNER_TYPE = 1; + + /** + * A type path step that steps into the bound of a wildcard type. See + * {@link #getStep getStep}. + */ + public final static int WILDCARD_BOUND = 2; + + /** + * A type path step that steps into a type argument of a generic type. See + * {@link #getStep getStep}. + */ + public final static int TYPE_ARGUMENT = 3; + + /** + * The byte array where the path is stored, in Java class file format. + */ + byte[] b; + + /** + * The offset of the first byte of the type path in 'b'. + */ + int offset; + + /** + * Creates a new type path. + * + * @param b + * the byte array containing the type path in Java class file + * format. + * @param offset + * the offset of the first byte of the type path in 'b'. + */ + TypePath(byte[] b, int offset) { + this.b = b; + this.offset = offset; + } + + /** + * Returns the length of this path. + * + * @return the length of this path. + */ + public int getLength() { + return b[offset]; + } + + /** + * Returns the value of the given step of this path. + * + * @param index + * an index between 0 and {@link #getLength()}, exclusive. + * @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE + * INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or + * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. + */ + public int getStep(int index) { + return b[offset + 2 * index + 1]; + } + + /** + * Returns the index of the type argument that the given step is stepping + * into. This method should only be used for steps whose value is + * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. + * + * @param index + * an index between 0 and {@link #getLength()}, exclusive. + * @return the index of the type argument that the given step is stepping + * into. + */ + public int getStepArgument(int index) { + return b[offset + 2 * index + 2]; + } + + /** + * Converts a type path in string form, in the format used by + * {@link #toString()}, into a TypePath object. + * + * @param typePath + * a type path in string form, in the format used by + * {@link #toString()}. May be null or empty. + * @return the corresponding TypePath object, or null if the path is empty. + */ + public static TypePath fromString(final String typePath) { + if (typePath == null || typePath.length() == 0) { + return null; + } + int n = typePath.length(); + ByteVector out = new ByteVector(n); + out.putByte(0); + for (int i = 0; i < n;) { + char c = typePath.charAt(i++); + if (c == '[') { + out.put11(ARRAY_ELEMENT, 0); + } else if (c == '.') { + out.put11(INNER_TYPE, 0); + } else if (c == '*') { + out.put11(WILDCARD_BOUND, 0); + } else if (c >= '0' && c <= '9') { + int typeArg = c - '0'; + while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') { + typeArg = typeArg * 10 + c - '0'; + i += 1; + } + out.put11(TYPE_ARGUMENT, typeArg); + } + } + out.data[0] = (byte) (out.length / 2); + return new TypePath(out.data, 0); + } + + /** + * Returns a string representation of this type path. {@link #ARRAY_ELEMENT + * ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE + * INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps + * with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type + * argument index in decimal form. + */ + @Override + public String toString() { + int length = getLength(); + StringBuilder result = new StringBuilder(length * 2); + for (int i = 0; i < length; ++i) { + switch (getStep(i)) { + case ARRAY_ELEMENT: + result.append('['); + break; + case INNER_TYPE: + result.append('.'); + break; + case WILDCARD_BOUND: + result.append('*'); + break; + case TYPE_ARGUMENT: + result.append(getStepArgument(i)); + break; + default: + result.append('_'); + } + } + return result.toString(); + } +} diff --git a/src/asm/scala/tools/asm/TypeReference.java b/src/asm/scala/tools/asm/TypeReference.java new file mode 100644 index 0000000000..118b0f6529 --- /dev/null +++ b/src/asm/scala/tools/asm/TypeReference.java @@ -0,0 +1,452 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2013 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package scala.tools.asm; + +/** + * A reference to a type appearing in a class, field or method declaration, or + * on an instruction. Such a reference designates the part of the class where + * the referenced type is appearing (e.g. an 'extends', 'implements' or 'throws' + * clause, a 'new' instruction, a 'catch' clause, a type cast, a local variable + * declaration, etc). + * + * @author Eric Bruneton + */ +public class TypeReference { + + /** + * The sort of type references that target a type parameter of a generic + * class. See {@link #getSort getSort}. + */ + public final static int CLASS_TYPE_PARAMETER = 0x00; + + /** + * The sort of type references that target a type parameter of a generic + * method. See {@link #getSort getSort}. + */ + public final static int METHOD_TYPE_PARAMETER = 0x01; + + /** + * The sort of type references that target the super class of a class or one + * of the interfaces it implements. See {@link #getSort getSort}. + */ + public final static int CLASS_EXTENDS = 0x10; + + /** + * The sort of type references that target a bound of a type parameter of a + * generic class. See {@link #getSort getSort}. + */ + public final static int CLASS_TYPE_PARAMETER_BOUND = 0x11; + + /** + * The sort of type references that target a bound of a type parameter of a + * generic method. See {@link #getSort getSort}. + */ + public final static int METHOD_TYPE_PARAMETER_BOUND = 0x12; + + /** + * The sort of type references that target the type of a field. See + * {@link #getSort getSort}. + */ + public final static int FIELD = 0x13; + + /** + * The sort of type references that target the return type of a method. See + * {@link #getSort getSort}. + */ + public final static int METHOD_RETURN = 0x14; + + /** + * The sort of type references that target the receiver type of a method. + * See {@link #getSort getSort}. + */ + public final static int METHOD_RECEIVER = 0x15; + + /** + * The sort of type references that target the type of a formal parameter of + * a method. See {@link #getSort getSort}. + */ + public final static int METHOD_FORMAL_PARAMETER = 0x16; + + /** + * The sort of type references that target the type of an exception declared + * in the throws clause of a method. See {@link #getSort getSort}. + */ + public final static int THROWS = 0x17; + + /** + * The sort of type references that target the type of a local variable in a + * method. See {@link #getSort getSort}. + */ + public final static int LOCAL_VARIABLE = 0x40; + + /** + * The sort of type references that target the type of a resource variable + * in a method. See {@link #getSort getSort}. + */ + public final static int RESOURCE_VARIABLE = 0x41; + + /** + * The sort of type references that target the type of the exception of a + * 'catch' clause in a method. See {@link #getSort getSort}. + */ + public final static int EXCEPTION_PARAMETER = 0x42; + + /** + * The sort of type references that target the type declared in an + * 'instanceof' instruction. See {@link #getSort getSort}. + */ + public final static int INSTANCEOF = 0x43; + + /** + * The sort of type references that target the type of the object created by + * a 'new' instruction. See {@link #getSort getSort}. + */ + public final static int NEW = 0x44; + + /** + * The sort of type references that target the receiver type of a + * constructor reference. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_REFERENCE = 0x45; + + /** + * The sort of type references that target the receiver type of a method + * reference. See {@link #getSort getSort}. + */ + public final static int METHOD_REFERENCE = 0x46; + + /** + * The sort of type references that target the type declared in an explicit + * or implicit cast instruction. See {@link #getSort getSort}. + */ + public final static int CAST = 0x47; + + /** + * The sort of type references that target a type parameter of a generic + * constructor in a constructor call. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; + + /** + * The sort of type references that target a type parameter of a generic + * method in a method call. See {@link #getSort getSort}. + */ + public final static int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; + + /** + * The sort of type references that target a type parameter of a generic + * constructor in a constructor reference. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; + + /** + * The sort of type references that target a type parameter of a generic + * method in a method reference. See {@link #getSort getSort}. + */ + public final static int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; + + /** + * The type reference value in Java class file format. + */ + private int value; + + /** + * Creates a new TypeReference. + * + * @param typeRef + * the int encoded value of the type reference, as received in a + * visit method related to type annotations, like + * visitTypeAnnotation. + */ + public TypeReference(int typeRef) { + this.value = typeRef; + } + + /** + * Returns a type reference of the given sort. + * + * @param sort + * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, + * {@link #METHOD_RECEIVER METHOD_RECEIVER}, + * {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, + * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, + * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, + * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, or + * {@link #METHOD_REFERENCE METHOD_REFERENCE}. + * @return a type reference of the given sort. + */ + public static TypeReference newTypeReference(int sort) { + return new TypeReference(sort << 24); + } + + /** + * Returns a reference to a type parameter of a generic class or method. + * + * @param sort + * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + * @param paramIndex + * the type parameter index. + * @return a reference to the given generic class or method type parameter. + */ + public static TypeReference newTypeParameterReference(int sort, + int paramIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16)); + } + + /** + * Returns a reference to a type parameter bound of a generic class or + * method. + * + * @param sort + * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + * @param paramIndex + * the type parameter index. + * @param boundIndex + * the type bound index within the above type parameters. + * @return a reference to the given generic class or method type parameter + * bound. + */ + public static TypeReference newTypeParameterBoundReference(int sort, + int paramIndex, int boundIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16) + | (boundIndex << 8)); + } + + /** + * Returns a reference to the super class or to an interface of the + * 'implements' clause of a class. + * + * @param itfIndex + * the index of an interface in the 'implements' clause of a + * class, or -1 to reference the super class of the class. + * @return a reference to the given super type of a class. + */ + public static TypeReference newSuperTypeReference(int itfIndex) { + itfIndex &= 0xFFFF; + return new TypeReference((CLASS_EXTENDS << 24) | (itfIndex << 8)); + } + + /** + * Returns a reference to the type of a formal parameter of a method. + * + * @param paramIndex + * the formal parameter index. + * + * @return a reference to the type of the given method formal parameter. + */ + public static TypeReference newFormalParameterReference(int paramIndex) { + return new TypeReference((METHOD_FORMAL_PARAMETER << 24) + | (paramIndex << 16)); + } + + /** + * Returns a reference to the type of an exception, in a 'throws' clause of + * a method. + * + * @param exceptionIndex + * the index of an exception in a 'throws' clause of a method. + * + * @return a reference to the type of the given exception. + */ + public static TypeReference newExceptionReference(int exceptionIndex) { + return new TypeReference((THROWS << 24) | (exceptionIndex << 8)); + } + + /** + * Returns a reference to the type of the exception declared in a 'catch' + * clause of a method. + * + * @param tryCatchBlockIndex + * the index of a try catch block (using the order in which they + * are visited with visitTryCatchBlock). + * + * @return a reference to the type of the given exception. + */ + public static TypeReference newTryCatchReference(int tryCatchBlockIndex) { + return new TypeReference((EXCEPTION_PARAMETER << 24) + | (tryCatchBlockIndex << 8)); + } + + /** + * Returns a reference to the type of a type argument in a constructor or + * method call or reference. + * + * @param sort + * {@link #CAST CAST}, + * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + * @param argIndex + * the type argument index. + * + * @return a reference to the type of the given type argument. + */ + public static TypeReference newTypeArgumentReference(int sort, int argIndex) { + return new TypeReference((sort << 24) | argIndex); + } + + /** + * Returns the sort of this type reference. + * + * @return {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, + * {@link #CLASS_EXTENDS CLASS_EXTENDS}, + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND}, + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}, + * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, + * {@link #METHOD_RECEIVER METHOD_RECEIVER}, + * {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}, + * {@link #THROWS THROWS}, {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, + * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, + * {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER}, + * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, + * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, + * {@link #METHOD_REFERENCE METHOD_REFERENCE}, {@link #CAST CAST}, + * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + */ + public int getSort() { + return value >>> 24; + } + + /** + * Returns the index of the type parameter referenced by this type + * reference. This method must only be used for type references whose sort + * is {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter index. + */ + public int getTypeParameterIndex() { + return (value & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the type parameter bound, within the type parameter + * {@link #getTypeParameterIndex}, referenced by this type reference. This + * method must only be used for type references whose sort is + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter bound index. + */ + public int getTypeParameterBoundIndex() { + return (value & 0x0000FF00) >> 8; + } + + /** + * Returns the index of the "super type" of a class that is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #CLASS_EXTENDS CLASS_EXTENDS}. + * + * @return the index of an interface in the 'implements' clause of a class, + * or -1 if this type reference references the type of the super + * class. + */ + public int getSuperTypeIndex() { + return (short) ((value & 0x00FFFF00) >> 8); + } + + /** + * Returns the index of the formal parameter whose type is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}. + * + * @return a formal parameter index. + */ + public int getFormalParameterIndex() { + return (value & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the exception, in a 'throws' clause of a method, + * whose type is referenced by this type reference. This method must only be + * used for type references whose sort is {@link #THROWS THROWS}. + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getExceptionIndex() { + return (value & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the try catch block (using the order in which they + * are visited with visitTryCatchBlock), whose 'catch' type is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER} . + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getTryCatchBlockIndex() { + return (value & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the type argument referenced by this type reference. + * This method must only be used for type references whose sort is + * {@link #CAST CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}. + * + * @return a type parameter index. + */ + public int getTypeArgumentIndex() { + return value & 0xFF; + } + + /** + * Returns the int encoded value of this type reference, suitable for use in + * visit methods related to type annotations, like visitTypeAnnotation. + * + * @return the int encoded value of this type reference. + */ + public int getValue() { + return value; + } +} diff --git a/src/asm/scala/tools/asm/signature/SignatureVisitor.java b/src/asm/scala/tools/asm/signature/SignatureVisitor.java index f38f81f53b..1e16bd3f7c 100644 --- a/src/asm/scala/tools/asm/signature/SignatureVisitor.java +++ b/src/asm/scala/tools/asm/signature/SignatureVisitor.java @@ -73,7 +73,7 @@ public abstract class SignatureVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -82,9 +82,12 @@ public abstract class SignatureVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public SignatureVisitor(final int api) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { + throw new IllegalArgumentException(); + } this.api = api; } diff --git a/src/asm/scala/tools/asm/signature/SignatureWriter.java b/src/asm/scala/tools/asm/signature/SignatureWriter.java index ebf4fe07b4..65756eee51 100644 --- a/src/asm/scala/tools/asm/signature/SignatureWriter.java +++ b/src/asm/scala/tools/asm/signature/SignatureWriter.java @@ -66,7 +66,7 @@ public class SignatureWriter extends SignatureVisitor { * Constructs a new {@link SignatureWriter} object. */ public SignatureWriter() { - super(Opcodes.ASM4); + super(Opcodes.ASM5); } // ------------------------------------------------------------------------ diff --git a/src/asm/scala/tools/asm/tree/AbstractInsnNode.java b/src/asm/scala/tools/asm/tree/AbstractInsnNode.java index 411eead3c7..2ce0c8b6ee 100644 --- a/src/asm/scala/tools/asm/tree/AbstractInsnNode.java +++ b/src/asm/scala/tools/asm/tree/AbstractInsnNode.java @@ -29,6 +29,7 @@ */ package scala.tools.asm.tree; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -127,6 +128,28 @@ public abstract class AbstractInsnNode { */ protected int opcode; + /** + * The runtime visible type annotations of this instruction. This field is + * only used for real instructions (i.e. not for labels, frames, or line + * number nodes). This list is a list of {@link TypeAnnotationNode} objects. + * May be null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this instruction. This field is + * only used for real instructions (i.e. not for labels, frames, or line + * number nodes). This list is a list of {@link TypeAnnotationNode} objects. + * May be null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * Previous instruction in the list to which this instruction belongs. */ @@ -203,6 +226,29 @@ public abstract class AbstractInsnNode { */ public abstract void accept(final MethodVisitor cv); + /** + * Makes the given visitor visit the annotations of this instruction. + * + * @param mv + * a method visitor. + */ + protected final void acceptAnnotations(final MethodVisitor mv) { + int n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitInsnAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitInsnAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } + } + /** * Returns a copy of this instruction. * @@ -245,4 +291,36 @@ public abstract class AbstractInsnNode { } return clones; } + + /** + * Clones the annotations of the given instruction into this instruction. + * + * @param insn + * the source instruction. + * @return this instruction. + */ + protected final AbstractInsnNode cloneAnnotations( + final AbstractInsnNode insn) { + if (insn.visibleTypeAnnotations != null) { + this.visibleTypeAnnotations = new ArrayList(); + for (int i = 0; i < insn.visibleTypeAnnotations.size(); ++i) { + TypeAnnotationNode src = insn.visibleTypeAnnotations.get(i); + TypeAnnotationNode ann = new TypeAnnotationNode(src.typeRef, + src.typePath, src.desc); + src.accept(ann); + this.visibleTypeAnnotations.add(ann); + } + } + if (insn.invisibleTypeAnnotations != null) { + this.invisibleTypeAnnotations = new ArrayList(); + for (int i = 0; i < insn.invisibleTypeAnnotations.size(); ++i) { + TypeAnnotationNode src = insn.invisibleTypeAnnotations.get(i); + TypeAnnotationNode ann = new TypeAnnotationNode(src.typeRef, + src.typePath, src.desc); + src.accept(ann); + this.invisibleTypeAnnotations.add(ann); + } + } + return this; + } } diff --git a/src/asm/scala/tools/asm/tree/AnnotationNode.java b/src/asm/scala/tools/asm/tree/AnnotationNode.java index 1f4beef9f7..b8d5988066 100644 --- a/src/asm/scala/tools/asm/tree/AnnotationNode.java +++ b/src/asm/scala/tools/asm/tree/AnnotationNode.java @@ -67,9 +67,14 @@ public class AnnotationNode extends AnnotationVisitor { * * @param desc * the class descriptor of the annotation class. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public AnnotationNode(final String desc) { - this(Opcodes.ASM4, desc); + this(Opcodes.ASM5, desc); + if (getClass() != AnnotationNode.class) { + throw new IllegalStateException(); + } } /** @@ -77,7 +82,7 @@ public class AnnotationNode extends AnnotationVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param desc * the class descriptor of the annotation class. */ @@ -93,7 +98,7 @@ public class AnnotationNode extends AnnotationVisitor { * where the visited values must be stored. */ AnnotationNode(final List values) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); this.values = values; } @@ -166,7 +171,8 @@ public class AnnotationNode extends AnnotationVisitor { * versions of the ASM API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { // nothing to do diff --git a/src/asm/scala/tools/asm/tree/ClassNode.java b/src/asm/scala/tools/asm/tree/ClassNode.java index c3d999985a..304b4ec9f5 100644 --- a/src/asm/scala/tools/asm/tree/ClassNode.java +++ b/src/asm/scala/tools/asm/tree/ClassNode.java @@ -39,6 +39,7 @@ import scala.tools.asm.ClassVisitor; import scala.tools.asm.FieldVisitor; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A node that represents a class. @@ -132,6 +133,24 @@ public class ClassNode extends ClassVisitor { */ public List invisibleAnnotations; + /** + * The runtime visible type annotations of this class. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this class. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * The non standard attributes of this class. This list is a list of * {@link Attribute} objects. May be null. @@ -168,9 +187,15 @@ public class ClassNode extends ClassVisitor { * Constructs a new {@link ClassNode}. Subclasses must not use this * constructor. Instead, they must use the {@link #ClassNode(int)} * version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public ClassNode() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != ClassNode.class) { + throw new IllegalStateException(); + } } /** @@ -178,7 +203,7 @@ public class ClassNode extends ClassVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public ClassNode(final int api) { super(api); @@ -238,6 +263,24 @@ public class ClassNode extends ClassVisitor { return an; } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public void visitAttribute(final Attribute attr) { if (attrs == null) { @@ -286,10 +329,26 @@ public class ClassNode extends ClassVisitor { * API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + for (FieldNode f : fields) { + f.check(api); + } + for (MethodNode m : methods) { + m.check(api); + } + } } /** @@ -323,6 +382,19 @@ public class ClassNode extends ClassVisitor { AnnotationNode an = invisibleAnnotations.get(i); an.accept(cv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(cv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(cv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = attrs == null ? 0 : attrs.size(); for (i = 0; i < n; ++i) { cv.visitAttribute(attrs.get(i)); diff --git a/src/asm/scala/tools/asm/tree/FieldInsnNode.java b/src/asm/scala/tools/asm/tree/FieldInsnNode.java index 0c94f18adf..c027de109b 100644 --- a/src/asm/scala/tools/asm/tree/FieldInsnNode.java +++ b/src/asm/scala/tools/asm/tree/FieldInsnNode.java @@ -97,12 +97,14 @@ public class FieldInsnNode extends AbstractInsnNode { } @Override - public void accept(final MethodVisitor cv) { - cv.visitFieldInsn(opcode, owner, name, desc); + public void accept(final MethodVisitor mv) { + mv.visitFieldInsn(opcode, owner, name, desc); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new FieldInsnNode(opcode, owner, name, desc); + return new FieldInsnNode(opcode, owner, name, desc) + .cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/FieldNode.java b/src/asm/scala/tools/asm/tree/FieldNode.java index 61b614ec59..3fb14dac4f 100644 --- a/src/asm/scala/tools/asm/tree/FieldNode.java +++ b/src/asm/scala/tools/asm/tree/FieldNode.java @@ -37,6 +37,7 @@ import scala.tools.asm.Attribute; import scala.tools.asm.ClassVisitor; import scala.tools.asm.FieldVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A node that represents a field. @@ -91,6 +92,24 @@ public class FieldNode extends FieldVisitor { */ public List invisibleAnnotations; + /** + * The runtime visible type annotations of this field. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this field. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * The non standard attributes of this field. This list is a list of * {@link Attribute} objects. May be null. @@ -120,20 +139,24 @@ public class FieldNode extends FieldVisitor { * null if the field does not have an initial value, * must be an {@link Integer}, a {@link Float}, a {@link Long}, a * {@link Double} or a {@link String}. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public FieldNode(final int access, final String name, final String desc, final String signature, final Object value) { - this(Opcodes.ASM4, access, name, desc, signature, value); + this(Opcodes.ASM5, access, name, desc, signature, value); + if (getClass() != FieldNode.class) { + throw new IllegalStateException(); + } } /** * Constructs a new {@link FieldNode}. Subclasses must not use this - * constructor. Instead, they must use the - * {@link #FieldNode(int, int, String, String, String, Object)} version. + * constructor. * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param access * the field's access flags (see * {@link scala.tools.asm.Opcodes}). This parameter also @@ -183,6 +206,24 @@ public class FieldNode extends FieldVisitor { return an; } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public void visitAttribute(final Attribute attr) { if (attrs == null) { @@ -206,10 +247,20 @@ public class FieldNode extends FieldVisitor { * API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + } } /** @@ -234,6 +285,19 @@ public class FieldNode extends FieldVisitor { AnnotationNode an = invisibleAnnotations.get(i); an.accept(fv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(fv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(fv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = attrs == null ? 0 : attrs.size(); for (i = 0; i < n; ++i) { fv.visitAttribute(attrs.get(i)); diff --git a/src/asm/scala/tools/asm/tree/IincInsnNode.java b/src/asm/scala/tools/asm/tree/IincInsnNode.java index f9adf2e38c..c37ac91c27 100644 --- a/src/asm/scala/tools/asm/tree/IincInsnNode.java +++ b/src/asm/scala/tools/asm/tree/IincInsnNode.java @@ -73,10 +73,11 @@ public class IincInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitIincInsn(var, incr); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new IincInsnNode(var, incr); + return new IincInsnNode(var, incr).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/InsnList.java b/src/asm/scala/tools/asm/tree/InsnList.java index b1e2d97c6f..e808712e78 100644 --- a/src/asm/scala/tools/asm/tree/InsnList.java +++ b/src/asm/scala/tools/asm/tree/InsnList.java @@ -100,7 +100,7 @@ public class InsnList { * the index of the instruction that must be returned. * @return the instruction whose index is given. * @throws IndexOutOfBoundsException - * if (index < 0 || index >= size()). + * if (index < 0 || index >= size()). */ public AbstractInsnNode get(final int index) { if (index < 0 || index >= size) { @@ -535,6 +535,8 @@ public class InsnList { AbstractInsnNode prev; + AbstractInsnNode remove; + InsnListIterator(int index) { if (index == size()) { next = null; @@ -556,12 +558,22 @@ public class InsnList { AbstractInsnNode result = next; prev = result; next = result.next; + remove = result; return result; } public void remove() { - InsnList.this.remove(prev); - prev = prev.prev; + if (remove != null) { + if (remove == next) { + next = next.next; + } else { + prev = prev.prev; + } + InsnList.this.remove(remove); + remove = null; + } else { + throw new IllegalStateException(); + } } public boolean hasPrevious() { @@ -572,6 +584,7 @@ public class InsnList { AbstractInsnNode result = prev; next = result; prev = result.prev; + remove = result; return result; } @@ -598,6 +611,7 @@ public class InsnList { public void add(Object o) { InsnList.this.insertBefore(next, (AbstractInsnNode) o); prev = (AbstractInsnNode) o; + remove = null; } public void set(Object o) { diff --git a/src/asm/scala/tools/asm/tree/InsnNode.java b/src/asm/scala/tools/asm/tree/InsnNode.java index 4d5288cafa..f5313929ee 100644 --- a/src/asm/scala/tools/asm/tree/InsnNode.java +++ b/src/asm/scala/tools/asm/tree/InsnNode.java @@ -78,10 +78,11 @@ public class InsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitInsn(opcode); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new InsnNode(opcode); + return new InsnNode(opcode).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/IntInsnNode.java b/src/asm/scala/tools/asm/tree/IntInsnNode.java index e0aeed4bc8..6bbe8d845c 100644 --- a/src/asm/scala/tools/asm/tree/IntInsnNode.java +++ b/src/asm/scala/tools/asm/tree/IntInsnNode.java @@ -78,10 +78,11 @@ public class IntInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitIntInsn(opcode, operand); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new IntInsnNode(opcode, operand); + return new IntInsnNode(opcode, operand).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java b/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java index 7ee84b875b..0f85e60291 100644 --- a/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java +++ b/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java @@ -91,10 +91,12 @@ public class InvokeDynamicInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs); + return new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs) + .cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/JumpInsnNode.java b/src/asm/scala/tools/asm/tree/JumpInsnNode.java index 81e1e09deb..8b8a769204 100644 --- a/src/asm/scala/tools/asm/tree/JumpInsnNode.java +++ b/src/asm/scala/tools/asm/tree/JumpInsnNode.java @@ -86,10 +86,12 @@ public class JumpInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitJumpInsn(opcode, label.getLabel()); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new JumpInsnNode(opcode, clone(label, labels)); + return new JumpInsnNode(opcode, clone(label, labels)) + .cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/LdcInsnNode.java b/src/asm/scala/tools/asm/tree/LdcInsnNode.java index 4e328f9b39..1cc850bb31 100644 --- a/src/asm/scala/tools/asm/tree/LdcInsnNode.java +++ b/src/asm/scala/tools/asm/tree/LdcInsnNode.java @@ -69,10 +69,11 @@ public class LdcInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitLdcInsn(cst); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new LdcInsnNode(cst); + return new LdcInsnNode(cst).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/LocalVariableAnnotationNode.java b/src/asm/scala/tools/asm/tree/LocalVariableAnnotationNode.java new file mode 100644 index 0000000000..d05b808171 --- /dev/null +++ b/src/asm/scala/tools/asm/tree/LocalVariableAnnotationNode.java @@ -0,0 +1,157 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package scala.tools.asm.tree; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import scala.tools.asm.Label; +import scala.tools.asm.MethodVisitor; +import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; + +/** + * A node that represents a type annotation on a local or resource variable. + * + * @author Eric Bruneton + */ +public class LocalVariableAnnotationNode extends TypeAnnotationNode { + + /** + * The fist instructions corresponding to the continuous ranges that make + * the scope of this local variable (inclusive). Must not be null. + */ + public List start; + + /** + * The last instructions corresponding to the continuous ranges that make + * the scope of this local variable (exclusive). This list must have the + * same size as the 'start' list. Must not be null. + */ + public List end; + + /** + * The local variable's index in each range. This list must have the same + * size as the 'start' list. Must not be null. + */ + public List index; + + /** + * Constructs a new {@link LocalVariableAnnotationNode}. Subclasses must + * not use this constructor. Instead, they must use the + * {@link #LocalVariableAnnotationNode(int, TypePath, LabelNode[], LabelNode[], int[], String)} + * version. + * + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param start + * the fist instructions corresponding to the continuous ranges + * that make the scope of this local variable (inclusive). + * @param end + * the last instructions corresponding to the continuous ranges + * that make the scope of this local variable (exclusive). This + * array must have the same size as the 'start' array. + * @param index + * the local variable's index in each range. This array must have + * the same size as the 'start' array. + * @param desc + * the class descriptor of the annotation class. + */ + public LocalVariableAnnotationNode(int typeRef, TypePath typePath, + LabelNode[] start, LabelNode[] end, int[] index, String desc) { + this(Opcodes.ASM5, typeRef, typePath, start, end, index, desc); + } + + /** + * Constructs a new {@link LocalVariableAnnotationNode}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @param start + * the fist instructions corresponding to the continuous ranges + * that make the scope of this local variable (inclusive). + * @param end + * the last instructions corresponding to the continuous ranges + * that make the scope of this local variable (exclusive). This + * array must have the same size as the 'start' array. + * @param index + * the local variable's index in each range. This array must have + * the same size as the 'start' array. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + */ + public LocalVariableAnnotationNode(int api, int typeRef, TypePath typePath, + LabelNode[] start, LabelNode[] end, int[] index, String desc) { + super(api, typeRef, typePath, desc); + this.start = new ArrayList(start.length); + this.start.addAll(Arrays.asList(start)); + this.end = new ArrayList(end.length); + this.end.addAll(Arrays.asList(end)); + this.index = new ArrayList(index.length); + for (int i : index) { + this.index.add(i); + } + } + + /** + * Makes the given visitor visit this type annotation. + * + * @param mv + * the visitor that must visit this annotation. + * @param visible + * true if the annotation is visible at runtime. + */ + public void accept(final MethodVisitor mv, boolean visible) { + Label[] start = new Label[this.start.size()]; + Label[] end = new Label[this.end.size()]; + int[] index = new int[this.index.size()]; + for (int i = 0; i < start.length; ++i) { + start[i] = this.start.get(i).getLabel(); + end[i] = this.end.get(i).getLabel(); + index[i] = this.index.get(i); + } + accept(mv.visitLocalVariableAnnotation(typeRef, typePath, start, end, + index, desc, true)); + } +} diff --git a/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java b/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java index d2479b4814..7db2f53ff4 100644 --- a/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java +++ b/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java @@ -105,6 +105,7 @@ public class LookupSwitchInsnNode extends AbstractInsnNode { labels[i] = this.labels.get(i).getLabel(); } mv.visitLookupSwitchInsn(dflt.getLabel(), keys, labels); + acceptAnnotations(mv); } @Override @@ -112,6 +113,6 @@ public class LookupSwitchInsnNode extends AbstractInsnNode { LookupSwitchInsnNode clone = new LookupSwitchInsnNode(clone(dflt, labels), null, clone(this.labels, labels)); clone.keys.addAll(keys); - return clone; + return clone.cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/MethodInsnNode.java b/src/asm/scala/tools/asm/tree/MethodInsnNode.java index bf09f556d8..1ec46d473d 100644 --- a/src/asm/scala/tools/asm/tree/MethodInsnNode.java +++ b/src/asm/scala/tools/asm/tree/MethodInsnNode.java @@ -32,6 +32,7 @@ package scala.tools.asm.tree; import java.util.Map; import scala.tools.asm.MethodVisitor; +import scala.tools.asm.Opcodes; /** * A node that represents a method instruction. A method instruction is an @@ -57,6 +58,11 @@ public class MethodInsnNode extends AbstractInsnNode { */ public String desc; + /** + * If the method's owner class if an interface. + */ + public boolean itf; + /** * Constructs a new {@link MethodInsnNode}. * @@ -73,12 +79,37 @@ public class MethodInsnNode extends AbstractInsnNode { * @param desc * the method's descriptor (see {@link scala.tools.asm.Type}). */ + @Deprecated public MethodInsnNode(final int opcode, final String owner, final String name, final String desc) { + this(opcode, owner, name, desc, opcode == Opcodes.INVOKEINTERFACE); + } + + /** + * Constructs a new {@link MethodInsnNode}. + * + * @param opcode + * the opcode of the type instruction to be constructed. This + * opcode must be INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link scala.tools.asm.Type#getInternalName() + * getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link scala.tools.asm.Type}). + * @param itf + * if the method's owner class is an interface. + */ + public MethodInsnNode(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { super(opcode); this.owner = owner; this.name = name; this.desc = desc; + this.itf = itf; } /** @@ -99,11 +130,11 @@ public class MethodInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { - mv.visitMethodInsn(opcode, owner, name, desc); + mv.visitMethodInsn(opcode, owner, name, desc, itf); } @Override public AbstractInsnNode clone(final Map labels) { - return new MethodInsnNode(opcode, owner, name, desc); + return new MethodInsnNode(opcode, owner, name, desc, itf); } } diff --git a/src/asm/scala/tools/asm/tree/MethodNode.java b/src/asm/scala/tools/asm/tree/MethodNode.java index a161600edb..3dec50e02c 100644 --- a/src/asm/scala/tools/asm/tree/MethodNode.java +++ b/src/asm/scala/tools/asm/tree/MethodNode.java @@ -41,6 +41,7 @@ import scala.tools.asm.Label; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; /** * A node that represents a method. @@ -77,6 +78,11 @@ public class MethodNode extends MethodVisitor { */ public List exceptions; + /** + * The method parameter info (access flags and name) + */ + public List parameters; + /** * The runtime visible annotations of this method. This list is a list of * {@link AnnotationNode} objects. May be null. @@ -95,6 +101,24 @@ public class MethodNode extends MethodVisitor { */ public List invisibleAnnotations; + /** + * The runtime visible type annotations of this method. This list is a list + * of {@link TypeAnnotationNode} objects. May be null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this method. This list is a + * list of {@link TypeAnnotationNode} objects. May be null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * The non standard attributes of this method. This list is a list of * {@link Attribute} objects. May be null. @@ -166,6 +190,22 @@ public class MethodNode extends MethodVisitor { */ public List localVariables; + /** + * The visible local variable annotations of this method. This list is a + * list of {@link LocalVariableAnnotationNode} objects. May be null + * + * @associates scala.tools.asm.tree.LocalVariableAnnotationNode + */ + public List visibleLocalVariableAnnotations; + + /** + * The invisible local variable annotations of this method. This list is a + * list of {@link LocalVariableAnnotationNode} objects. May be null + * + * @associates scala.tools.asm.tree.LocalVariableAnnotationNode + */ + public List invisibleLocalVariableAnnotations; + /** * If the accept method has been called on this object. */ @@ -175,9 +215,15 @@ public class MethodNode extends MethodVisitor { * Constructs an uninitialized {@link MethodNode}. Subclasses must not * use this constructor. Instead, they must use the * {@link #MethodNode(int)} version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public MethodNode() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != MethodNode.class) { + throw new IllegalStateException(); + } } /** @@ -185,7 +231,7 @@ public class MethodNode extends MethodVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public MethodNode(final int api) { super(api); @@ -211,10 +257,15 @@ public class MethodNode extends MethodVisitor { * the internal names of the method's exception classes (see * {@link Type#getInternalName() getInternalName}). May be * null. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public MethodNode(final int access, final String name, final String desc, final String signature, final String[] exceptions) { - this(Opcodes.ASM4, access, name, desc, signature, exceptions); + this(Opcodes.ASM5, access, name, desc, signature, exceptions); + if (getClass() != MethodNode.class) { + throw new IllegalStateException(); + } } /** @@ -222,7 +273,7 @@ public class MethodNode extends MethodVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param access * the method's access flags (see {@link Opcodes}). This * parameter also indicates if the method is synthetic and/or @@ -263,6 +314,15 @@ public class MethodNode extends MethodVisitor { // ------------------------------------------------------------------------ @Override + public void visitParameter(String name, int access) { + if (parameters == null) { + parameters = new ArrayList(5); + } + parameters.add(new ParameterNode(name, access)); + } + + @Override + @SuppressWarnings("serial") public AnnotationVisitor visitAnnotationDefault() { return new AnnotationNode(new ArrayList(0) { @Override @@ -291,6 +351,24 @@ public class MethodNode extends MethodVisitor { return an; } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { @@ -365,12 +443,27 @@ public class MethodNode extends MethodVisitor { instructions.add(new FieldInsnNode(opcode, owner, name, desc)); } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } instructions.add(new MethodInsnNode(opcode, owner, name, desc)); } + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + instructions.add(new MethodInsnNode(opcode, owner, name, desc, itf)); + } + @Override public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { @@ -416,6 +509,33 @@ public class MethodNode extends MethodVisitor { instructions.add(new MultiANewArrayInsnNode(desc, dims)); } + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + // Finds the last real instruction, i.e. the instruction targeted by + // this annotation. + AbstractInsnNode insn = instructions.getLast(); + while (insn.getOpcode() == -1) { + insn = insn.getPrevious(); + } + // Adds the annotation to this instruction. + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (insn.visibleTypeAnnotations == null) { + insn.visibleTypeAnnotations = new ArrayList( + 1); + } + insn.visibleTypeAnnotations.add(an); + } else { + if (insn.invisibleTypeAnnotations == null) { + insn.invisibleTypeAnnotations = new ArrayList( + 1); + } + insn.invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -423,6 +543,27 @@ public class MethodNode extends MethodVisitor { getLabelNode(end), getLabelNode(handler), type)); } + @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TryCatchBlockNode tcb = tryCatchBlocks.get((typeRef & 0x00FFFF00) >> 8); + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (tcb.visibleTypeAnnotations == null) { + tcb.visibleTypeAnnotations = new ArrayList( + 1); + } + tcb.visibleTypeAnnotations.add(an); + } else { + if (tcb.invisibleTypeAnnotations == null) { + tcb.invisibleTypeAnnotations = new ArrayList( + 1); + } + tcb.invisibleTypeAnnotations.add(an); + } + return an; + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -431,6 +572,29 @@ public class MethodNode extends MethodVisitor { getLabelNode(start), getLabelNode(end), index)); } + @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + LocalVariableAnnotationNode an = new LocalVariableAnnotationNode( + typeRef, typePath, getLabelNodes(start), getLabelNodes(end), + index, desc); + if (visible) { + if (visibleLocalVariableAnnotations == null) { + visibleLocalVariableAnnotations = new ArrayList( + 1); + } + visibleLocalVariableAnnotations.add(an); + } else { + if (invisibleLocalVariableAnnotations == null) { + invisibleLocalVariableAnnotations = new ArrayList( + 1); + } + invisibleLocalVariableAnnotations.add(an); + } + return an; + } + @Override public void visitLineNumber(final int line, final Label start) { instructions.add(new LineNumberNode(line, getLabelNode(start))); @@ -494,10 +658,57 @@ public class MethodNode extends MethodVisitor { * versions of the ASM API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + int n = tryCatchBlocks == null ? 0 : tryCatchBlocks.size(); + for (int i = 0; i < n; ++i) { + TryCatchBlockNode tcb = tryCatchBlocks.get(i); + if (tcb.visibleTypeAnnotations != null + && tcb.visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (tcb.invisibleTypeAnnotations != null + && tcb.invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + } + for (int i = 0; i < instructions.size(); ++i) { + AbstractInsnNode insn = instructions.get(i); + if (insn.visibleTypeAnnotations != null + && insn.visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (insn.invisibleTypeAnnotations != null + && insn.invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (insn instanceof MethodInsnNode) { + boolean itf = ((MethodInsnNode) insn).itf; + if (itf != (insn.opcode == Opcodes.INVOKEINTERFACE)) { + throw new RuntimeException(); + } + } + } + if (visibleLocalVariableAnnotations != null + && visibleLocalVariableAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleLocalVariableAnnotations != null + && invisibleLocalVariableAnnotations.size() > 0) { + throw new RuntimeException(); + } + } } /** @@ -523,8 +734,14 @@ public class MethodNode extends MethodVisitor { * a method visitor. */ public void accept(final MethodVisitor mv) { - // visits the method attributes + // visits the method parameters int i, j, n; + n = parameters == null ? 0 : parameters.size(); + for (i = 0; i < n; i++) { + ParameterNode parameter = parameters.get(i); + mv.visitParameter(parameter.name, parameter.access); + } + // visits the method attributes if (annotationDefault != null) { AnnotationVisitor av = mv.visitAnnotationDefault(); AnnotationNode.accept(av, null, annotationDefault); @@ -542,6 +759,19 @@ public class MethodNode extends MethodVisitor { AnnotationNode an = invisibleAnnotations.get(i); an.accept(mv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = visibleParameterAnnotations == null ? 0 : visibleParameterAnnotations.length; for (i = 0; i < n; ++i) { @@ -579,6 +809,7 @@ public class MethodNode extends MethodVisitor { // visits try catch blocks n = tryCatchBlocks == null ? 0 : tryCatchBlocks.size(); for (i = 0; i < n; ++i) { + tryCatchBlocks.get(i).updateIndex(i); tryCatchBlocks.get(i).accept(mv); } // visits instructions @@ -588,6 +819,17 @@ public class MethodNode extends MethodVisitor { for (i = 0; i < n; ++i) { localVariables.get(i).accept(mv); } + // visits local variable annotations + n = visibleLocalVariableAnnotations == null ? 0 + : visibleLocalVariableAnnotations.size(); + for (i = 0; i < n; ++i) { + visibleLocalVariableAnnotations.get(i).accept(mv, true); + } + n = invisibleLocalVariableAnnotations == null ? 0 + : invisibleLocalVariableAnnotations.size(); + for (i = 0; i < n; ++i) { + invisibleLocalVariableAnnotations.get(i).accept(mv, false); + } // visits maxs mv.visitMaxs(maxStack, maxLocals); visited = true; diff --git a/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java b/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java index fe5e8832b3..a8339a20b5 100644 --- a/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java +++ b/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java @@ -73,11 +73,12 @@ public class MultiANewArrayInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitMultiANewArrayInsn(desc, dims); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new MultiANewArrayInsnNode(desc, dims); + return new MultiANewArrayInsnNode(desc, dims).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/ParameterNode.java b/src/asm/scala/tools/asm/tree/ParameterNode.java new file mode 100644 index 0000000000..a3e55d5629 --- /dev/null +++ b/src/asm/scala/tools/asm/tree/ParameterNode.java @@ -0,0 +1,76 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package scala.tools.asm.tree; + +import scala.tools.asm.MethodVisitor; + +/** + * A node that represents a parameter access and name. + * + * @author Remi Forax + */ +public class ParameterNode { + /** + * The parameter's name. + */ + public String name; + + /** + * The parameter's access flags (see {@link scala.tools.asm.Opcodes}). + * Valid values are ACC_FINAL, ACC_SYNTHETIC and + * ACC_MANDATED. + */ + public int access; + + /** + * Constructs a new {@link ParameterNode}. + * + * @param access + * The parameter's access flags. Valid values are + * ACC_FINAL, ACC_SYNTHETIC or/and + * ACC_MANDATED (see {@link scala.tools.asm.Opcodes}). + * @param name + * the parameter's name. + */ + public ParameterNode(final String name, final int access) { + this.name = name; + this.access = access; + } + + /** + * Makes the given visitor visit this parameter declaration. + * + * @param mv + * a method visitor. + */ + public void accept(final MethodVisitor mv) { + mv.visitParameter(name, access); + } +} diff --git a/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java b/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java index 9b3c2a3437..fb17b9e2e9 100644 --- a/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java +++ b/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java @@ -103,11 +103,12 @@ public class TableSwitchInsnNode extends AbstractInsnNode { labels[i] = this.labels.get(i).getLabel(); } mv.visitTableSwitchInsn(min, max, dflt.getLabel(), labels); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { return new TableSwitchInsnNode(min, max, clone(dflt, labels), clone( - this.labels, labels)); + this.labels, labels)).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java b/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java index ab4fa97c34..c639b9aa8b 100644 --- a/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java +++ b/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java @@ -29,6 +29,8 @@ */ package scala.tools.asm.tree; +import java.util.List; + import scala.tools.asm.MethodVisitor; /** @@ -59,6 +61,26 @@ public class TryCatchBlockNode { */ public String type; + /** + * The runtime visible type annotations on the exception handler type. This + * list is a list of {@link TypeAnnotationNode} objects. May be + * null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List visibleTypeAnnotations; + + /** + * The runtime invisible type annotations on the exception handler type. + * This list is a list of {@link TypeAnnotationNode} objects. May be + * null. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List invisibleTypeAnnotations; + /** * Constructs a new {@link TryCatchBlockNode}. * @@ -81,6 +103,29 @@ public class TryCatchBlockNode { this.type = type; } + /** + * Updates the index of this try catch block in the method's list of try + * catch block nodes. This index maybe stored in the 'target' field of the + * type annotations of this block. + * + * @param index + * the new index of this try catch block in the method's list of + * try catch block nodes. + */ + public void updateIndex(final int index) { + int newTypeRef = 0x42000000 | (index << 8); + if (visibleTypeAnnotations != null) { + for (TypeAnnotationNode tan : visibleTypeAnnotations) { + tan.typeRef = newTypeRef; + } + } + if (invisibleTypeAnnotations != null) { + for (TypeAnnotationNode tan : invisibleTypeAnnotations) { + tan.typeRef = newTypeRef; + } + } + } + /** * Makes the given visitor visit this try catch block. * @@ -90,5 +135,19 @@ public class TryCatchBlockNode { public void accept(final MethodVisitor mv) { mv.visitTryCatchBlock(start.getLabel(), end.getLabel(), handler == null ? null : handler.getLabel(), type); + int n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitTryCatchAnnotation(an.typeRef, an.typePath, + an.desc, true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitTryCatchAnnotation(an.typeRef, an.typePath, + an.desc, false)); + } } } diff --git a/src/asm/scala/tools/asm/tree/TypeAnnotationNode.java b/src/asm/scala/tools/asm/tree/TypeAnnotationNode.java new file mode 100644 index 0000000000..73b29624f7 --- /dev/null +++ b/src/asm/scala/tools/asm/tree/TypeAnnotationNode.java @@ -0,0 +1,100 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package scala.tools.asm.tree; + +import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; + +/** + * A node that represents a type annotationn. + * + * @author Eric Bruneton + */ +public class TypeAnnotationNode extends AnnotationNode { + + /** + * A reference to the annotated type. See {@link TypeReference}. + */ + public int typeRef; + + /** + * The path to the annotated type argument, wildcard bound, array element + * type, or static outer type within the referenced type. May be + * null if the annotation targets 'typeRef' as a whole. + */ + public TypePath typePath; + + /** + * Constructs a new {@link AnnotationNode}. Subclasses must not use this + * constructor. Instead, they must use the + * {@link #TypeAnnotationNode(int, int, TypePath, String)} version. + * + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @throws IllegalStateException + * If a subclass calls this constructor. + */ + public TypeAnnotationNode(final int typeRef, final TypePath typePath, + final String desc) { + this(Opcodes.ASM5, typeRef, typePath, desc); + if (getClass() != TypeAnnotationNode.class) { + throw new IllegalStateException(); + } + } + + /** + * Constructs a new {@link AnnotationNode}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + */ + public TypeAnnotationNode(final int api, final int typeRef, + final TypePath typePath, final String desc) { + super(api, desc); + this.typeRef = typeRef; + this.typePath = typePath; + } +} diff --git a/src/asm/scala/tools/asm/tree/TypeInsnNode.java b/src/asm/scala/tools/asm/tree/TypeInsnNode.java index 3210dd60e6..401400c3cb 100644 --- a/src/asm/scala/tools/asm/tree/TypeInsnNode.java +++ b/src/asm/scala/tools/asm/tree/TypeInsnNode.java @@ -81,10 +81,11 @@ public class TypeInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitTypeInsn(opcode, desc); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new TypeInsnNode(opcode, desc); + return new TypeInsnNode(opcode, desc).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/VarInsnNode.java b/src/asm/scala/tools/asm/tree/VarInsnNode.java index 5dd9ef6726..685e4fce2c 100644 --- a/src/asm/scala/tools/asm/tree/VarInsnNode.java +++ b/src/asm/scala/tools/asm/tree/VarInsnNode.java @@ -84,10 +84,11 @@ public class VarInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitVarInsn(opcode, var); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map labels) { - return new VarInsnNode(opcode, var); + return new VarInsnNode(opcode, var).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java b/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java index 5e3f51f21a..52b2a11d6f 100644 --- a/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java +++ b/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java @@ -37,6 +37,7 @@ import scala.tools.asm.tree.AbstractInsnNode; * @author Bing Ran * @author Eric Bruneton */ +@SuppressWarnings("serial") public class AnalyzerException extends Exception { public final AbstractInsnNode node; diff --git a/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java b/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java index 8d6653c1c5..7d0b7b0694 100644 --- a/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java +++ b/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java @@ -53,7 +53,7 @@ public class BasicInterpreter extends Interpreter implements Opcodes { public BasicInterpreter() { - super(ASM4); + super(ASM5); } protected BasicInterpreter(final int api) { diff --git a/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java b/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java index 71666edb74..b852f20acf 100644 --- a/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java +++ b/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java @@ -47,7 +47,7 @@ import scala.tools.asm.tree.MethodInsnNode; public class BasicVerifier extends BasicInterpreter { public BasicVerifier() { - super(ASM4); + super(ASM5); } protected BasicVerifier(final int api) { diff --git a/src/asm/scala/tools/asm/tree/analysis/Frame.java b/src/asm/scala/tools/asm/tree/analysis/Frame.java index 0d92edc4d6..44a07ee27c 100644 --- a/src/asm/scala/tools/asm/tree/analysis/Frame.java +++ b/src/asm/scala/tools/asm/tree/analysis/Frame.java @@ -133,6 +133,15 @@ public class Frame { return locals; } + /** + * Returns the maximum stack size of this frame. + * + * @return the maximum stack size of this frame. + */ + public int getMaxStackSize() { + return values.length - locals; + } + /** * Returns the value of the given local variable. * diff --git a/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java b/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java index eaecd057ea..a345981f36 100644 --- a/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java +++ b/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java @@ -107,7 +107,7 @@ public class SimpleVerifier extends BasicVerifier { public SimpleVerifier(final Type currentClass, final Type currentSuperClass, final List currentClassInterfaces, final boolean isInterface) { - this(ASM4, currentClass, currentSuperClass, currentClassInterfaces, + this(ASM5, currentClass, currentSuperClass, currentClassInterfaces, isInterface); } diff --git a/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java b/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java index a68086c073..7d739d3df9 100644 --- a/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java +++ b/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java @@ -50,7 +50,7 @@ public class SourceInterpreter extends Interpreter implements Opcodes { public SourceInterpreter() { - super(ASM4); + super(ASM5); } protected SourceInterpreter(final int api) { diff --git a/src/asm/scala/tools/asm/util/ASMifier.java b/src/asm/scala/tools/asm/util/ASMifier.java index 7e6b223853..521e07541b 100644 --- a/src/asm/scala/tools/asm/util/ASMifier.java +++ b/src/asm/scala/tools/asm/util/ASMifier.java @@ -40,6 +40,7 @@ import scala.tools.asm.Handle; import scala.tools.asm.Label; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; /** * A {@link Printer} that prints the ASM code to generate the classes if visits. @@ -83,9 +84,15 @@ public class ASMifier extends Printer { * Constructs a new {@link ASMifier}. Subclasses must not use this * constructor. Instead, they must use the * {@link #ASMifier(int, String, int)} version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public ASMifier() { - this(Opcodes.ASM4, "cw", 0); + this(Opcodes.ASM5, "cw", 0); + if (getClass() != ASMifier.class) { + throw new IllegalStateException(); + } } /** @@ -93,7 +100,7 @@ public class ASMifier extends Printer { * * @param api * the ASM API version implemented by this class. Must be one of - * {@link Opcodes#ASM4}. + * {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param name * the name of the visitor variable in the produced code. * @param id @@ -170,7 +177,6 @@ public class ASMifier extends Printer { } text.add("import java.util.*;\n"); text.add("import scala.tools.asm.*;\n"); - text.add("import scala.tools.asm.attrs.*;\n"); text.add("public class " + simpleName + "Dump implements Opcodes {\n\n"); text.add("public static byte[] dump () throws Exception {\n\n"); text.add("ClassWriter cw = new ClassWriter(0);\n"); @@ -260,6 +266,12 @@ public class ASMifier extends Printer { return visitAnnotation(desc, visible); } + @Override + public ASMifier visitClassTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitClassAttribute(final Attribute attr) { visitAttribute(attr); @@ -422,6 +434,12 @@ public class ASMifier extends Printer { return visitAnnotation(desc, visible); } + @Override + public ASMifier visitFieldTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitFieldAttribute(final Attribute attr) { visitAttribute(attr); @@ -438,6 +456,16 @@ public class ASMifier extends Printer { // Methods // ------------------------------------------------------------------------ + @Override + public void visitParameter(String parameterName, int access) { + buf.setLength(0); + buf.append(name).append(".visitParameter("); + appendString(buf, parameterName); + buf.append(", "); + appendAccess(access); + text.add(buf.append(");\n").toString()); + } + @Override public ASMifier visitAnnotationDefault() { buf.setLength(0); @@ -456,6 +484,12 @@ public class ASMifier extends Printer { return visitAnnotation(desc, visible); } + @Override + public ASMifier visitMethodTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public ASMifier visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { @@ -582,9 +616,30 @@ public class ASMifier extends Printer { text.add(buf.toString()); } + @Deprecated @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, + opcode == Opcodes.INVOKEINTERFACE); + } + + @Override + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, itf); + } + + private void doVisitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { buf.setLength(0); buf.append(this.name).append(".visitMethodInsn(") .append(OPCODES[opcode]).append(", "); @@ -593,6 +648,8 @@ public class ASMifier extends Printer { appendConstant(name); buf.append(", "); appendConstant(desc); + buf.append(", "); + buf.append(itf ? "true" : "false"); buf.append(");\n"); text.add(buf.toString()); } @@ -710,6 +767,13 @@ public class ASMifier extends Printer { text.add(buf.toString()); } + @Override + public ASMifier visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitInsnAnnotation", typeRef, typePath, + desc, visible); + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -729,6 +793,13 @@ public class ASMifier extends Printer { text.add(buf.toString()); } + @Override + public ASMifier visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitTryCatchAnnotation", typeRef, + typePath, desc, visible); + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -748,6 +819,39 @@ public class ASMifier extends Printer { text.add(buf.toString()); } + @Override + public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath, + Label[] start, Label[] end, int[] index, String desc, + boolean visible) { + buf.setLength(0); + buf.append("{\n").append("av0 = ").append(name) + .append(".visitLocalVariableAnnotation("); + buf.append(typeRef); + buf.append(", TypePath.fromString(\"").append(typePath).append("\"), "); + buf.append("new Label[] {"); + for (int i = 0; i < start.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(start[i]); + } + buf.append(" }, new Label[] {"); + for (int i = 0; i < end.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(end[i]); + } + buf.append(" }, new int[] {"); + for (int i = 0; i < index.length; ++i) { + buf.append(i == 0 ? " " : ", ").append(index[i]); + } + buf.append(" }, "); + appendConstant(desc); + buf.append(", ").append(visible).append(");\n"); + text.add(buf.toString()); + ASMifier a = createASMifier("av", 0); + text.add(a.getText()); + text.add("}\n"); + return a; + } + @Override public void visitLineNumber(final int line, final Label start) { buf.setLength(0); @@ -789,6 +893,28 @@ public class ASMifier extends Printer { return a; } + public ASMifier visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitTypeAnnotation", typeRef, typePath, + desc, visible); + } + + public ASMifier visitTypeAnnotation(final String method, final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + buf.setLength(0); + buf.append("{\n").append("av0 = ").append(name).append(".") + .append(method).append("("); + buf.append(typeRef); + buf.append(", TypePath.fromString(\"").append(typePath).append("\"), "); + appendConstant(desc); + buf.append(", ").append(visible).append(");\n"); + text.add(buf.toString()); + ASMifier a = createASMifier("av", 0); + text.add(a.getText()); + text.add("}\n"); + return a; + } + public void visitAttribute(final Attribute attr) { buf.setLength(0); buf.append("// ATTRIBUTE ").append(attr.type).append('\n'); @@ -809,7 +935,7 @@ public class ASMifier extends Printer { // ------------------------------------------------------------------------ protected ASMifier createASMifier(final String name, final int id) { - return new ASMifier(Opcodes.ASM4, name, id); + return new ASMifier(Opcodes.ASM5, name, id); } /** @@ -950,6 +1076,13 @@ public class ASMifier extends Printer { buf.append("ACC_DEPRECATED"); first = false; } + if ((access & Opcodes.ACC_MANDATED) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_MANDATED"); + first = false; + } if (first) { buf.append('0'); } diff --git a/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java b/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java index f00a8f04a2..70441d1df4 100644 --- a/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java @@ -49,7 +49,7 @@ public class CheckAnnotationAdapter extends AnnotationVisitor { } CheckAnnotationAdapter(final AnnotationVisitor av, final boolean named) { - super(Opcodes.ASM4, av); + super(Opcodes.ASM5, av); this.named = named; } @@ -70,7 +70,7 @@ public class CheckAnnotationAdapter extends AnnotationVisitor { } if (value instanceof Type) { int sort = ((Type) value).getSort(); - if (sort != Type.OBJECT && sort != Type.ARRAY) { + if (sort == Type.METHOD) { throw new IllegalArgumentException("Invalid annotation value"); } } diff --git a/src/asm/scala/tools/asm/util/CheckClassAdapter.java b/src/asm/scala/tools/asm/util/CheckClassAdapter.java index 0bfa143a95..9909208cc4 100644 --- a/src/asm/scala/tools/asm/util/CheckClassAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckClassAdapter.java @@ -46,6 +46,8 @@ import scala.tools.asm.Label; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; import scala.tools.asm.tree.ClassNode; import scala.tools.asm.tree.MethodNode; import scala.tools.asm.tree.analysis.Analyzer; @@ -91,9 +93,9 @@ import scala.tools.asm.tree.analysis.SimpleVerifier; * insnNumber locals : stack): * *
    - * scala.tools.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
    - *   at scala.tools.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289)
    - *   at scala.tools.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135)
    + * org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
    + *   at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289)
    + *   at org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135)
      * ...
      * remove()V
      * 00000 LinkedBlockingQueue$Itr . . . . . . . .  :
    @@ -106,7 +108,7 @@ import scala.tools.asm.tree.analysis.SimpleVerifier;
      * 00071 LinkedBlockingQueue$Itr . I . . . . . .  :
      *   ILOAD 1
      * 00072 ?
    - *   INVOKESPECIAL java/lang/Integer. (I)V
    + *   INVOKESPECIAL java/lang/Integer.<init> (I)V
      * ...
      * 
    * @@ -215,7 +217,7 @@ public class CheckClassAdapter extends ClassVisitor { List interfaces = new ArrayList(); for (Iterator i = cn.interfaces.iterator(); i.hasNext();) { - interfaces.add(Type.getObjectType(i.next().toString())); + interfaces.add(Type.getObjectType(i.next())); } for (int i = 0; i < methods.size(); ++i) { @@ -328,9 +330,14 @@ public class CheckClassAdapter extends ClassVisitor { * false to not perform any data flow check (see * {@link CheckMethodAdapter}). This option requires valid * maxLocals and maxStack values. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow) { - this(Opcodes.ASM4, cv, checkDataFlow); + this(Opcodes.ASM5, cv, checkDataFlow); + if (getClass() != CheckClassAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -338,7 +345,7 @@ public class CheckClassAdapter extends ClassVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param cv * the class visitor to which this adapter must delegate calls. * @param checkDataFlow @@ -440,7 +447,15 @@ public class CheckClassAdapter extends ClassVisitor { CheckMethodAdapter.checkInternalName(outerName, "outer class name"); } if (innerName != null) { - CheckMethodAdapter.checkIdentifier(innerName, "inner class name"); + int start = 0; + while (start < innerName.length() + && Character.isDigit(innerName.charAt(start))) { + start++; + } + if (start == 0 || start < innerName.length()) { + CheckMethodAdapter.checkIdentifier(innerName, start, -1, + "inner class name"); + } } checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC @@ -516,6 +531,23 @@ public class CheckClassAdapter extends ClassVisitor { return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkState(); + int sort = typeRef >>> 24; + if (sort != TypeReference.CLASS_TYPE_PARAMETER + && sort != TypeReference.CLASS_TYPE_PARAMETER_BOUND + && sort != TypeReference.CLASS_EXTENDS) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + @Override public void visitAttribute(final Attribute attr) { checkState(); @@ -660,6 +692,77 @@ public class CheckClassAdapter extends ClassVisitor { } } + /** + * Checks the reference to a type in a type annotation. + * + * @param typeRef + * a reference to an annotated type. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + */ + static void checkTypeRefAndPath(int typeRef, TypePath typePath) { + int mask = 0; + switch (typeRef >>> 24) { + case TypeReference.CLASS_TYPE_PARAMETER: + case TypeReference.METHOD_TYPE_PARAMETER: + case TypeReference.METHOD_FORMAL_PARAMETER: + mask = 0xFFFF0000; + break; + case TypeReference.FIELD: + case TypeReference.METHOD_RETURN: + case TypeReference.METHOD_RECEIVER: + case TypeReference.LOCAL_VARIABLE: + case TypeReference.RESOURCE_VARIABLE: + case TypeReference.INSTANCEOF: + case TypeReference.NEW: + case TypeReference.CONSTRUCTOR_REFERENCE: + case TypeReference.METHOD_REFERENCE: + mask = 0xFF000000; + break; + case TypeReference.CLASS_EXTENDS: + case TypeReference.CLASS_TYPE_PARAMETER_BOUND: + case TypeReference.METHOD_TYPE_PARAMETER_BOUND: + case TypeReference.THROWS: + case TypeReference.EXCEPTION_PARAMETER: + mask = 0xFFFFFF00; + break; + case TypeReference.CAST: + case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: + case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: + mask = 0xFF0000FF; + break; + default: + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(typeRef >>> 24)); + } + if ((typeRef & ~mask) != 0) { + throw new IllegalArgumentException("Invalid type reference 0x" + + Integer.toHexString(typeRef)); + } + if (typePath != null) { + for (int i = 0; i < typePath.getLength(); ++i) { + int step = typePath.getStep(i); + if (step != TypePath.ARRAY_ELEMENT + && step != TypePath.INNER_TYPE + && step != TypePath.TYPE_ARGUMENT + && step != TypePath.WILDCARD_BOUND) { + throw new IllegalArgumentException( + "Invalid type path step " + i + " in " + typePath); + } + if (step != TypePath.TYPE_ARGUMENT + && typePath.getStepArgument(i) != 0) { + throw new IllegalArgumentException( + "Invalid type path step argument for step " + i + + " in " + typePath); + } + } + } + } + /** * Checks the formal type parameters of a class or method signature. * diff --git a/src/asm/scala/tools/asm/util/CheckFieldAdapter.java b/src/asm/scala/tools/asm/util/CheckFieldAdapter.java index 4657605936..e682df47af 100644 --- a/src/asm/scala/tools/asm/util/CheckFieldAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckFieldAdapter.java @@ -33,6 +33,8 @@ import scala.tools.asm.AnnotationVisitor; import scala.tools.asm.Attribute; import scala.tools.asm.FieldVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; /** * A {@link FieldVisitor} that checks that its methods are properly used. @@ -48,9 +50,14 @@ public class CheckFieldAdapter extends FieldVisitor { * * @param fv * the field visitor to which this adapter must delegate calls. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckFieldAdapter(final FieldVisitor fv) { - this(Opcodes.ASM4, fv); + this(Opcodes.ASM5, fv); + if (getClass() != CheckFieldAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -58,7 +65,7 @@ public class CheckFieldAdapter extends FieldVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param fv * the field visitor to which this adapter must delegate calls. */ @@ -74,6 +81,21 @@ public class CheckFieldAdapter extends FieldVisitor { return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkEnd(); + int sort = typeRef >>> 24; + if (sort != TypeReference.FIELD) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + @Override public void visitAttribute(final Attribute attr) { checkEnd(); diff --git a/src/asm/scala/tools/asm/util/CheckMethodAdapter.java b/src/asm/scala/tools/asm/util/CheckMethodAdapter.java index 9da01c9d6e..131dfa5e5b 100644 --- a/src/asm/scala/tools/asm/util/CheckMethodAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckMethodAdapter.java @@ -46,6 +46,8 @@ import scala.tools.asm.Label; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; import scala.tools.asm.tree.MethodNode; import scala.tools.asm.tree.analysis.Analyzer; import scala.tools.asm.tree.analysis.BasicValue; @@ -390,10 +392,15 @@ public class CheckMethodAdapter extends MethodVisitor { * the method visitor to which this adapter must delegate calls. * @param labels * a map of already visited labels (in other methods). + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckMethodAdapter(final MethodVisitor mv, final Map labels) { - this(Opcodes.ASM4, mv, labels); + this(Opcodes.ASM5, mv, labels); + if (getClass() != CheckMethodAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -434,7 +441,7 @@ public class CheckMethodAdapter extends MethodVisitor { public CheckMethodAdapter(final int access, final String name, final String desc, final MethodVisitor cmv, final Map labels) { - this(new MethodNode(access, name, desc, null, null) { + this(new MethodNode(Opcodes.ASM5, access, name, desc, null, null) { @Override public void visitEnd() { Analyzer a = new Analyzer( @@ -461,6 +468,16 @@ public class CheckMethodAdapter extends MethodVisitor { this.access = access; } + @Override + public void visitParameter(String name, int access) { + if (name != null) { + checkUnqualifiedName(version, name, "name"); + } + CheckClassAdapter.checkAccess(access, Opcodes.ACC_FINAL + + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC); + super.visitParameter(name, access); + } + @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { @@ -469,6 +486,26 @@ public class CheckMethodAdapter extends MethodVisitor { return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkEndMethod(); + int sort = typeRef >>> 24; + if (sort != TypeReference.METHOD_TYPE_PARAMETER + && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND + && sort != TypeReference.METHOD_RETURN + && sort != TypeReference.METHOD_RECEIVER + && sort != TypeReference.METHOD_FORMAL_PARAMETER + && sort != TypeReference.THROWS) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + @Override public AnnotationVisitor visitAnnotationDefault() { checkEndMethod(); @@ -647,9 +684,30 @@ public class CheckMethodAdapter extends MethodVisitor { ++insnCount; } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, + opcode == Opcodes.INVOKEINTERFACE); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, itf); + } + + private void doVisitMethodInsn(int opcode, final String owner, + final String name, final String desc, final boolean itf) { checkStartCode(); checkEndCode(); checkOpcode(opcode, 5); @@ -658,7 +716,21 @@ public class CheckMethodAdapter extends MethodVisitor { } checkInternalName(owner, "owner"); checkMethodDesc(desc); - super.visitMethodInsn(opcode, owner, name, desc); + if (opcode == Opcodes.INVOKEVIRTUAL && itf) { + throw new IllegalArgumentException( + "INVOKEVIRTUAL can't be used with interfaces"); + } + if (opcode == Opcodes.INVOKEINTERFACE && !itf) { + throw new IllegalArgumentException( + "INVOKEINTERFACE can't be used with classes"); + } + // Calling super.visitMethodInsn requires to call the correct version + // depending on this.api (otherwise infinite loops can occur). To + // simplify and to make it easier to automatically remove the backward + // compatibility code, we inline the code of the overridden method here. + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } ++insnCount; } @@ -796,6 +868,29 @@ public class CheckMethodAdapter extends MethodVisitor { ++insnCount; } + @Override + public AnnotationVisitor visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.INSTANCEOF && sort != TypeReference.NEW + && sort != TypeReference.CONSTRUCTOR_REFERENCE + && sort != TypeReference.METHOD_REFERENCE + && sort != TypeReference.CAST + && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT + && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitInsnAnnotation(typeRef, + typePath, desc, visible)); + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -820,6 +915,22 @@ public class CheckMethodAdapter extends MethodVisitor { handlers.add(end); } + @Override + public AnnotationVisitor visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.EXCEPTION_PARAMETER) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTryCatchAnnotation( + typeRef, typePath, desc, visible)); + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -840,6 +951,40 @@ public class CheckMethodAdapter extends MethodVisitor { super.visitLocalVariable(name, desc, signature, start, end, index); } + @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.LOCAL_VARIABLE + && sort != TypeReference.RESOURCE_VARIABLE) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + checkDesc(desc, false); + if (start == null || end == null || index == null + || end.length != start.length || index.length != start.length) { + throw new IllegalArgumentException( + "Invalid start, end and index arrays (must be non null and of identical length"); + } + for (int i = 0; i < start.length; ++i) { + checkLabel(start[i], true, "start label"); + checkLabel(end[i], true, "end label"); + checkUnsignedShort(index[i], "Invalid variable index"); + int s = labels.get(start[i]).intValue(); + int e = labels.get(end[i]).intValue(); + if (e < s) { + throw new IllegalArgumentException( + "Invalid start and end labels (end must be greater than start)"); + } + } + return super.visitLocalVariableAnnotation(typeRef, typePath, start, + end, index, desc, visible); + } + @Override public void visitLineNumber(final int line, final Label start) { checkStartCode(); @@ -1202,7 +1347,7 @@ public class CheckMethodAdapter extends MethodVisitor { checkIdentifier(name, begin, slash, null); begin = slash + 1; } while (slash != max); - } catch (IllegalArgumentException _) { + } catch (IllegalArgumentException unused) { throw new IllegalArgumentException( "Invalid " + msg @@ -1280,7 +1425,7 @@ public class CheckMethodAdapter extends MethodVisitor { } try { checkInternalName(desc, start + 1, index, null); - } catch (IllegalArgumentException _) { + } catch (IllegalArgumentException unused) { throw new IllegalArgumentException("Invalid descriptor: " + desc); } diff --git a/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java b/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java index e69302b8a6..54c9033c90 100644 --- a/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java @@ -113,7 +113,7 @@ public class CheckSignatureAdapter extends SignatureVisitor { * null. */ public CheckSignatureAdapter(final int type, final SignatureVisitor sv) { - this(Opcodes.ASM4, type, sv); + this(Opcodes.ASM5, type, sv); } /** @@ -121,7 +121,7 @@ public class CheckSignatureAdapter extends SignatureVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param type * the type of signature to be checked. See * {@link #CLASS_SIGNATURE}, {@link #METHOD_SIGNATURE} and diff --git a/src/asm/scala/tools/asm/util/Printer.java b/src/asm/scala/tools/asm/util/Printer.java index 86e0f9e122..4135672c6b 100644 --- a/src/asm/scala/tools/asm/util/Printer.java +++ b/src/asm/scala/tools/asm/util/Printer.java @@ -37,6 +37,7 @@ import scala.tools.asm.Attribute; import scala.tools.asm.Handle; import scala.tools.asm.Label; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * An abstract converter from visit events to text. @@ -116,7 +117,7 @@ public abstract class Printer { /** * The ASM API version implemented by this class. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -174,6 +175,15 @@ public abstract class Printer { public abstract Printer visitClassAnnotation(final String desc, final boolean visible); + /** + * Class type annotation. See + * {@link scala.tools.asm.ClassVisitor#visitTypeAnnotation}. + */ + public Printer visitClassTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + /** * Class attribute. See * {@link scala.tools.asm.ClassVisitor#visitAttribute}. @@ -248,6 +258,15 @@ public abstract class Printer { public abstract Printer visitFieldAnnotation(final String desc, final boolean visible); + /** + * Field type annotation. See + * {@link scala.tools.asm.FieldVisitor#visitTypeAnnotation}. + */ + public Printer visitFieldTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + /** * Field attribute. See * {@link scala.tools.asm.FieldVisitor#visitAttribute}. @@ -263,6 +282,14 @@ public abstract class Printer { // Methods // ------------------------------------------------------------------------ + /** + * Method parameter. See + * {@link scala.tools.asm.MethodVisitor#visitParameter(String, int)}. + */ + public void visitParameter(String name, int access) { + throw new RuntimeException("Must be overriden"); + } + /** * Method default annotation. See * {@link scala.tools.asm.MethodVisitor#visitAnnotationDefault}. @@ -276,6 +303,15 @@ public abstract class Printer { public abstract Printer visitMethodAnnotation(final String desc, final boolean visible); + /** + * Method type annotation. See + * {@link scala.tools.asm.MethodVisitor#visitTypeAnnotation}. + */ + public Printer visitMethodTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + /** * Method parameter annotation. See * {@link scala.tools.asm.MethodVisitor#visitParameterAnnotation}. @@ -336,8 +372,33 @@ public abstract class Printer { * Method instruction. See * {@link scala.tools.asm.MethodVisitor#visitMethodInsn}. */ - public abstract void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc); + @Deprecated + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc) { + if (api >= Opcodes.ASM5) { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + throw new RuntimeException("Must be overriden"); + } + + /** + * Method instruction. See + * {@link scala.tools.asm.MethodVisitor#visitMethodInsn}. + */ + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { + if (api < Opcodes.ASM5) { + if (itf != (opcode == Opcodes.INVOKEINTERFACE)) { + throw new IllegalArgumentException( + "INVOKESPECIAL/STATIC on interfaces require ASM 5"); + } + visitMethodInsn(opcode, owner, name, desc); + return; + } + throw new RuntimeException("Must be overriden"); + } /** * Method instruction. See @@ -390,6 +451,15 @@ public abstract class Printer { public abstract void visitMultiANewArrayInsn(final String desc, final int dims); + /** + * Instruction type annotation. See + * {@link scala.tools.asm.MethodVisitor#visitInsnAnnotation}. + */ + public Printer visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + /** * Method exception handler. See * {@link scala.tools.asm.MethodVisitor#visitTryCatchBlock}. @@ -397,6 +467,15 @@ public abstract class Printer { public abstract void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type); + /** + * Try catch block type annotation. See + * {@link scala.tools.asm.MethodVisitor#visitTryCatchAnnotation}. + */ + public Printer visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + /** * Method debug info. See * {@link scala.tools.asm.MethodVisitor#visitLocalVariable}. @@ -405,6 +484,16 @@ public abstract class Printer { final String desc, final String signature, final Label start, final Label end, final int index); + /** + * Local variable type annotation. See + * {@link scala.tools.asm.MethodVisitor#visitTryCatchAnnotation}. + */ + public Printer visitLocalVariableAnnotation(final int typeRef, + final TypePath typePath, final Label[] start, final Label[] end, + final int[] index, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + /** * Method debug info. See * {@link scala.tools.asm.MethodVisitor#visitLineNumber}. diff --git a/src/asm/scala/tools/asm/util/Textifier.java b/src/asm/scala/tools/asm/util/Textifier.java index a5c4f6779e..373e46f5ed 100644 --- a/src/asm/scala/tools/asm/util/Textifier.java +++ b/src/asm/scala/tools/asm/util/Textifier.java @@ -40,6 +40,8 @@ import scala.tools.asm.Handle; import scala.tools.asm.Label; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; import scala.tools.asm.signature.SignatureReader; /** @@ -135,15 +137,26 @@ public class Textifier extends Printer { */ protected Map labelNames; + /** + * Class access flags + */ + private int access; + private int valueNumber = 0; /** * Constructs a new {@link Textifier}. Subclasses must not use this * constructor. Instead, they must use the {@link #Textifier(int)} * version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public Textifier() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != Textifier.class) { + throw new IllegalStateException(); + } } /** @@ -151,7 +164,7 @@ public class Textifier extends Printer { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected Textifier(final int api) { super(api); @@ -208,6 +221,7 @@ public class Textifier extends Printer { public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { + this.access = access; int major = version & 0xFFFF; int minor = version >>> 16; buf.setLength(0); @@ -293,6 +307,13 @@ public class Textifier extends Printer { return visitAnnotation(desc, visible); } + @Override + public Printer visitClassTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + text.add("\n"); + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitClassAttribute(final Attribute attr) { text.add("\n"); @@ -393,7 +414,7 @@ public class Textifier extends Printer { } buf.append(tab); - appendAccess(access); + appendAccess(access & ~Opcodes.ACC_VOLATILE); if ((access & Opcodes.ACC_NATIVE) != 0) { buf.append("native "); } @@ -403,6 +424,11 @@ public class Textifier extends Printer { if ((access & Opcodes.ACC_BRIDGE) != 0) { buf.append("bridge "); } + if ((this.access & Opcodes.ACC_INTERFACE) != 0 + && (access & Opcodes.ACC_ABSTRACT) == 0 + && (access & Opcodes.ACC_STATIC) == 0) { + buf.append("default "); + } buf.append(name); appendDescriptor(METHOD_DESCRIPTOR, desc); @@ -616,6 +642,12 @@ public class Textifier extends Printer { return visitAnnotation(desc, visible); } + @Override + public Printer visitFieldTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitFieldAttribute(final Attribute attr) { visitAttribute(attr); @@ -629,6 +661,16 @@ public class Textifier extends Printer { // Methods // ------------------------------------------------------------------------ + @Override + public void visitParameter(final String name, final int access) { + buf.setLength(0); + buf.append(tab2).append("// parameter "); + appendAccess(access); + buf.append(' ').append((name == null) ? "" : name) + .append('\n'); + text.add(buf.toString()); + } + @Override public Textifier visitAnnotationDefault() { text.add(tab2 + "default="); @@ -644,6 +686,12 @@ public class Textifier extends Printer { return visitAnnotation(desc, visible); } + @Override + public Printer visitMethodTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public Textifier visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { @@ -761,9 +809,30 @@ public class Textifier extends Printer { text.add(buf.toString()); } + @Deprecated @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, + opcode == Opcodes.INVOKEINTERFACE); + } + + @Override + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, itf); + } + + private void doVisitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { buf.setLength(0); buf.append(tab2).append(OPCODES[opcode]).append(' '); appendDescriptor(INTERNAL_NAME, owner); @@ -781,26 +850,35 @@ public class Textifier extends Printer { buf.append(name); appendDescriptor(METHOD_DESCRIPTOR, desc); buf.append(" ["); + buf.append('\n'); + buf.append(tab3); appendHandle(bsm); + buf.append('\n'); buf.append(tab3).append("// arguments:"); if (bsmArgs.length == 0) { buf.append(" none"); } else { - buf.append('\n').append(tab3); + buf.append('\n'); for (int i = 0; i < bsmArgs.length; i++) { + buf.append(tab3); Object cst = bsmArgs[i]; if (cst instanceof String) { Printer.appendString(buf, (String) cst); } else if (cst instanceof Type) { - buf.append(((Type) cst).getDescriptor()).append(".class"); + Type type = (Type) cst; + if(type.getSort() == Type.METHOD){ + appendDescriptor(METHOD_DESCRIPTOR, type.getDescriptor()); + } else { + buf.append(type.getDescriptor()).append(".class"); + } } else if (cst instanceof Handle) { appendHandle((Handle) cst); } else { buf.append(cst); } - buf.append(", "); + buf.append(", \n"); } - buf.setLength(buf.length() - 2); + buf.setLength(buf.length() - 3); } buf.append('\n'); buf.append(tab2).append("]\n"); @@ -889,6 +967,12 @@ public class Textifier extends Printer { text.add(buf.toString()); } + @Override + public Printer visitInsnAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -905,6 +989,25 @@ public class Textifier extends Printer { text.add(buf.toString()); } + @Override + public Printer visitTryCatchAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + buf.setLength(0); + buf.append(tab2).append("TRYCATCHBLOCK @"); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -931,6 +1034,33 @@ public class Textifier extends Printer { text.add(buf.toString()); } + @Override + public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath, + Label[] start, Label[] end, int[] index, String desc, + boolean visible) { + buf.setLength(0); + buf.append(tab2).append("LOCALVARIABLE @"); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + for (int i = 0; i < start.length; ++i) { + buf.append(" [ "); + appendLabel(start[i]); + buf.append(" - "); + appendLabel(end[i]); + buf.append(" - ").append(index[i]).append(" ]"); + } + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + @Override public void visitLineNumber(final int line, final Label start) { buf.setLength(0); @@ -980,6 +1110,39 @@ public class Textifier extends Printer { return t; } + /** + * Prints a disassembled view of the given type annotation. + * + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values. + */ + public Textifier visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + buf.setLength(0); + buf.append(tab).append('@'); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + /** * Prints a disassembled view of the given attribute. * @@ -1061,10 +1224,10 @@ public class Textifier extends Printer { * a handle, non null. */ protected void appendHandle(final Handle h) { - buf.append('\n').append(tab3); int tag = h.getTag(); buf.append("// handle kind 0x").append(Integer.toHexString(tag)) .append(" : "); + boolean isMethodHandle = false; switch (tag) { case Opcodes.H_GETFIELD: buf.append("GETFIELD"); @@ -1080,18 +1243,23 @@ public class Textifier extends Printer { break; case Opcodes.H_INVOKEINTERFACE: buf.append("INVOKEINTERFACE"); + isMethodHandle = true; break; case Opcodes.H_INVOKESPECIAL: buf.append("INVOKESPECIAL"); + isMethodHandle = true; break; case Opcodes.H_INVOKESTATIC: buf.append("INVOKESTATIC"); + isMethodHandle = true; break; case Opcodes.H_INVOKEVIRTUAL: buf.append("INVOKEVIRTUAL"); + isMethodHandle = true; break; case Opcodes.H_NEWINVOKESPECIAL: buf.append("NEWINVOKESPECIAL"); + isMethodHandle = true; break; } buf.append('\n'); @@ -1099,9 +1267,13 @@ public class Textifier extends Printer { appendDescriptor(INTERNAL_NAME, h.getOwner()); buf.append('.'); buf.append(h.getName()); - buf.append('('); + if(!isMethodHandle){ + buf.append('('); + } appendDescriptor(HANDLE_DESCRIPTOR, h.getDesc()); - buf.append(')').append('\n'); + if(!isMethodHandle){ + buf.append(')'); + } } /** @@ -1145,6 +1317,9 @@ public class Textifier extends Printer { if ((access & Opcodes.ACC_SYNTHETIC) != 0) { buf.append("synthetic "); } + if ((access & Opcodes.ACC_MANDATED) != 0) { + buf.append("mandated "); + } if ((access & Opcodes.ACC_ENUM) != 0) { buf.append("enum "); } @@ -1156,6 +1331,90 @@ public class Textifier extends Printer { } } + private void appendTypeReference(final int typeRef) { + TypeReference ref = new TypeReference(typeRef); + switch (ref.getSort()) { + case TypeReference.CLASS_TYPE_PARAMETER: + buf.append("CLASS_TYPE_PARAMETER ").append( + ref.getTypeParameterIndex()); + break; + case TypeReference.METHOD_TYPE_PARAMETER: + buf.append("METHOD_TYPE_PARAMETER ").append( + ref.getTypeParameterIndex()); + break; + case TypeReference.CLASS_EXTENDS: + buf.append("CLASS_EXTENDS ").append(ref.getSuperTypeIndex()); + break; + case TypeReference.CLASS_TYPE_PARAMETER_BOUND: + buf.append("CLASS_TYPE_PARAMETER_BOUND ") + .append(ref.getTypeParameterIndex()).append(", ") + .append(ref.getTypeParameterBoundIndex()); + break; + case TypeReference.METHOD_TYPE_PARAMETER_BOUND: + buf.append("METHOD_TYPE_PARAMETER_BOUND ") + .append(ref.getTypeParameterIndex()).append(", ") + .append(ref.getTypeParameterBoundIndex()); + break; + case TypeReference.FIELD: + buf.append("FIELD"); + break; + case TypeReference.METHOD_RETURN: + buf.append("METHOD_RETURN"); + break; + case TypeReference.METHOD_RECEIVER: + buf.append("METHOD_RECEIVER"); + break; + case TypeReference.METHOD_FORMAL_PARAMETER: + buf.append("METHOD_FORMAL_PARAMETER ").append( + ref.getFormalParameterIndex()); + break; + case TypeReference.THROWS: + buf.append("THROWS ").append(ref.getExceptionIndex()); + break; + case TypeReference.LOCAL_VARIABLE: + buf.append("LOCAL_VARIABLE"); + break; + case TypeReference.RESOURCE_VARIABLE: + buf.append("RESOURCE_VARIABLE"); + break; + case TypeReference.EXCEPTION_PARAMETER: + buf.append("EXCEPTION_PARAMETER ").append( + ref.getTryCatchBlockIndex()); + break; + case TypeReference.INSTANCEOF: + buf.append("INSTANCEOF"); + break; + case TypeReference.NEW: + buf.append("NEW"); + break; + case TypeReference.CONSTRUCTOR_REFERENCE: + buf.append("CONSTRUCTOR_REFERENCE"); + break; + case TypeReference.METHOD_REFERENCE: + buf.append("METHOD_REFERENCE"); + break; + case TypeReference.CAST: + buf.append("CAST ").append(ref.getTypeArgumentIndex()); + break; + case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + buf.append("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: + buf.append("METHOD_INVOCATION_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + buf.append("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: + buf.append("METHOD_REFERENCE_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + } + } + private void appendFrameTypes(final int n, final Object[] o) { for (int i = 0; i < n; ++i) { if (i > 0) { diff --git a/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java b/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java index 33e7cf0b26..7a9dbfef06 100644 --- a/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java @@ -47,7 +47,7 @@ public final class TraceAnnotationVisitor extends AnnotationVisitor { } public TraceAnnotationVisitor(final AnnotationVisitor av, final Printer p) { - super(Opcodes.ASM4, av); + super(Opcodes.ASM5, av); this.p = p; } diff --git a/src/asm/scala/tools/asm/util/TraceClassVisitor.java b/src/asm/scala/tools/asm/util/TraceClassVisitor.java index ff7a017482..842d286672 100644 --- a/src/asm/scala/tools/asm/util/TraceClassVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceClassVisitor.java @@ -37,6 +37,7 @@ import scala.tools.asm.ClassVisitor; import scala.tools.asm.FieldVisitor; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A {@link ClassVisitor} that prints the classes it visits with a @@ -130,7 +131,7 @@ public final class TraceClassVisitor extends ClassVisitor { */ public TraceClassVisitor(final ClassVisitor cv, final Printer p, final PrintWriter pw) { - super(Opcodes.ASM4, cv); + super(Opcodes.ASM5, cv); this.pw = pw; this.p = p; } @@ -165,6 +166,16 @@ public final class TraceClassVisitor extends ClassVisitor { return new TraceAnnotationVisitor(av, p); } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitClassTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = cv == null ? null : cv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitAttribute(final Attribute attr) { p.visitClassAttribute(attr); diff --git a/src/asm/scala/tools/asm/util/TraceFieldVisitor.java b/src/asm/scala/tools/asm/util/TraceFieldVisitor.java index 9547a70008..1d0743a424 100644 --- a/src/asm/scala/tools/asm/util/TraceFieldVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceFieldVisitor.java @@ -33,6 +33,7 @@ import scala.tools.asm.AnnotationVisitor; import scala.tools.asm.Attribute; import scala.tools.asm.FieldVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A {@link FieldVisitor} that prints the fields it visits with a @@ -49,7 +50,7 @@ public final class TraceFieldVisitor extends FieldVisitor { } public TraceFieldVisitor(final FieldVisitor fv, final Printer p) { - super(Opcodes.ASM4, fv); + super(Opcodes.ASM5, fv); this.p = p; } @@ -62,6 +63,16 @@ public final class TraceFieldVisitor extends FieldVisitor { return new TraceAnnotationVisitor(av, p); } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitFieldTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = fv == null ? null : fv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitAttribute(final Attribute attr) { p.visitFieldAttribute(attr); diff --git a/src/asm/scala/tools/asm/util/TraceMethodVisitor.java b/src/asm/scala/tools/asm/util/TraceMethodVisitor.java index 9034567c8f..db5f051003 100644 --- a/src/asm/scala/tools/asm/util/TraceMethodVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceMethodVisitor.java @@ -35,6 +35,7 @@ import scala.tools.asm.Handle; import scala.tools.asm.Label; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A {@link MethodVisitor} that prints the methods it visits with a @@ -51,10 +52,16 @@ public final class TraceMethodVisitor extends MethodVisitor { } public TraceMethodVisitor(final MethodVisitor mv, final Printer p) { - super(Opcodes.ASM4, mv); + super(Opcodes.ASM5, mv); this.p = p; } + @Override + public void visitParameter(String name, int access) { + p.visitParameter(name, access); + super.visitParameter(name, access); + } + @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { @@ -64,6 +71,16 @@ public final class TraceMethodVisitor extends MethodVisitor { return new TraceAnnotationVisitor(av, p); } + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitMethodTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = mv == null ? null : mv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitAttribute(final Attribute attr) { p.visitMethodAttribute(attr); @@ -130,11 +147,31 @@ public final class TraceMethodVisitor extends MethodVisitor { super.visitFieldInsn(opcode, owner, name, desc); } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } p.visitMethodInsn(opcode, owner, name, desc); - super.visitMethodInsn(opcode, owner, name, desc); + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + p.visitMethodInsn(opcode, owner, name, desc, itf); + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } } @Override @@ -188,6 +225,16 @@ public final class TraceMethodVisitor extends MethodVisitor { super.visitMultiANewArrayInsn(desc, dims); } + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p + .visitInsnAnnotation(typeRef, typePath, desc, visible); + AnnotationVisitor av = mv == null ? null : mv.visitInsnAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { @@ -195,6 +242,16 @@ public final class TraceMethodVisitor extends MethodVisitor { super.visitTryCatchBlock(start, end, handler, type); } + @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitTryCatchAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = mv == null ? null : mv.visitTryCatchAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, @@ -203,6 +260,18 @@ public final class TraceMethodVisitor extends MethodVisitor { super.visitLocalVariable(name, desc, signature, start, end, index); } + @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + Printer p = this.p.visitLocalVariableAnnotation(typeRef, typePath, + start, end, index, desc, visible); + AnnotationVisitor av = mv == null ? null : mv + .visitLocalVariableAnnotation(typeRef, typePath, start, end, + index, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + @Override public void visitLineNumber(final int line, final Label start) { p.visitLineNumber(line, start); diff --git a/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java b/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java index 1e23c7ef1a..f99ec2b0c2 100644 --- a/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java @@ -75,13 +75,13 @@ public final class TraceSignatureVisitor extends SignatureVisitor { private String separator = ""; public TraceSignatureVisitor(final int access) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); isInterface = (access & Opcodes.ACC_INTERFACE) != 0; this.declaration = new StringBuffer(); } private TraceSignatureVisitor(final StringBuffer buf) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); this.declaration = buf; } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 1ede914288..3fdb38ce0e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -824,7 +824,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { index += jparamType.getSize } - mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, asmMethodType(m).getDescriptor) + mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, asmMethodType(m).getDescriptor, false) mirrorMethod.visitInsn(jReturnType.getOpcode(asm.Opcodes.IRETURN)) mirrorMethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments @@ -1122,7 +1122,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { // invoke the superclass constructor, which will do the // necessary java reflection and create Method objects. - constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor) + constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor, false) constructor.visitInsn(asm.Opcodes.RETURN) constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments @@ -1187,7 +1187,8 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { asm.Opcodes.INVOKEVIRTUAL, moduleName, "CREATOR", - bt.getDescriptor + bt.getDescriptor, + false ) // PUTSTATIC `thisName`.CREATOR; diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index c3492b79a9..f9f6e7c3ff 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -409,19 +409,19 @@ abstract class BCodeIdiomatic extends BCodeGlue { // can-multi-thread final def invokespecial(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false) } // can-multi-thread final def invokestatic(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false) } // can-multi-thread final def invokeinterface(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, true) } // can-multi-thread final def invokevirtual(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false) } // can-multi-thread diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index ee9be5b11c..a76fa4d7ba 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -234,7 +234,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { if (isCZStaticModule) { clinit.visitTypeInsn(asm.Opcodes.NEW, thisName) clinit.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, - thisName, INSTANCE_CONSTRUCTOR_NAME, "()V") + thisName, INSTANCE_CONSTRUCTOR_NAME, "()V", false) } if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisName) } clinit.visitInsn(asm.Opcodes.RETURN) @@ -686,7 +686,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val jname = callee.javaSimpleName.toString val jowner = internalName(callee.owner) val jtype = asmMethodType(callee).getDescriptor - insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype) + insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype, false) } var insnParcA: asm.tree.AbstractInsnNode = null @@ -707,7 +707,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val jowner = internalName(callee.owner) val jname = callee.javaSimpleName.toString val jtype = asmMethodType(callee).getDescriptor - insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype) + insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype, false) // PUTSTATIC `thisName`.CREATOR; insnParcB = new asm.tree.FieldInsnNode(asm.Opcodes.PUTSTATIC, thisName, "CREATOR", andrFieldDescr) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index b7f9b30e19..13a5c6413d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -975,7 +975,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { index += jparamType.getSize() } - mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, javaType(m).getDescriptor) + mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, javaType(m).getDescriptor, false) mirrorMethod.visitInsn(jReturnType.getOpcode(asm.Opcodes.IRETURN)) mirrorMethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments @@ -1061,7 +1061,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { asm.Opcodes.INVOKEVIRTUAL, moduleName, androidFieldName.toString, - asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*) + asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*), + false ) // PUTSTATIC `thisName`.CREATOR; @@ -1521,7 +1522,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { if (isStaticModule(clasz.symbol)) { clinit.visitTypeInsn(asm.Opcodes.NEW, thisName) clinit.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, - thisName, INSTANCE_CONSTRUCTOR_NAME, mdesc_arglessvoid) + thisName, INSTANCE_CONSTRUCTOR_NAME, mdesc_arglessvoid, false) } if (isParcelableClass) { legacyAddCreatorCode(clinit) } @@ -1665,16 +1666,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def rem(tk: TypeKind) { emitPrimitive(remOpcodes, tk) } def invokespecial(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false) } def invokestatic(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false) } def invokeinterface(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, true) } def invokevirtual(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false) } def goTo(label: asm.Label) { jmethod.visitJumpInsn(Opcodes.GOTO, label) } @@ -2924,7 +2925,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // invoke the superclass constructor, which will do the // necessary java reflection and create Method objects. - constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor) + constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor, false) constructor.visitInsn(asm.Opcodes.RETURN) constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments diff --git a/src/partest-javaagent/scala/tools/partest/javaagent/ProfilerVisitor.java b/src/partest-javaagent/scala/tools/partest/javaagent/ProfilerVisitor.java index b1b100fbb0..d97756c171 100644 --- a/src/partest-javaagent/scala/tools/partest/javaagent/ProfilerVisitor.java +++ b/src/partest-javaagent/scala/tools/partest/javaagent/ProfilerVisitor.java @@ -50,7 +50,7 @@ public class ProfilerVisitor extends ClassVisitor implements Opcodes { mv.visitLdcInsn(name); mv.visitLdcInsn(desc); mv.visitMethodInsn(INVOKESTATIC, profilerClass, "methodCalled", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false); } } return mv; diff --git a/test/files/run/classfile-format-51.scala b/test/files/run/classfile-format-51.scala index f92382d89b..24b1ee8397 100644 --- a/test/files/run/classfile-format-51.scala +++ b/test/files/run/classfile-format-51.scala @@ -32,7 +32,7 @@ object Test extends DirectTest { val constructor = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null) constructor.visitCode() constructor.visitVarInsn(ALOAD, 0) - constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V") + constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false) constructor.visitInsn(RETURN) constructor.visitMaxs(1, 1) constructor.visitEnd() @@ -47,19 +47,19 @@ object Test extends DirectTest { val bootstrap = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, bootstrapMethodName, bootStrapMethodType, null, null) bootstrap.visitCode() // val lookup = MethodHandles.lookup(); - bootstrap.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;") + bootstrap.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false) bootstrap.visitVarInsn(ASTORE, 3) // lookup // val clazz = lookup.lookupClass(); bootstrap.visitVarInsn(ALOAD, 3) // lookup - bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;") + bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;", false) bootstrap.visitVarInsn(ASTORE, 4) // clazz // val methodType = MethodType.fromMethodDescriptorString("()Ljava/lang/String, clazz.getClassLoader()") bootstrap.visitLdcInsn("()Ljava/lang/String;") bootstrap.visitVarInsn(ALOAD, 4) // CLAZZ - bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;") - bootstrap.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;") + bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) + bootstrap.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", false) bootstrap.visitVarInsn(ASTORE, 5) // methodType // val methodHandle = lookup.findStatic(thisClass, "target", methodType) @@ -67,14 +67,14 @@ object Test extends DirectTest { bootstrap.visitVarInsn(ALOAD, 4) // clazz bootstrap.visitLdcInsn("target") bootstrap.visitVarInsn(ALOAD, 5) // methodType - bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;") + bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false) bootstrap.visitVarInsn(ASTORE, 6) // methodHandle // new ConstantCallSite(methodHandle) bootstrap.visitTypeInsn(NEW, "java/lang/invoke/ConstantCallSite") bootstrap.visitInsn(DUP) bootstrap.visitVarInsn(ALOAD, 6) // methodHandle - bootstrap.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V") + bootstrap.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false) bootstrap.visitInsn(ARETURN) bootstrap.visitMaxs(4,7) bootstrap.visitEnd() diff --git a/test/files/run/large_class.check b/test/files/run/large_class.check new file mode 100644 index 0000000000..0585c267ac --- /dev/null +++ b/test/files/run/large_class.check @@ -0,0 +1,3 @@ +newSource1.scala:1: error: Could not write class BigEnoughToFail because it exceeds JVM code size limits. Class file too large! +class BigEnoughToFail { + ^ diff --git a/test/files/run/large_class.scala b/test/files/run/large_class.scala new file mode 100644 index 0000000000..aa486ef8f7 --- /dev/null +++ b/test/files/run/large_class.scala @@ -0,0 +1,27 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +// a cold run of partest takes about 15s for this test on my laptop +object Test extends DirectTest { + override def extraSettings: String = "-usejavacp -d " + testOutput.path + + def s(n: Int) = "\""+n+"\"" + + override def code + = s""" + |class BigEnoughToFail { + | def m(a: String, b: String, c: String, d: String, e: String, f: String) = null + | ${(1 to 5500) map (n => "def f"+n+" = m("+ s(n+10000)+","+ + s(n+20000)+","+ + s(n+30000)+","+ + s(n+40000)+","+ + s(n+50000)+","+ + s(n+60000)+")") mkString ";"} + |}""".stripMargin.trim + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} -- cgit v1.2.3