From 22341e7ab9722cf212c01e9830014648849f8e70 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 8 Feb 2013 13:52:58 -0800 Subject: Expanded bytecode testing code. def sameMethodAndFieldSignatures compares two classes to verify they have all the same methods and fields, and no others. --- src/partest/scala/tools/partest/AsmNode.scala | 60 ++++++++++++++++++++++ src/partest/scala/tools/partest/BytecodeTest.scala | 33 ++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 src/partest/scala/tools/partest/AsmNode.scala (limited to 'src/partest') diff --git a/src/partest/scala/tools/partest/AsmNode.scala b/src/partest/scala/tools/partest/AsmNode.scala new file mode 100644 index 0000000000..d181436676 --- /dev/null +++ b/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) +} diff --git a/src/partest/scala/tools/partest/BytecodeTest.scala b/src/partest/scala/tools/partest/BytecodeTest.scala index 41329a8264..2699083069 100644 --- a/src/partest/scala/tools/partest/BytecodeTest.scala +++ b/src/partest/scala/tools/partest/BytecodeTest.scala @@ -3,9 +3,10 @@ package scala.tools.partest import scala.tools.nsc.util.JavaClassPath import scala.collection.JavaConverters._ import scala.tools.asm -import asm.ClassReader +import asm.{ ClassReader } import asm.tree.{ClassNode, MethodNode, InsnList} import java.io.InputStream +import AsmNode._ /** * Provides utilities for inspecting bytecode using ASM library. @@ -29,13 +30,14 @@ import java.io.InputStream * */ abstract class BytecodeTest extends ASMConverters { + import instructions._ /** produce the output to be compared against a checkfile */ protected def show(): Unit def main(args: Array[String]): Unit = show -// asserts + // asserts def sameBytecode(methA: MethodNode, methB: MethodNode) = { val isa = instructions.fromMethod(methA) val isb = instructions.fromMethod(methB) @@ -43,7 +45,32 @@ abstract class BytecodeTest extends ASMConverters { 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 def equalsModuloVar(a: Instruction, b: Instruction) = (a, b) match { case _ if a == b => true -- cgit v1.2.3