org.opalj.ai.XHTMLTracer.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License:
* Copyright (c) 2009 - 2017
* Software Technology Group
* Department of Computer Science
* Technische Universität Darmstadt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.opalj
package ai
import scala.language.existentials
import scala.xml.Node
import org.opalj.collection.immutable.{Chain ⇒ List}
import org.opalj.io.writeAndOpen
import org.opalj.br.Code
import org.opalj.br.instructions.CHECKCAST
import org.opalj.br.instructions.FieldAccess
import org.opalj.br.instructions.Instruction
import org.opalj.br.instructions.LoadString
import org.opalj.br.instructions.NEW
import org.opalj.br.instructions.NonVirtualMethodInvocationInstruction
import org.opalj.ai.common.XHTML.dumpLocals
import org.opalj.ai.common.XHTML.dumpStack
case class FlowEntity(
pc: PC,
instruction: Instruction,
operands: Operands[_ >: Null <: Domain#DomainValue],
locals: Locals[_ >: Null <: Domain#DomainValue],
properties: Option[String]
) {
val flowId = FlowEntity.nextFlowId
}
private[ai] object FlowEntity {
private var flowId = -1
private def nextFlowId = { flowId += 1; flowId }
def lastFlowId: Int = flowId - 1
}
/**
* A tracer that generates an HTML document.
*
* @author Michael Eichberg
*/
trait XHTMLTracer extends AITracer {
private[this] var flow: List[List[FlowEntity]] = List(List.empty)
private[this] def newBranch(): List[List[FlowEntity]] = {
flow = List.empty[FlowEntity] :&: flow
flow
}
private[this] def addFlowEntity(flowEntity: FlowEntity): Unit = {
if (flow.head.exists(_.pc == flowEntity.pc))
newBranch()
flow = (flowEntity :&: flow.head) :&: flow.tail
}
private def instructionToNode(
flowId: Int,
pc: PC,
instruction: Instruction
): xml.Node = {
val openDialog = "$( \"#dialog"+flowId+"\" ).dialog(\"open\");"
val instructionAsString =
instruction match {
case NEW(objectType) ⇒
"new …"+objectType.simpleName;
case CHECKCAST(referenceType) ⇒
"checkcast "+referenceType.toJava;
case LoadString(s) if s.size < 5 ⇒
"Load \""+s+"\"";
case LoadString(s) ⇒
"Load \""+s.substring(0, 4)+"…\""
case fieldAccess: FieldAccess ⇒
fieldAccess.mnemonic+" "+fieldAccess.name
case invoke: NonVirtualMethodInvocationInstruction ⇒
val declaringClass = invoke.declaringClass.toJava
"…"+declaringClass.substring(declaringClass.lastIndexOf('.') + 1)+" "+
invoke.name+"(…)"
case _ ⇒ instruction.toString(pc)
}
{ instructionAsString }
}
def dumpXHTML(title: String): scala.xml.Node = {
import scala.collection.immutable.{SortedMap, SortedSet}
val inOrderFlow = flow.map(_.reverse).reverse
var pathsCount = 0
var pcs = SortedSet.empty[PC]
for (path ← flow) {
pathsCount += 1
for (entity ← path) {
pcs += entity.pc
}
}
val pcsToRowIndex = SortedMap.empty[Int, Int] ++ pcs.zipWithIndex
val ids = new java.util.IdentityHashMap[AnyRef, Integer]
var nextId = 1
val idsLookup = (value: AnyRef) ⇒ {
var id = ids.get(value)
if (id == null) {
id = nextId
nextId += 1
ids.put(value, id)
}
id.intValue()
}
val dialogSetup =
(for {
path ← inOrderFlow
entity ← path
} yield {
xml.Unparsed("$(function() { $( \"#dialog"+entity.flowId+"\" ).dialog({autoOpen:false}); });\n")
}).toIterable
val dialogs: Iterable[Node] =
(for {
(path, index) ← inOrderFlow.zipWithIndex
flowEntity ← path
} yield {
val dialogId = "dialog"+flowEntity.flowId
Stack
{ dumpStack(flowEntity.operands)(Some(idsLookup)) }
Locals
{ dumpLocals(flowEntity.locals)(Some(idsLookup)) }
}).toIterable
def row(pc: PC) =
(for (path ← inOrderFlow) yield {
val flowEntity = path.find(_.pc == pc)
{
flowEntity.
map(fe ⇒ instructionToNode(fe.flowId, pc, fe.instruction)).
getOrElse(xml.Text(" "))
}
}).toIterable
val cfJoins = code.cfJoins
val flowTable =
for ((pc, rowIndex) ← pcsToRowIndex) yield {
{ if (cfJoins.contains(pc)) "⇶ " else "" } { pc }
{ row(pc) }
}
{ title+" (Paths: "+pathsCount+"; Flow Nodes: "+FlowEntity.lastFlowId+")" }
PC
{ (1 to inOrderFlow.size).map(index ⇒ { index } ) }
{ flowTable }
{ dialogs }
}
private var code: Code = null
override def initialLocals(domain: Domain)(locals: domain.Locals): Unit = { /*EMPTY*/ }
override def continuingInterpretation(
code: Code,
domain: Domain
)(
initialWorkList: List[PC],
alreadyEvaluated: List[PC],
operandsArray: domain.OperandsArray,
localsArray: domain.LocalsArray,
memoryLayoutBeforeSubroutineCall: List[(PC, domain.OperandsArray, domain.LocalsArray)]
): Unit = {
if ((this.code eq code) || (this.code == null))
this.code = code
else
throw new IllegalStateException("this XHTMLtracer is already used; create a new one")
}
private[this] var continuingWithBranch = true
override def flow(
domain: Domain
)(
currentPC: PC,
successorPC: PC,
isExceptionalControlFlow: Boolean
): Unit = {
continuingWithBranch = currentPC < successorPC
}
override def deadLocalVariable(domain: Domain)(pc: PC, lvIndex: Int): Unit = { /*EMPTY*/ }
override def noFlow(domain: Domain)(currentPC: PC, targetPC: PC): Unit = { /*EMPTY*/ }
override def rescheduled(
domain: Domain
)(
sourcePC: PC,
targetPC: PC,
isExceptionalControlFlow: Boolean,
worklist: List[PC]
): Unit = {
/*ignored for now*/
}
override def instructionEvalution(
domain: Domain
)(
pc: PC,
instruction: Instruction,
operands: domain.Operands,
locals: domain.Locals
): Unit = {
if (!continuingWithBranch)
newBranch()
addFlowEntity(FlowEntity(pc, instruction, operands, locals, domain.properties(pc)))
// if we have a call to instruction evaluation without an intermediate
// flow call, we are continuing the evaluation with a branch
continuingWithBranch = false
}
override def join(
domain: Domain
)(
pc: PC,
thisOperands: domain.Operands,
thisLocals: domain.Locals,
otherOperands: domain.Operands,
otherLocals: domain.Locals,
result: Update[(domain.Operands, domain.Locals)]
): Unit = { /*ignored*/ }
override def establishedConstraint(
domain: Domain
)(
pc: PC,
effectivePC: PC,
operands: domain.Operands,
locals: domain.Locals,
newOperands: domain.Operands,
newLocals: domain.Locals
): Unit = { /*ignored*/ }
override def abruptMethodExecution(
domain: Domain
)(
pc: Int,
exception: domain.ExceptionValue
): Unit = { /*ignored*/ }
override def jumpToSubroutine(
domain: Domain
)(
pc: PC, target: PC, nestingLevel: Int
): Unit = { /* ignored */ }
override def returnFromSubroutine(
domain: Domain
)(
pc: PC,
returnAddress: PC,
subroutineInstructions: List[PC]
): Unit = { /*ignored*/ }
override def abruptSubroutineTermination(
domain: Domain
)(
sourcePC: PC, targetPC: PC, jumpToSubroutineId: Int,
terminatedSubroutinesCount: Int,
oldWorklist: List[PC],
newWorklist: List[PC]
): Unit = { /*ignored*/ }
/**
* Called when a ret instruction is encountered.
*/
override def ret(
domain: Domain
)(
pc: PC,
returnAddress: PC,
oldWorklist: List[PC],
newWorklist: List[PC]
): Unit = { /*ignored*/ }
override def domainMessage(
domain: Domain,
source: Class[_], typeID: String,
pc: Option[PC], message: ⇒ String
): Unit = { /*EMPTY*/ }
def result(result: AIResult): Unit = {
writeAndOpen(dumpXHTML((new java.util.Date).toString()), "AITrace", ".html")
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy