summaryrefslogtreecommitdiff
path: root/src/partest-extras/scala/tools/partest
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2014-09-04 08:07:57 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2014-09-10 09:55:33 +0200
commit59070cc385560b48267cbc77e872027dd8304c05 (patch)
treef2eaf1bd8f1737cf04bfe600a05307f4d220f47b /src/partest-extras/scala/tools/partest
parent01f5435318551ce12787b120440572f169c71463 (diff)
downloadscala-59070cc385560b48267cbc77e872027dd8304c05.tar.gz
scala-59070cc385560b48267cbc77e872027dd8304c05.tar.bz2
scala-59070cc385560b48267cbc77e872027dd8304c05.zip
Remove stale local variables and exception handlers after DCE
This is required for correctness of the generated bytecode. Exception handlers and local variable descriptors specify code offset ranges. These offsets have to exist, not be eliminated.
Diffstat (limited to 'src/partest-extras/scala/tools/partest')
-rw-r--r--src/partest-extras/scala/tools/partest/ASMConverters.scala54
1 files changed, 35 insertions, 19 deletions
diff --git a/src/partest-extras/scala/tools/partest/ASMConverters.scala b/src/partest-extras/scala/tools/partest/ASMConverters.scala
index f4a90d9acf..d443a31112 100644
--- a/src/partest-extras/scala/tools/partest/ASMConverters.scala
+++ b/src/partest-extras/scala/tools/partest/ASMConverters.scala
@@ -3,7 +3,6 @@ package scala.tools.partest
import scala.collection.JavaConverters._
import scala.tools.asm
import asm.{tree => t}
-import scala.tools.asm.tree.LabelNode
/** Makes using ASM from ByteCodeTests more convenient.
*
@@ -15,13 +14,9 @@ object ASMConverters {
/**
* Transform the instructions of an ASM Method into a list of [[Instruction]]s.
*/
- def instructionsFromMethod(meth: t.MethodNode): List[Instruction] = {
- val insns = meth.instructions
- val asmToScala = new AsmToScala {
- def labelIndex(l: t.LabelNode) = insns.indexOf(l)
- }
- asmToScala.convert(insns.iterator.asScala.toList)
- }
+ def instructionsFromMethod(meth: t.MethodNode): List[Instruction] = new AsmToScala(meth).instructions
+
+ def convertMethod(meth: t.MethodNode): Method = new AsmToScala(meth).method
implicit class RichInstructionLists(val self: List[Instruction]) extends AnyVal {
def === (other: List[Instruction]) = equivalentBytecode(self, other)
@@ -61,6 +56,8 @@ object ASMConverters {
}
}
+ case class Method(instructions: List[Instruction], handlers: List[ExceptionHandler], localVars: List[LocalVariable])
+
case class Field (opcode: Int, owner: String, name: String, desc: String) extends Instruction
case class Incr (opcode: Int, `var`: Int, incr: Int) extends Instruction
case class Op (opcode: Int) extends Instruction
@@ -69,7 +66,7 @@ object ASMConverters {
case class Ldc (opcode: Int, cst: Any) extends Instruction
case class LookupSwitch(opcode: Int, dflt: Label, keys: List[Int], labels: List[Label]) extends Instruction
case class TableSwitch (opcode: Int, min: Int, max: Int, dflt: Label, labels: List[Label]) extends Instruction
- case class Method (opcode: Int, owner: String, name: String, desc: String, itf: Boolean) extends Instruction
+ case class Invoke (opcode: Int, owner: String, name: String, desc: String, itf: Boolean) 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
@@ -77,13 +74,18 @@ object ASMConverters {
case class FrameEntry (`type`: Int, local: List[Any], stack: List[Any]) extends Instruction { def opcode: Int = -1 }
case class LineNumber (line: Int, start: Label) extends Instruction { def opcode: Int = -1 }
- abstract class AsmToScala {
+ case class ExceptionHandler(start: Label, end: Label, handler: Label, desc: Option[String])
+ case class LocalVariable(name: String, desc: String, signature: Option[String], start: Label, end: Label, index: Int)
- def labelIndex(l: t.LabelNode): Int
+ class AsmToScala(asmMethod: t.MethodNode) {
- def op(i: t.AbstractInsnNode): Int = i.getOpcode
+ def instructions: List[Instruction] = asmMethod.instructions.iterator.asScala.toList map apply
- def convert(instructions: List[t.AbstractInsnNode]): List[Instruction] = instructions map apply
+ def method: Method = Method(instructions, convertHandlers(asmMethod), convertLocalVars(asmMethod))
+
+ private def labelIndex(l: t.LabelNode): Int = asmMethod.instructions.indexOf(l)
+
+ private def op(i: t.AbstractInsnNode): Int = i.getOpcode
private def lst[T](xs: java.util.List[T]): List[T] = if (xs == null) Nil else xs.asScala.toList
@@ -108,7 +110,7 @@ object ASMConverters {
case i: t.LdcInsnNode => Ldc (op(i), i.cst: Any)
case i: t.LookupSwitchInsnNode => LookupSwitch (op(i), applyLabel(i.dflt), lst(i.keys) map (x => x: Int), lst(i.labels) map applyLabel)
case i: t.TableSwitchInsnNode => TableSwitch (op(i), i.min, i.max, applyLabel(i.dflt), lst(i.labels) map applyLabel)
- case i: t.MethodInsnNode => Method (op(i), i.owner, i.name, i.desc, i.itf)
+ case i: t.MethodInsnNode => Invoke (op(i), i.owner, i.name, i.desc, i.itf)
case i: t.MultiANewArrayInsnNode => NewArray (op(i), i.desc, i.dims)
case i: t.TypeInsnNode => TypeOp (op(i), i.desc)
case i: t.VarInsnNode => VarOp (op(i), i.`var`)
@@ -116,6 +118,14 @@ object ASMConverters {
case i: t.FrameNode => FrameEntry (i.`type`, mapOverFrameTypes(lst(i.local)), mapOverFrameTypes(lst(i.stack)))
case i: t.LineNumberNode => LineNumber (i.line, applyLabel(i.start))
}
+
+ private def convertHandlers(method: t.MethodNode): List[ExceptionHandler] = {
+ method.tryCatchBlocks.asScala.map(h => ExceptionHandler(applyLabel(h.start), applyLabel(h.end), applyLabel(h.handler), Option(h.`type`)))(collection.breakOut)
+ }
+
+ private def convertLocalVars(method: t.MethodNode): List[LocalVariable] = {
+ method.localVariables.asScala.map(v => LocalVariable(v.name, v.desc, Option(v.signature), applyLabel(v.start), applyLabel(v.end), v.index))(collection.breakOut)
+ }
}
import collection.mutable.{Map => MMap}
@@ -159,15 +169,21 @@ object ASMConverters {
}) && equivalentBytecode(as.tail, bs.tail, varMap, labelMap)
}
- /**
- * Convert back a list of [[Instruction]]s to ASM land. The code is emitted into the parameter
- * `method`.
- */
def applyToMethod(method: t.MethodNode, instructions: List[Instruction]): Unit = {
val asmLabel = createLabelNodes(instructions)
instructions.foreach(visitMethod(method, _, asmLabel))
}
+ /**
+ * Convert back a [[Method]] to ASM land. The code is emitted into the parameter `asmMethod`.
+ */
+ def applyToMethod(asmMethod: t.MethodNode, method: Method): Unit = {
+ val asmLabel = createLabelNodes(method.instructions)
+ method.instructions.foreach(visitMethod(asmMethod, _, asmLabel))
+ method.handlers.foreach(h => asmMethod.visitTryCatchBlock(asmLabel(h.start), asmLabel(h.end), asmLabel(h.handler), h.desc.orNull))
+ method.localVars.foreach(v => asmMethod.visitLocalVariable(v.name, v.desc, v.signature.orNull, asmLabel(v.start), asmLabel(v.end), v.index))
+ }
+
private def createLabelNodes(instructions: List[Instruction]): Map[Label, asm.Label] = {
val labels = instructions collect {
case l: Label => l
@@ -190,7 +206,7 @@ object ASMConverters {
case Ldc(op, cst) => method.visitLdcInsn(cst)
case LookupSwitch(op, dflt, keys, labels) => method.visitLookupSwitchInsn(asmLabel(dflt), keys.toArray, (labels map asmLabel).toArray)
case TableSwitch(op, min, max, dflt, labels) => method.visitTableSwitchInsn(min, max, asmLabel(dflt), (labels map asmLabel).toArray: _*)
- case Method(op, owner, name, desc, itf) => method.visitMethodInsn(op, owner, name, desc, itf)
+ case Invoke(op, owner, name, desc, itf) => method.visitMethodInsn(op, owner, name, desc, itf)
case NewArray(op, desc, dims) => method.visitMultiANewArrayInsn(desc, dims)
case TypeOp(op, desc) => method.visitTypeInsn(op, desc)
case VarOp(op, vr) => method.visitVarInsn(op, vr)