/* 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
import org.opalj.util.elidedAssert
/**
* @author Wael Alkhatib
* @author Isbel Isbel
* @author Noorulla Sharief
* @author Tobias Becker
*/
case class Code(instructions: Array[Byte]) {
elidedAssert(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 <- instructions.indices
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 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 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.length
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
{asJavaClassType(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 classType = asJavaClassType(in.readUnsignedShort())
new
{classType.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"
}