/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package da
import scala.xml.Node
import scala.xml.NodeSeq
import scala.xml.Text
import scala.xml.Unparsed
import org.opalj.control.repeat
/**
* @author Wael Alkhatib
* @author Isbel Isbel
* @author Noorulla Sharief
* @author Tobias Becker
*/
case class Code(instructions: Array[Byte]) {
assert(instructions.length > 0)
import Code.id
def toXHTML(
methodIndex: Int,
exceptionTable: ExceptionTable,
lineNumberTable: Option[Seq[LineNumberTableEntry]]
)(
implicit
cp: Constant_Pool
): Node = {
val instructions = InstructionsToXHTML(methodIndex, this.instructions)
val exceptions = ExceptionsToXHTMLTableElements(instructions, exceptionTable)
PC |
{
lineNumberTable.map(_ => Line | ).getOrElse(NodeSeq.Empty)
}
Instruction |
{
if (exceptionTable.nonEmpty)
Seq(
) ++ exceptionTable.tail.map(_ => )
else
scala.xml.NodeSeq.Empty
}
{ // One instruction per row
for {
pc <- (0 until instructions.length)
if instructions(pc) != null
} yield {
val exceptionInfo =
exceptions.foldRight(Seq.empty[Node]) { (a, b) =>
Seq(a(pc)) ++ b
}
createTableRowForInstruction(
methodIndex, instructions(pc), exceptionInfo, pc, lineNumberTable
)
}
}
}
private[this] def createTableRowForInstruction(
methodIndex: Int,
instruction: Node,
exceptions: Seq[Node],
pc: Int,
lineNumberTable: Option[Seq[LineNumberTableEntry]]
): Node = {
{ pc } |
{
lineNumberTable.map { lineNumberTable =>
val ln = lineNumberTable.find(e => e.start_pc == pc).map(_.line_number)
{ Text(ln.map(_.toString).getOrElse("|")) } |
}.getOrElse {
scala.xml.NodeSeq.Empty
}
}
{ instruction } |
{ exceptions }
}
private[this] def InstructionsToXHTML(
methodIndex: Int,
source: Array[Byte]
)(
implicit
cp: Constant_Pool
): Array[Node] = {
import java.io.DataInputStream
import java.io.ByteArrayInputStream
val bas = new ByteArrayInputStream(source)
val in = new DataInputStream(bas)
val codeLength = source.size
val instructions = new Array[Node](codeLength)
var wide: Boolean = false
def lvIndex: Int =
if (wide) {
wide = false
in.readUnsignedShort
} else {
in.readUnsignedByte
}
def ifToString(mnemonic: String, pc: Int): Node = {
val targetPC = in.readShort + pc
val targetID = "#"+id(methodIndex, targetPC)
{ mnemonic }
{ Text(targetPC.toString) }
}
while (in.available > 0) {
val pc = codeLength - in.available
instructions(pc) = (in.readUnsignedByte: @scala.annotation.switch) match {
case 50 => aaload
case 83 => aastore
case 1 => aconst_null
case 25 =>
aload
{ lvIndex }
case 42 => aload_0
case 43 => aload_1
case 44 => aload_2
case 45 => aload_3
case 189 =>
anewarray
{ asJavaObjectType(in.readUnsignedShort()).asSpan("") }
case 176 => areturn
case 190 => arraylength
case 58 =>
astore
{ lvIndex }
case 75 => astore_0
case 76 => astore_1
case 77 => astore_2
case 78 => astore_3
case 191 => athrow
case 51 => baload
case 84 => bastore
case 16 =>
bipush
{ in.readByte }
case 52 => caload
case 85 => castore
case 192 =>
checkcast
{ asJavaReferenceType(in.readUnsignedShort()).asSpan("") }
case 144 => d2f
case 142 => d2i
case 143 => d2l
case 99 => dadd
case 49 => daload
case 82 => dastore
case 152 => dcmpg
case 151 => dcmpl
case 14 => dconst_0
case 15 => dconst_1
case 111 => ddiv
case 24 =>
dload
{ lvIndex }
case 38 => dload_0
case 39 => dload_1
case 40 => dload_2
case 41 => dload_3
case 107 => dmul
case 119 => dneg
case 115 => drem
case 175 => dreturn
case 57 =>
dstore
{ lvIndex }
case 71 => dstore_0
case 72 => dstore_1
case 73 => dstore_2
case 74 => dstore_3
case 103 => dsub
case 89 => dup
case 90 => dup_x1
case 91 => dup_x2
case 92 => dup2
case 93 => dup2_x1
case 94 => dup2_x2
case 141 => f2d
case 139 => f2i
case 140 => f2l
case 98 => fadd
case 48 => faload
case 81 => fastore
case 150 => fcmpg
case 149 => fcmpl
case 11 => fconst_0
case 12 => fconst_1
case 13 => fconst_2
case 110 => fdiv
case 23 =>
fload
{ lvIndex }
case 34 => fload_0
case 35 => fload_1
case 36 => fload_2
case 37 => fload_3
case 106 => fmul
case 118 => fneg
case 114 => frem
case 174 => freturn
case 56 =>
fstore
{ lvIndex }
case 67 => fstore_0
case 68 => fstore_1
case 69 => fstore_2
case 70 => fstore_3
case 102 => fsub
case 180 =>
getfield
{ val c = in.readUnsignedShort(); cp(c).asInstructionParameter }
case 178 =>
getstatic
{ val c = in.readUnsignedShort(); cp(c).asInstructionParameter }
case 167 =>
val targetPC = in.readShort + pc
val targetID = "#"+id(methodIndex, targetPC)
goto
{ targetPC }
case 200 =>
val targetPC = in.readInt + pc
val targetID = "#"+id(methodIndex, targetPC)
goto_w
{ targetPC }
case 145 => i2b
case 146 => i2c
case 135 => i2d
case 134 => i2f
case 133 => i2l
case 147 => i2s
case 96 => iadd
case 46 => iaload
case 126 => iand
case 79 => iastore
case 2 => iconst_m1
case 3 => iconst_0
case 4 => iconst_1
case 5 => iconst_2
case 6 => iconst_3
case 7 => iconst_4
case 8 => iconst_5
case 108 => idiv
case 165 => ifToString("if_acmpeq", pc)
case 166 => ifToString("if_acmpne", pc)
case 159 => ifToString("if_icmpeq", pc)
case 160 => ifToString("if_icmpne", pc)
case 161 => ifToString("if_icmplt", pc)
case 162 => ifToString("if_icmpge", pc)
case 163 => ifToString("if_icmpgt", pc)
case 164 => ifToString("if_icmple", pc)
case 153 => ifToString("ifeq", pc)
case 154 => ifToString("ifne", pc)
case 155 => ifToString("iflt", pc)
case 156 => ifToString("ifge", pc)
case 157 => ifToString("ifgt", pc)
case 158 => ifToString("ifle", pc)
case 199 => ifToString("ifnonnull", pc)
case 198 => ifToString("ifnull", pc)
case 132 =>
val (lvIndex, increment) = if (wide) {
wide = false
val lvIndex = in.readUnsignedShort
val constValue = in.readShort
(lvIndex, constValue)
} else {
val lvIndex = in.readUnsignedByte
val constValue = in.readByte
(lvIndex, constValue)
}
iinc
{ lvIndex }
{ increment }
case 21 =>
iload
{ lvIndex }
case 26 => iload_0
case 27 => iload_1
case 28 => iload_2
case 29 => iload_3
case 104 => imul
case 116 => ineg
case 193 =>
instanceof
{ asJavaReferenceType(in.readUnsignedShort()).asSpan("") }
case 186 =>
val c = in.readUnsignedShort()
in.readByte // ignored; fixed value
in.readByte // ignored; fixed value
val signature = cp(c).asInstructionParameter
invokedynamic
{ signature }
case 185 =>
val c = in.readUnsignedShort()
val count = in.readByte
in.readByte // ignored; fixed value
val signature = cp(c).asInstructionParameter
invokeinterface (nargs={ count })
{ signature }
case 183 =>
val c = in.readUnsignedShort()
val signature = cp(c).asInstructionParameter
invokespecial
{ signature }
case 184 =>
val c = in.readUnsignedShort()
val signature = cp(c).asInstructionParameter
invokestatic
{ signature }
case 182 =>
val c = in.readUnsignedShort()
val signature = cp(c).asInstructionParameter
invokevirtual
{ signature }
case 128 => ior
case 112 => irem
case 172 => ireturn
case 120 => ishl
case 122 => ishr
case 54 =>
istore
{ lvIndex }
case 59 => istore_0
case 60 => istore_1
case 61 => istore_2
case 62 => istore_3
case 100 => isub
case 124 => iushr
case 130 => ixor
case 168 =>
val targetPC = in.readShort + pc
val targetID = "#"+id(methodIndex, targetPC)
jsr
{ targetPC }
case 201 =>
val targetPC = in.readInt + pc
val targetID = "#"+id(methodIndex, targetPC)
jsr_w
{ targetPC }
case 138 => l2d
case 137 => l2f
case 136 => l2i
case 97 => ladd
case 47 => laload
case 127 => land
case 80 => lastore
case 148 => lcmp
case 9 => lconst_0
case 10 => lconst_1
case 18 =>
val constantValue =
cp(in.readUnsignedByte()) match {
case ci: CONSTANT_Class_info => Seq(ci.asInstructionParameter, Text(".class"))
case cv => Seq(cv.asInstructionParameter)
}
ldc
{ constantValue }
case 19 =>
val constantValue =
cp(in.readUnsignedShort()) match {
case ci: CONSTANT_Class_info => Seq(ci.asInstructionParameter, Text(".class"))
case cv => Seq(cv.asInstructionParameter)
}
ldc_w
{ constantValue }
case 20 =>
val constantValue = cp(in.readUnsignedShort).asInstructionParameter
ldc2_w
{ constantValue }
case 109 => ldiv
case 22 =>
lload
{ lvIndex }
case 30 => lload_0
case 31 => lload_1
case 32 => lload_2
case 33 => lload_3
case 105 => lmul
case 117 => lneg
case 171 =>
// LOOKUPSWITCH
in.skip((3 - (pc % 4)).toLong) // skip padding bytes
val defaultTarget = in.readInt + pc
val npairsCount = in.readInt
val table = new StringBuilder("");
repeat(npairsCount) {
table.append("(case:"+in.readInt+","+(in.readInt + pc)+") ")
}
lookupswitch default:{ defaultTarget } [{ table }]
case 129 => lor
case 113 => lrem
case 173 => lreturn
case 121 => lshl
case 123 => lshr
case 55 => lstore { lvIndex }
case 63 => lstore_0
case 64 => lstore_1
case 65 => lstore_2
case 66 => lstore_3
case 101 => lsub
case 125 => lushr
case 131 => lxor
case 194 => monitorenter
case 195 => monitorexit
case 197 =>
val referenceType = asJavaReferenceType(in.readUnsignedShort())
val dim = in.readUnsignedByte
multianewarray
{ referenceType.asSpan("") }{ dim }
case 187 =>
val objectType = asJavaObjectType(in.readUnsignedShort())
new
{ objectType.asSpan("") }
case 188 =>
newarray
{
in.readByte match {
case 4=> "T_BOOLEAN (4)"
case 5=> "T_CHAR (5)"
case 6=> "T_FLOAT (6)"
case 7=> "T_DOUBLE (7)"
case 8=> "T_BYTE (8)"
case 9=> "T_SHORT (9)"
case 10=> "T_INT (10)"
case 11=> "T_LONG (11)"
}
}
case 0 => nop
case 87 => pop
case 88 => pop2
case 181 =>
val c = in.readUnsignedShort()
val signature = cp(c).asInstructionParameter
putfield { signature }
case 179 =>
val c = in.readUnsignedShort()
val signature = cp(c).asInstructionParameter
putstatic { signature }
case 169 =>
ret
{ lvIndex }
case 177 => return
case 53 => saload
case 86 => sastore
case 17 =>
sipush
{ in.readShort /* value */ }
case 95 => swap
case 170 =>
in.skip((3 - (pc % 4)).toLong) // skip padding bytes
val defaultTargetPC = in.readInt + pc
val defaultTargetID = "#"+id(methodIndex, defaultTargetPC)
val defaultTarget = s"$defaultTargetPC"
val low = in.readInt
val high = in.readInt
var offsetcounter: Int = 0;
val switchTargets = new StringBuilder("");
repeat(high - low + 1) {
val targetPC = in.readInt + pc
val targetID = "#"+id(methodIndex, targetPC)
val target = s"$targetPC"
switchTargets.append("(case "+(low + offsetcounter)+" → "+target+") ")
offsetcounter += 1;
}
tableswitch default → { Unparsed(defaultTarget) }; { Unparsed(switchTargets.toString) }
case 196 =>
wide = true
wide
case opcode =>
throw new UnknownError("unknown opcode: "+opcode)
}
}
instructions
}
def ExceptionsToXHTMLTableElements(
instructions: Array[Node],
exceptionTable: ExceptionTable
)(
implicit
cp: Constant_Pool
): Array[Array[Node]] = {
val exceptions: Array[Array[Node]] = new Array(exceptionTable.size)
for { (exceptionHandler, index) <- exceptionTable.iterator.zipWithIndex } {
val exceptionName =
(index + 1).toString+": "+(
if (exceptionHandler.catch_type != 0)
cp(exceptionHandler.catch_type).toString
else
"Any"
)
var exceptionPCLength = 0
var exceptionPCStart = -1
exceptions(index) = new Array[Node](instructions.length)
for {
i <- exceptionHandler.start_pc until exceptionHandler.end_pc
if instructions(i) ne null
} {
if (exceptionPCLength == 0)
exceptionPCStart = i
exceptionPCLength += 1
}
for {
i <- 0 until exceptionHandler.start_pc
if i != exceptionHandler.handler_pc
} {
exceptions(index)(i) = |
}
for {
i <- exceptionHandler.end_pc until instructions.length
if i != exceptionHandler.handler_pc
} {
exceptions(index)(i) = |
}
var classes = "exception"
if (exceptionPCStart == exceptionHandler.handler_pc)
classes += " handler_overlap"
else
exceptions(index)(exceptionHandler.handler_pc) =
|
exceptions(index)(exceptionPCStart) =
{ exceptionName }
|
}
exceptions
}
}
object Code {
def id(methodIndex: Int, pc: Int): String = s"m${methodIndex}_pc$pc"
}