diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-01-30 14:57:05 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-01-31 11:00:42 -0800 |
commit | a07555f015939412c6e4421860abce6e5f90f710 (patch) | |
tree | 08414eca725a1a1fbcbf356f4c2cd3a8b8f31162 /src | |
parent | 42c4cc7a1eed222a1593c6ac2652cd5357c2897a (diff) | |
download | scala-a07555f015939412c6e4421860abce6e5f90f710.tar.gz scala-a07555f015939412c6e4421860abce6e5f90f710.tar.bz2 scala-a07555f015939412c6e4421860abce6e5f90f710.zip |
bytecode diffing support in ByteCodeTest
use sameByteCode(methodNode1, methodNode2) to check methods
compile to identical bytecode (line number info is not taken into account)
Diffstat (limited to 'src')
-rw-r--r-- | src/partest/scala/tools/partest/BytecodeTest.scala | 77 |
1 files changed, 73 insertions, 4 deletions
diff --git a/src/partest/scala/tools/partest/BytecodeTest.scala b/src/partest/scala/tools/partest/BytecodeTest.scala index 93183c2095..1f43ee7d2e 100644 --- a/src/partest/scala/tools/partest/BytecodeTest.scala +++ b/src/partest/scala/tools/partest/BytecodeTest.scala @@ -8,11 +8,11 @@ import asm.tree.{ClassNode, MethodNode, InsnList} import java.io.InputStream /** - * Providies utilities for inspecting bytecode using ASM library. + * Provides 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 + * 2. Create $TESTDIR/BytecodeSrc_1.scala that contains Scala source file that 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: @@ -35,11 +35,23 @@ abstract class BytecodeTest { def main(args: Array[String]): Unit = show +// asserts + def sameBytecode(methA: MethodNode, methB: MethodNode) = { + val isa = instructions.fromMethod(methA) + val isb = instructions.fromMethod(methB) + if (isa == isb) println("bytecode identical") + else (isa, isb).zipped.foreach { case (a, b) => + if (a == b) println("OK : "+ a) + else println("DIFF: "+ a +" <=> "+ b) + } + } + +// loading 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 = { + protected def loadClassNode(name: String, skipDebugInfo: Boolean = true): ClassNode = { val classBytes: InputStream = (for { classRep <- classpath.findClass(name) binary <- classRep.binary @@ -47,7 +59,7 @@ abstract class BytecodeTest { val cr = new ClassReader(classBytes) val cn = new ClassNode() - cr.accept(cn, 0) + cr.accept(cn, if (skipDebugInfo) ClassReader.SKIP_DEBUG else 0) cn } @@ -58,4 +70,61 @@ abstract class BytecodeTest { val containers = DefaultJavaContext.classesInExpandedPath(Defaults.javaUserClassPath) new JavaClassPath(containers, DefaultJavaContext) } + + // wrap ASM's instructions so we get case class-style `equals` and `toString` + object instructions { + def fromMethod(meth: MethodNode): List[instructions.Instruction] = { + val insns = meth.instructions + val asmToScala = new AsmToScala{ def labelIndex(l: asm.tree.AbstractInsnNode) = insns.indexOf(l) } + + asmToScala.mapOver(insns.iterator.asScala.toList).asInstanceOf[List[instructions.Instruction]] + } + + sealed abstract class Instruction { def opcode: Int } + case class Field (opcode: Int, desc: String, name: String, owner: String) extends Instruction + case class Incr (opcode: Int, incr: Int, `var`: Int) extends Instruction + case class Op (opcode: Int) extends Instruction + case class IntOp (opcode: Int, operand: Int) extends Instruction + case class Jump (opcode: Int, label: Label) extends Instruction + case class Ldc (opcode: Int, cst: Any) extends Instruction + case class LookupSwitch (opcode: Int, dflt: Label, keys: List[Integer], labels: List[Label]) extends Instruction + case class TableSwitch (opcode: Int, dflt: Label, max: Int, min: Int, labels: List[Label]) extends Instruction + case class Method (opcode: Int, desc: String, name: String, owner: String) extends Instruction + case class NewArray (opcode: Int, desc: String, dims: Int) extends Instruction + case class TypeOp (opcode: Int, desc: String) extends Instruction + case class VarOp (opcode: Int, `var`: Int) extends Instruction + case class Label (opcode: Int, offset: Int) extends Instruction + case class FrameEntry (opcode: Int, local: List[Any], stack: List[Any]) extends Instruction + case class LineNumber (opcode: Int, line: Int, start: Label) extends Instruction + } + + abstract class AsmToScala { + import instructions._ + def labelIndex(l: asm.tree.AbstractInsnNode): Int + + def mapOver(is: List[Any]): List[Any] = is map { + case i: asm.tree.AbstractInsnNode => apply(i) + case x => x + } + + def lst[T](xs: java.util.List[T]): List[T] = if (xs == null) Nil else xs.asScala.toList + def apply(l: asm.tree.LabelNode): Label = this(l: asm.tree.AbstractInsnNode).asInstanceOf[Label] + def apply(x: asm.tree.AbstractInsnNode): Instruction = x match { + case i: asm.tree.FieldInsnNode => Field (i.getOpcode: Int, i.desc: String, i.name: String, i.owner: String) + case i: asm.tree.IincInsnNode => Incr (i.getOpcode: Int, i.incr: Int, i.`var`: Int) + case i: asm.tree.InsnNode => Op (i.getOpcode: Int) + case i: asm.tree.IntInsnNode => IntOp (i.getOpcode: Int, i.operand: Int) + case i: asm.tree.JumpInsnNode => Jump (i.getOpcode: Int, this(i.label)) + case i: asm.tree.LdcInsnNode => Ldc (i.getOpcode: Int, i.cst: Any) + case i: asm.tree.LookupSwitchInsnNode => LookupSwitch (i.getOpcode: Int, this(i.dflt), lst(i.keys), mapOver(lst(i.labels)).asInstanceOf[List[Label]]) + case i: asm.tree.TableSwitchInsnNode => TableSwitch (i.getOpcode: Int, this(i.dflt), i.max: Int, i.min: Int, mapOver(lst(i.labels)).asInstanceOf[List[Label]]) + case i: asm.tree.MethodInsnNode => Method (i.getOpcode: Int, i.desc: String, i.name: String, i.owner: String) + case i: asm.tree.MultiANewArrayInsnNode => NewArray (i.getOpcode: Int, i.desc: String, i.dims: Int) + case i: asm.tree.TypeInsnNode => TypeOp (i.getOpcode: Int, i.desc: String) + case i: asm.tree.VarInsnNode => VarOp (i.getOpcode: Int, i.`var`: Int) + case i: asm.tree.LabelNode => Label (i.getOpcode: Int, labelIndex(x)) + case i: asm.tree.FrameNode => FrameEntry (i.getOpcode: Int, mapOver(lst(i.local)), mapOver(lst(i.stack))) + case i: asm.tree.LineNumberNode => LineNumber (i.getOpcode: Int, i.line: Int, this(i.start): Label) + } + } } |