Skip to content

Commit 22341e7

Browse files
committed
Expanded bytecode testing code.
def sameMethodAndFieldSignatures compares two classes to verify they have all the same methods and fields, and no others.
1 parent 6537d79 commit 22341e7

File tree

2 files changed

+90
-3
lines changed

2 files changed

+90
-3
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package scala.tools.partest
2+
3+
import scala.collection.JavaConverters._
4+
import scala.tools.asm
5+
import asm._
6+
import asm.tree._
7+
import java.lang.reflect.Modifier
8+
9+
sealed trait AsmNode[+T] {
10+
def node: T
11+
def access: Int
12+
def desc: String
13+
def name: String
14+
def signature: String
15+
def attrs: List[Attribute]
16+
def visibleAnnotations: List[AnnotationNode]
17+
def invisibleAnnotations: List[AnnotationNode]
18+
def characteristics = f"$name%15s $desc%-30s$accessString$sigString"
19+
20+
private def accessString = if (access == 0) "" else " " + Modifier.toString(access)
21+
private def sigString = if (signature == null) "" else " " + signature
22+
override def toString = characteristics
23+
}
24+
25+
object AsmNode {
26+
type AsmMethod = AsmNode[MethodNode]
27+
type AsmField = AsmNode[FieldNode]
28+
type AsmMember = AsmNode[_]
29+
30+
implicit class ClassNodeOps(val node: ClassNode) {
31+
def fieldsAndMethods: List[AsmMember] = {
32+
val xs: List[AsmMember] = (
33+
node.methods.asScala.toList.map(x => (x: AsmMethod))
34+
++ node.fields.asScala.toList.map(x => (x: AsmField))
35+
)
36+
xs sortBy (_.characteristics)
37+
}
38+
}
39+
implicit class AsmMethodNode(val node: MethodNode) extends AsmNode[MethodNode] {
40+
def access: Int = node.access
41+
def desc: String = node.desc
42+
def name: String = node.name
43+
def signature: String = node.signature
44+
def attrs: List[Attribute] = node.attrs.asScala.toList
45+
def visibleAnnotations: List[AnnotationNode] = node.visibleAnnotations.asScala.toList
46+
def invisibleAnnotations: List[AnnotationNode] = node.invisibleAnnotations.asScala.toList
47+
}
48+
implicit class AsmFieldNode(val node: FieldNode) extends AsmNode[FieldNode] {
49+
def access: Int = node.access
50+
def desc: String = node.desc
51+
def name: String = node.name
52+
def signature: String = node.signature
53+
def attrs: List[Attribute] = node.attrs.asScala.toList
54+
def visibleAnnotations: List[AnnotationNode] = node.visibleAnnotations.asScala.toList
55+
def invisibleAnnotations: List[AnnotationNode] = node.invisibleAnnotations.asScala.toList
56+
}
57+
58+
def apply(node: MethodNode): AsmMethodNode = new AsmMethodNode(node)
59+
def apply(node: FieldNode): AsmFieldNode = new AsmFieldNode(node)
60+
}

src/partest/scala/tools/partest/BytecodeTest.scala

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package scala.tools.partest
33
import scala.tools.nsc.util.JavaClassPath
44
import scala.collection.JavaConverters._
55
import scala.tools.asm
6-
import asm.ClassReader
6+
import asm.{ ClassReader }
77
import asm.tree.{ClassNode, MethodNode, InsnList}
88
import java.io.InputStream
9+
import AsmNode._
910

1011
/**
1112
* Provides utilities for inspecting bytecode using ASM library.
@@ -29,21 +30,47 @@ import java.io.InputStream
2930
*
3031
*/
3132
abstract class BytecodeTest extends ASMConverters {
33+
import instructions._
3234

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

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

38-
// asserts
40+
// asserts
3941
def sameBytecode(methA: MethodNode, methB: MethodNode) = {
4042
val isa = instructions.fromMethod(methA)
4143
val isb = instructions.fromMethod(methB)
4244
if (isa == isb) println("bytecode identical")
4345
else diffInstructions(isa, isb)
4446
}
4547

46-
import instructions._
48+
// Do these classes have all the same methods, with the same names, access,
49+
// descriptors and generic signatures? Method bodies are not considered, and
50+
// the names of the classes containing the methods are substituted so they do
51+
// not appear as differences.
52+
def sameMethodAndFieldSignatures(clazzA: ClassNode, clazzB: ClassNode): Boolean = {
53+
val ms1 = clazzA.fieldsAndMethods.toIndexedSeq
54+
val ms2 = clazzB.fieldsAndMethods.toIndexedSeq
55+
val name1 = clazzA.name
56+
val name2 = clazzB.name
57+
58+
if (ms1.length != ms2.length) {
59+
println("Different member counts in $name1 and $name2")
60+
false
61+
}
62+
else (ms1, ms2).zipped forall { (m1, m2) =>
63+
val c1 = m1.characteristics
64+
val c2 = m2.characteristics.replaceAllLiterally(name2, name1)
65+
if (c1 == c2)
66+
println(s"[ok] $m1")
67+
else
68+
println(s"[fail]\n in $name1: $c1\n in $name2: $c2")
69+
70+
c1 == c2
71+
}
72+
}
73+
4774
// bytecode is equal modulo local variable numbering
4875
def equalsModuloVar(a: Instruction, b: Instruction) = (a, b) match {
4976
case _ if a == b => true

0 commit comments

Comments
 (0)