Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Test for reading JDK 8 (classfile format 52) class files.

This commit includes a test for reading JDK 8 (classfile format 52)
class files, in particular default (aka defender) methods. It uses ASM
to generate an interface with default methods then exercises that
interface from Scala.

Surprisingly no changes are necessary to the Scala code base to support
reading format 52 class files.

Because the test can only run under JDK 8, the JDK version is checked
and the expected output is synthesized for previous versions.
  • Loading branch information...
commit fc6da8d8b765ddc3c492d0884164561ca7a8b4d8 1 parent 9eb63c5
James Iry authored
View
2  test/files/run/classfile-format-52.check
@@ -0,0 +1,2 @@
+hello from publicMethod
+hello from staticMethod
View
80 test/files/run/classfile-format-52.scala
@@ -0,0 +1,80 @@
+import java.io.{File, FileOutputStream}
+
+import scala.tools.nsc.settings.ScalaVersion
+import scala.tools.partest._
+import scala.tools.asm
+import asm.{AnnotationVisitor, ClassWriter, FieldVisitor, Handle, MethodVisitor, Opcodes}
+import Opcodes._
+
+// This test ensures that we can read JDK 8 (classfile format 52) files, including those
+// with default methods. To do that it first uses ASM to generate an interface called
+// HasDefaultMethod. Then it runs a normal compile on Scala source that extends that
+// interface. Any failure will be dumped to std out.
+//
+// By it's nature the test can only work on JDK 8+ because under JDK 7- the
+// interface won't verify.
+object Test extends DirectTest {
+ override def extraSettings: String = "-optimise -usejavacp -d " + testOutput.path + " -cp " + testOutput.path
+
+ def generateInterface() {
+ val interfaceName = "HasDefaultMethod"
+ val methodType = "()Ljava/lang/String;"
+
+ val cw = new ClassWriter(0)
+ cw.visit(52, ACC_PUBLIC+ACC_ABSTRACT+ACC_INTERFACE, interfaceName, null, "java/lang/Object", null)
+
+ def createMethod(flags:Int, name: String) {
+ val method = cw.visitMethod(flags, name, methodType, null, null)
+ method.visitCode()
+ method.visitLdcInsn(s"hello from $name")
+ method.visitInsn(ARETURN)
+ method.visitMaxs(1, 1)
+ method.visitEnd()
+ }
+
+ createMethod(ACC_PUBLIC, "publicMethod")
+ createMethod(ACC_PUBLIC+ACC_STATIC, "staticMethod")
+ createMethod(ACC_PRIVATE, "privateMethod")
+
+ cw.visitEnd()
+ val bytes = cw.toByteArray()
+
+ val fos = new FileOutputStream(new File(s"${testOutput.path}/$interfaceName.class"))
+ try
+ fos write bytes
+ finally
+ fos.close()
+
+ }
+
+ def code =
+"""
+class Driver extends HasDefaultMethod {
+ println(publicMethod())
+ println(HasDefaultMethod.staticMethod())
+}
+"""
+
+ override def show(): Unit = {
+ // redirect err to out, for logging
+ val prevErr = System.err
+ System.setErr(System.out)
+ try {
+ // this test is only valid under JDK 1.8+
+ // cheat a little by using 'ScalaVersion' because it can parse java versions just as well
+ val requiredJavaVersion = ScalaVersion("1.8")
+ val executingJavaVersion = ScalaVersion(System.getProperty("java.specification.version"))
+ if (executingJavaVersion >= requiredJavaVersion) {
+ generateInterface()
+ compile()
+ Class.forName("Driver").newInstance()
+ } else {
+ // under other versions just dump the expected results
+ println("hello from publicMethod")
+ println("hello from staticMethod")
+ }
+ }
+ finally
+ System.setErr(prevErr)
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.