Skip to content

Commit

Permalink
Expanded bytecode testing code.
Browse files Browse the repository at this point in the history
def sameMethodAndFieldSignatures compares two classes to verify
they have all the same methods and fields, and no others.
  • Loading branch information
paulp committed Feb 9, 2013
1 parent 6537d79 commit 22341e7
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 3 deletions.
60 changes: 60 additions & 0 deletions src/partest/scala/tools/partest/AsmNode.scala
@@ -0,0 +1,60 @@
package scala.tools.partest

import scala.collection.JavaConverters._
import scala.tools.asm
import asm._
import asm.tree._
import java.lang.reflect.Modifier

sealed trait AsmNode[+T] {
def node: T
def access: Int
def desc: String
def name: String
def signature: String
def attrs: List[Attribute]
def visibleAnnotations: List[AnnotationNode]
def invisibleAnnotations: List[AnnotationNode]
def characteristics = f"$name%15s $desc%-30s$accessString$sigString"

private def accessString = if (access == 0) "" else " " + Modifier.toString(access)
private def sigString = if (signature == null) "" else " " + signature
override def toString = characteristics
}

object AsmNode {
type AsmMethod = AsmNode[MethodNode]
type AsmField = AsmNode[FieldNode]
type AsmMember = AsmNode[_]

implicit class ClassNodeOps(val node: ClassNode) {
def fieldsAndMethods: List[AsmMember] = {
val xs: List[AsmMember] = (
node.methods.asScala.toList.map(x => (x: AsmMethod))
++ node.fields.asScala.toList.map(x => (x: AsmField))
)
xs sortBy (_.characteristics)
}
}
implicit class AsmMethodNode(val node: MethodNode) extends AsmNode[MethodNode] {
def access: Int = node.access
def desc: String = node.desc
def name: String = node.name
def signature: String = node.signature
def attrs: List[Attribute] = node.attrs.asScala.toList
def visibleAnnotations: List[AnnotationNode] = node.visibleAnnotations.asScala.toList
def invisibleAnnotations: List[AnnotationNode] = node.invisibleAnnotations.asScala.toList
}
implicit class AsmFieldNode(val node: FieldNode) extends AsmNode[FieldNode] {
def access: Int = node.access
def desc: String = node.desc
def name: String = node.name
def signature: String = node.signature
def attrs: List[Attribute] = node.attrs.asScala.toList
def visibleAnnotations: List[AnnotationNode] = node.visibleAnnotations.asScala.toList
def invisibleAnnotations: List[AnnotationNode] = node.invisibleAnnotations.asScala.toList
}

def apply(node: MethodNode): AsmMethodNode = new AsmMethodNode(node)
def apply(node: FieldNode): AsmFieldNode = new AsmFieldNode(node)
}
33 changes: 30 additions & 3 deletions src/partest/scala/tools/partest/BytecodeTest.scala
Expand Up @@ -3,9 +3,10 @@ package scala.tools.partest
import scala.tools.nsc.util.JavaClassPath import scala.tools.nsc.util.JavaClassPath
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
import scala.tools.asm import scala.tools.asm
import asm.ClassReader import asm.{ ClassReader }
import asm.tree.{ClassNode, MethodNode, InsnList} import asm.tree.{ClassNode, MethodNode, InsnList}
import java.io.InputStream import java.io.InputStream
import AsmNode._


/** /**
* Provides utilities for inspecting bytecode using ASM library. * Provides utilities for inspecting bytecode using ASM library.
Expand All @@ -29,21 +30,47 @@ import java.io.InputStream
* *
*/ */
abstract class BytecodeTest extends ASMConverters { abstract class BytecodeTest extends ASMConverters {
import instructions._


/** produce the output to be compared against a checkfile */ /** produce the output to be compared against a checkfile */
protected def show(): Unit protected def show(): Unit


def main(args: Array[String]): Unit = show def main(args: Array[String]): Unit = show


// asserts // asserts
def sameBytecode(methA: MethodNode, methB: MethodNode) = { def sameBytecode(methA: MethodNode, methB: MethodNode) = {
val isa = instructions.fromMethod(methA) val isa = instructions.fromMethod(methA)
val isb = instructions.fromMethod(methB) val isb = instructions.fromMethod(methB)
if (isa == isb) println("bytecode identical") if (isa == isb) println("bytecode identical")
else diffInstructions(isa, isb) else diffInstructions(isa, isb)
} }


import instructions._ // Do these classes have all the same methods, with the same names, access,
// descriptors and generic signatures? Method bodies are not considered, and
// the names of the classes containing the methods are substituted so they do
// not appear as differences.
def sameMethodAndFieldSignatures(clazzA: ClassNode, clazzB: ClassNode): Boolean = {
val ms1 = clazzA.fieldsAndMethods.toIndexedSeq
val ms2 = clazzB.fieldsAndMethods.toIndexedSeq
val name1 = clazzA.name
val name2 = clazzB.name

if (ms1.length != ms2.length) {
println("Different member counts in $name1 and $name2")
false
}
else (ms1, ms2).zipped forall { (m1, m2) =>
val c1 = m1.characteristics
val c2 = m2.characteristics.replaceAllLiterally(name2, name1)
if (c1 == c2)
println(s"[ok] $m1")
else
println(s"[fail]\n in $name1: $c1\n in $name2: $c2")

c1 == c2
}
}

// bytecode is equal modulo local variable numbering // bytecode is equal modulo local variable numbering
def equalsModuloVar(a: Instruction, b: Instruction) = (a, b) match { def equalsModuloVar(a: Instruction, b: Instruction) = (a, b) match {
case _ if a == b => true case _ if a == b => true
Expand Down

0 comments on commit 22341e7

Please sign in to comment.