Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve dot ast #2002

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class DotAstGeneratorTests extends CCodeToCpgSuite {
inside(cpg.method.name("my_func").dotAst.l) { case List(x) =>
x should (
startWith("digraph \"my_func\"") and
include("""[label = <(CONTROL_STRUCTURE,if (y &gt; 42),if (y &gt; 42))<SUB>5</SUB>> ]""") and
include("""[label = <CONTROL_STRUCTURE, 5<BR/>if (y &gt; 42)> ]""") and
endWith("}\n")
)
}
Expand All @@ -52,18 +52,16 @@ class DotAstGeneratorTests extends CCodeToCpgSuite {

"allow plotting sub trees of methods" in {
inside(cpg.method.ast.isControlStructure.code(".*y > 42.*").dotAst.l) { case List(x, _) =>
x should (include("y &gt; 42") and include("IDENTIFIER,y") and not include "x * 2")
x should (include("y &gt; 42") and include("IDENTIFIER, 5<BR/>y") and not include "x * 2")
}
}

"allow plotting sub trees of methods correctly escaped" in {
inside(cpg.method.name("lemon").dotAst.l) { case List(x) =>
x should (
startWith("digraph \"lemon\"") and
include("""[label = <(goog,goog(&quot;\&quot;yes\&quot;&quot;))<SUB>18</SUB>> ]""") and
include(
"""[label = <(LITERAL,&quot;\&quot;yes\&quot;&quot;,goog(&quot;\&quot;yes\&quot;&quot;))<SUB>18</SUB>> ]"""
) and
include("""[label = <CALL, 18<BR/>goog<BR/>goog(&quot;\&quot;yes\&quot;&quot;)> ]""") and
include("""[label = <LITERAL, 18<BR/>&quot;\&quot;yes\&quot;&quot;> ]""") and
endWith("}\n")
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class DotCdgGeneratorTests extends DataFlowCodeToCpgSuite {
inside(cpg.method.name("foo").dotCdg.l) { case List(x) =>
x should (
startWith("digraph \"foo\"") and
include("""[label = <(&lt;operator&gt;.greaterThan,x &gt; 8)<SUB>3</SUB>> ]""") and
include("""[label = <(&lt;operator&gt;.assignment,z = a(x))<SUB>4</SUB>> ]""") and
include("""[label = <(a,a(x))<SUB>4</SUB>> ]""") and
include("""[label = <CALL, 3<BR/>&lt;operator&gt;.greaterThan<BR/>x &gt; 8> ]""") and
include("""[label = <CALL, 4<BR/>&lt;operator&gt;.assignment<BR/>z = a(x)> ]""") and
include("""[label = <CALL, 4<BR/>a<BR/>a(x)> ]""") and
endWith("}\n")
)
val lines = x.split("\n")
Expand All @@ -46,9 +46,9 @@ class DotCdgGeneratorTests extends DataFlowCodeToCpgSuite {
inside(cpg.method.name("foo").dotCdg.l) { case List(x) =>
x should (
startWith("digraph \"foo\"") and
include("""[label = <(&lt;operator&gt;.greaterThan,x &gt; 8)<SUB>3</SUB>> ]""") and
include("""[label = <(&lt;operator&gt;.assignment,z = a(x))<SUB>4</SUB>> ]""") and
include("""[label = <(a,a(x))<SUB>4</SUB>> ]""") and
include("""[label = <CALL, 3<BR/>&lt;operator&gt;.greaterThan<BR/>x &gt; 8> ]""") and
include("""[label = <CALL, 4<BR/>&lt;operator&gt;.assignment<BR/>z = a(x)> ]""") and
include("""[label = <CALL, 4<BR/>a<BR/>a(x)> ]""") and
endWith("}\n")
)
val lines = x.split("\n")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DotCfgGeneratorTests extends CCodeToCpgSuite {
inside(cpg.method.name("main").dotCfg.l) { case List(dotStr) =>
dotStr should (
startWith("digraph \"main\" {") and
include("(&lt;operator&gt;.assignment,i = 0)") and
include("&lt;operator&gt;.assignment<BR/>i = 0") and
endWith("}\n")
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import io.shiftleft.codepropertygraph.generated.PropertyNames
import io.shiftleft.codepropertygraph.generated.nodes._
import io.shiftleft.semanticcpg.language._
import io.shiftleft.semanticcpg.utils.MemberAccess
import org.apache.commons.lang.StringUtils

import java.util.Optional
import scala.collection.immutable.HashMap
import scala.collection.mutable
import scala.jdk.OptionConverters.RichOptional
import scala.language.postfixOps

object DotSerializer {
Expand Down Expand Up @@ -36,6 +37,7 @@ object DotSerializer {
case Some(r) => namedGraphBegin(r)
case None => defaultGraphBegin()
}
sb.append("node [shape=\"rect\"];\n")
val nodeStrings = graph.vertices.map(nodeToDot)
val edgeStrings = graph.edges.map(e => edgeToDot(e, withEdgeTypes))
val subgraphStrings = graph.subgraph.zipWithIndex.map { case ((subgraph, nodes), idx) =>
Expand All @@ -61,23 +63,29 @@ object DotSerializer {
}

private def stringRepr(vertex: StoredNode): String = {
val maybeLineNo: Optional[AnyRef] = vertex.propertyOption(PropertyNames.LINE_NUMBER)
escape(vertex match {
case call: Call => (call.name, call.code).toString
case expr: Expression => (expr.label, expr.code, toCfgNode(expr).code).toString
case method: Method => (method.label, method.name).toString
case ret: MethodReturn => (ret.label, ret.typeFullName).toString
case param: MethodParameterIn => ("PARAM", param.code).toString
case local: Local => (local.label, s"${local.code}: ${local.typeFullName}").toString
case target: JumpTarget => (target.label, target.name).toString
case modifier: Modifier => (modifier.label, modifier.modifierType).toString()
case annoAssign: AnnotationParameterAssign => (annoAssign.label, annoAssign.code).toString()
case annoParam: AnnotationParameter => (annoParam.label, annoParam.code).toString()
case typ: Type => (typ.label, typ.name).toString()
case typeDecl: TypeDecl => (typeDecl.label, typeDecl.name).toString()
case member: Member => (member.label, member.name).toString()
case _ => ""
}) + (if (maybeLineNo.isPresent) s"<SUB>${maybeLineNo.get()}</SUB>" else "")
val lineOpt = vertex.propertyOption(PropertyNames.LINE_NUMBER).toScala.map(_.toString)
val list = (vertex match {
case call: Call => List(call.label, call.name, call.code)
case identifier: Identifier => List(identifier.label, identifier.name)
case literal: Literal => List(literal.label, literal.code)
case local: Local => List(local.label, local.name, local.typeFullName)
case method: Method => List(method.label, method.name)
case expr: Expression => List(expr.label, expr.code, toCfgNode(expr).code)
case ret: MethodReturn => List(ret.label)
case typeDecl: TypeDecl => List(typeDecl.label, typeDecl.name)
case param: MethodParameterIn => List("PARAM", param.name)
case target: JumpTarget => List(target.label, target.name)
case modifier: Modifier => List(modifier.label, modifier.modifierType)
case typ: Type => List(typ.label, typ.name)
case dec: DeclarationBase => List(dec.label, dec.name)
case others: AstNode => List(others.label, others.code)
}).map(StringUtils.normalizeSpace)
.map(escape)
(lineOpt match {
case Some(line) => s"${list.head}, $line" :: list.tail
case None => list
}).distinct // expr.code, toCfgNode(expr).code could be the same
.mkString("<BR/>")
}

private def toCfgNode(node: StoredNode): CfgNode = {
Expand Down