diff options
author | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2013-01-30 17:12:41 -0800 |
---|---|---|
committer | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2013-01-30 17:12:41 -0800 |
commit | c4f49759fe8f15e25174232abe566ad292d2e5e8 (patch) | |
tree | 9750714c8373d9b1d22a5a4cdb4c0caef905fa3c | |
parent | d24f341f081aef296d174ea54d0579976eeaae98 (diff) | |
parent | 8610d7ec063407f62b11df848dd588e4594b3b40 (diff) | |
download | scala-c4f49759fe8f15e25174232abe566ad292d2e5e8.tar.gz scala-c4f49759fe8f15e25174232abe566ad292d2e5e8.tar.bz2 scala-c4f49759fe8f15e25174232abe566ad292d2e5e8.zip |
Merge pull request #2014 from gkossakowski/bytecode-testing
Add Bytecode test (ASM-based) to partest.
-rw-r--r-- | build.xml | 1 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/BytecodeTest.scala | 61 | ||||
-rw-r--r-- | test/files/jvm/bytecode-test-example.check | 1 | ||||
-rw-r--r-- | test/files/jvm/bytecode-test-example/Foo_1.scala | 9 | ||||
-rw-r--r-- | test/files/jvm/bytecode-test-example/Test.scala | 32 |
5 files changed, 104 insertions, 0 deletions
@@ -1377,6 +1377,7 @@ QUICK BUILD (QUICK) <pathelement location="${ant.jar}"/> <path refid="forkjoin.classpath"/> <path refid="fjbg.classpath"/> + <path refid="asm.classpath"/> <pathelement location="${scalacheck.jar}"/> </compilationpath> </scalacfork> diff --git a/src/partest/scala/tools/partest/BytecodeTest.scala b/src/partest/scala/tools/partest/BytecodeTest.scala new file mode 100644 index 0000000000..93183c2095 --- /dev/null +++ b/src/partest/scala/tools/partest/BytecodeTest.scala @@ -0,0 +1,61 @@ +package scala.tools.partest + +import scala.tools.nsc.util.JavaClassPath +import scala.collection.JavaConverters._ +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, MethodNode, InsnList} +import java.io.InputStream + +/** + * Providies utilities for inspecting bytecode using ASM library. + * + * HOW TO USE + * 1. Create subdirectory in test/files/jvm for your test. Let's name it $TESTDIR. + * 2. Create $TESTDIR/BytecodeSrc_1.scala that contains Scala source file which you + * want to inspect the bytecode for. The '_1' suffix signals to partest that it + * should compile this file first. + * 3. Create $TESTDIR/Test.scala: + * import scala.tools.partest.BytecodeTest + * object Test extends BytecodeTest { + * def show { + * // your code that inspect ASM trees and prints values + * } + * } + * 4. Create corresponding check file. + * + * EXAMPLE + * See test/files/jvm/bytecode-test-example for an example of bytecode test. + * + */ +abstract class BytecodeTest { + + /** produce the output to be compared against a checkfile */ + protected def show(): Unit + + def main(args: Array[String]): Unit = show + + protected def getMethod(classNode: ClassNode, name: String): MethodNode = + classNode.methods.asScala.find(_.name == name) getOrElse + sys.error(s"Didn't find method '$name' in class '${classNode.name}'") + + protected def loadClassNode(name: String): ClassNode = { + val classBytes: InputStream = (for { + classRep <- classpath.findClass(name) + binary <- classRep.binary + } yield binary.input) getOrElse sys.error(s"failed to load class '$name'; classpath = $classpath") + + val cr = new ClassReader(classBytes) + val cn = new ClassNode() + cr.accept(cn, 0) + cn + } + + protected lazy val classpath: JavaClassPath = { + import scala.tools.nsc.util.ClassPath.DefaultJavaContext + import scala.tools.util.PathResolver.Defaults + // logic inspired by scala.tools.util.PathResolver implementation + val containers = DefaultJavaContext.classesInExpandedPath(Defaults.javaUserClassPath) + new JavaClassPath(containers, DefaultJavaContext) + } +} diff --git a/test/files/jvm/bytecode-test-example.check b/test/files/jvm/bytecode-test-example.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/jvm/bytecode-test-example.check @@ -0,0 +1 @@ +2 diff --git a/test/files/jvm/bytecode-test-example/Foo_1.scala b/test/files/jvm/bytecode-test-example/Foo_1.scala new file mode 100644 index 0000000000..4f679d156f --- /dev/null +++ b/test/files/jvm/bytecode-test-example/Foo_1.scala @@ -0,0 +1,9 @@ +class Foo_1 { + def foo(x: AnyRef): Int = { + val bool = x == null + if (x != null) + 1 + else + 0 + } +} diff --git a/test/files/jvm/bytecode-test-example/Test.scala b/test/files/jvm/bytecode-test-example/Test.scala new file mode 100644 index 0000000000..d668059cb7 --- /dev/null +++ b/test/files/jvm/bytecode-test-example/Test.scala @@ -0,0 +1,32 @@ +import scala.tools.partest.BytecodeTest + +import scala.tools.nsc.util.JavaClassPath +import java.io.InputStream +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, InsnList} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("Foo_1") + val methodNode = getMethod(classNode, "foo") + println(countNullChecks(methodNode.instructions)) + } + + def countNullChecks(insnList: InsnList): Int = { + /** Is given instruction a null check? + * NOTE + * This will detect direct null compparsion as in + * if (x == null) ... + * and not indirect as in + * val foo = null + * if (x == foo) ... + */ + def isNullCheck(node: asm.tree.AbstractInsnNode): Boolean = { + val opcode = node.getOpcode + (opcode == asm.Opcodes.IFNULL) || (opcode == asm.Opcodes.IFNONNULL) + } + insnList.iterator.asScala.count(isNullCheck) + } +} |