All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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+")" } { (1 to inOrderFlow.size).map(index ⇒ ) } { flowTable }
PC      { index }
{ 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