diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..74afbdd --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,37 @@ +name: Scala CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: '11' + + - name: Cache SBT + uses: actions/cache@v3 + with: + path: | + ~/.ivy2/cache + ~/.sbt + key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }} + restore-keys: | + ${{ runner.os }}-sbt- + + - name: Run sbt tests + run: sbt test diff --git a/build.sbt b/build.sbt index 5b5cd8c..f76adc6 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ scalaVersion := "2.12.8" name := "pdg-sdg" organization := "br.ufpe.cin" -version := "0.7.0" +version := "0.4.41" githubOwner := "spgroup" githubRepository := "pdg-sdg" @@ -11,34 +11,23 @@ githubTokenSource := TokenSource.GitConfig("github.token") parallelExecution in Test := false -resolvers += "soot snapshots" at "https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-snapshot/" - -resolvers += "soot releases" at "https://soot-build.cs.uni-paderborn.de/nexus/repository/soot-release/" - +resolvers += "Maven Central" at "https://repo1.maven.org/maven2/" +resolvers += "SPG Maven Repository" at "https://maven.pkg.github.com/spgroup/soot/" +resolvers += "SVFA releases" at "https://maven.pkg.github.com/spgroup/svfa-scala/" resolvers += "Local maven repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository" resolvers += Classpaths.typesafeReleases -resolvers += "SVFA releases" at "https://maven.pkg.github.com/galilasmb/svfa-scala/" - libraryDependencies += "org.typelevel" %% "cats-core" % "1.6.0" - -libraryDependencies += "ca.mcgill.sable" % "soot" % "3.3.0" +libraryDependencies += "org.soot-oss" % "soot" % "4.5.1" libraryDependencies += "com.google.guava" % "guava" % "27.1-jre" libraryDependencies += "org.scala-graph" %% "graph-core" % "1.13.0" - libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3" - libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2" - libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test" - libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.0.1" % "provided" - libraryDependencies += "javax.servlet" % "servlet-api" % "2.5" % "provided" - libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2" - -libraryDependencies += "br.unb.cic" % "svfa-scala_2.12" % "0.7.0" +libraryDependencies += "br.unb.cic" % "svfa-scala_2.12" % "0.5.13" parallelExecution in Test := false diff --git a/src/main/scala/br/ufpe/cin/soot/analysis/jimple/JDFP.scala b/src/main/scala/br/ufpe/cin/soot/analysis/jimple/JDFP.scala index e0c91d8..4437cc6 100644 --- a/src/main/scala/br/ufpe/cin/soot/analysis/jimple/JDFP.scala +++ b/src/main/scala/br/ufpe/cin/soot/analysis/jimple/JDFP.scala @@ -2,15 +2,13 @@ package br.ufpe.cin.soot.analysis.jimple import br.unb.cic.soot.graph.VisitedMethods import br.unb.cic.soot.svfa.jimple.{AssignStmt, InvalidStmt, InvokeStmt, JSVFA, Statement} -import soot.PackManager +import soot.{Local, PackManager, Scene, SceneTransformer, SootMethod, Transform} import soot.jimple._ import soot.toolkits.graph.ExceptionalUnitGraph import soot.toolkits.scalar.SimpleLocalDefs -import soot.{Local, Scene, SceneTransformer, SootMethod, Transform} -import java.io.{FileWriter, IOException} -import java.text.{DecimalFormat, NumberFormat} import java.util +import scala.collection.convert.ImplicitConversions.`collection asJava` import scala.collection.mutable.ListBuffer @@ -46,7 +44,7 @@ abstract class JDFP extends JSVFA{ class TransformerDFP extends SceneTransformer { override def internalTransform(phaseName: String, options: util.Map[String, String]): scala.Unit = { pointsToAnalysis = Scene.v().getPointsToAnalysis - Scene.v().getEntryPoints.forEach(method => { + getAnalysisEntryPoint().forEach(method => { traverseDFP(method, new ListBuffer[VisitedMethods]()) methods = methods + 1 }) @@ -132,6 +130,10 @@ abstract class JDFP extends JSVFA{ val stmt = base.asInstanceOf[soot.jimple.ReturnStmt] } + def getAnalysisEntryPoint(): util.List[SootMethod] = { + Scene.v().getEntryPoints + } + def traverse(stmt: ReturnStmt, method: SootMethod, defs: SimpleLocalDefs, visitedMethods: ListBuffer[VisitedMethods]) : scala.Unit = { val op = stmt.stmt.getUseBoxes diff --git a/src/main/scala/br/ufpe/cin/soot/graph/LocalDummy.java b/src/main/scala/br/ufpe/cin/soot/graph/LocalDummy.java index ab08faf..bafb40d 100644 --- a/src/main/scala/br/ufpe/cin/soot/graph/LocalDummy.java +++ b/src/main/scala/br/ufpe/cin/soot/graph/LocalDummy.java @@ -21,10 +21,6 @@ public class LocalDummy implements Local, ConvertToBaf { public LocalDummy(String name, Type type) { setName(name); setType(type); - Numberer numberer = Scene.v().getLocalNumberer(); - if (numberer != null) { - numberer.add(this); - } } /** Returns true if the given object is structurally equal to this one. */ @@ -78,6 +74,11 @@ public void setType(Type t) { this.type = t; } + @Override + public boolean isStackLocal() { + return true; + } + @Override public String toString() { return getName(); diff --git a/src/test/java/samples/PointerAnalysis/Main.java b/src/test/java/samples/PointerAnalysis/Main.java new file mode 100644 index 0000000..edba457 --- /dev/null +++ b/src/test/java/samples/PointerAnalysis/Main.java @@ -0,0 +1,9 @@ +package samples.PointerAnalysis; + +public class Main { + public static void main(String[] ars) { + Report r = new ReportAdvanced(); + Text t = new Text(r); + t.generateReport(); + } +} diff --git a/src/test/java/samples/PointerAnalysis/Report.java b/src/test/java/samples/PointerAnalysis/Report.java new file mode 100644 index 0000000..7839dc7 --- /dev/null +++ b/src/test/java/samples/PointerAnalysis/Report.java @@ -0,0 +1,11 @@ +package samples.PointerAnalysis; + +public interface Report { + + + void countDupWords(); + + void countDupWhiteSpace(); + + void countComments(); +} diff --git a/src/test/java/samples/PointerAnalysis/ReportAdvanced.java b/src/test/java/samples/PointerAnalysis/ReportAdvanced.java new file mode 100644 index 0000000..78cfd78 --- /dev/null +++ b/src/test/java/samples/PointerAnalysis/ReportAdvanced.java @@ -0,0 +1,21 @@ +package samples.PointerAnalysis; + +public class ReportAdvanced implements Report { + int fixes = 0; + + @Override + public void countDupWords() { + fixes++; + } + + @Override + public void countDupWhiteSpace() { + int count = fixes; + //fixes = count; + } + + @Override + public void countComments() { + + } +} diff --git a/src/test/java/samples/PointerAnalysis/ReportSimple.java b/src/test/java/samples/PointerAnalysis/ReportSimple.java new file mode 100644 index 0000000..2674aa7 --- /dev/null +++ b/src/test/java/samples/PointerAnalysis/ReportSimple.java @@ -0,0 +1,23 @@ +package samples.PointerAnalysis; + +public class ReportSimple implements Report { + int dupWords = 0; + int dupWhiteSpace = 0; + + @Override + public void countDupWords() { + dupWords++; + } + + @Override + public void countDupWhiteSpace() { + dupWhiteSpace++; + } + + @Override + public void countComments() { + + } + + +} diff --git a/src/test/java/samples/PointerAnalysis/Text.java b/src/test/java/samples/PointerAnalysis/Text.java new file mode 100644 index 0000000..c0a756c --- /dev/null +++ b/src/test/java/samples/PointerAnalysis/Text.java @@ -0,0 +1,15 @@ +package samples.PointerAnalysis; + +public class Text { + private Report r; + Text(Report r) { + this.r = r; + } + void generateReport() { + r = new ReportSimple(); + //Text t = new Text(report); + r.countDupWords(); // LEFT + r.countComments(); + r.countDupWhiteSpace(); // RIGHT + } +} diff --git a/src/test/scala/br/ufpe/cin/soot/DFPTest.scala b/src/test/scala/br/ufpe/cin/soot/DFPTest.scala index 69cb8f4..ab10c5c 100644 --- a/src/test/scala/br/ufpe/cin/soot/DFPTest.scala +++ b/src/test/scala/br/ufpe/cin/soot/DFPTest.scala @@ -1,11 +1,17 @@ package br.ufpe.cin.soot import br.unb.cic.soot.graph.{NodeType, SimpleNode, SinkNode, SourceNode} +import br.unb.cic.soot.svfa.{CG, CHA, SPARK} +import soot.SootMethod class DFPTest(leftchangedlines: Array[Int], rightchangedlines: Array[Int], className: String, mainMethod: String) extends JDFPTest { override def getClassName(): String = className override def getMainMethod(): String = mainMethod + override def callGraph(): CG = SPARK + + + // override def getClassName(): String = "samples.BlackBoard" // override def getMainMethod(): String = "main" diff --git a/src/test/scala/br/ufpe/cin/soot/JDFPTest.scala b/src/test/scala/br/ufpe/cin/soot/JDFPTest.scala index 747b9b1..6e8c754 100644 --- a/src/test/scala/br/ufpe/cin/soot/JDFPTest.scala +++ b/src/test/scala/br/ufpe/cin/soot/JDFPTest.scala @@ -4,6 +4,9 @@ import br.ufpe.cin.soot.analysis.jimple.JDFP import br.unb.cic.soot.svfa.jimple.{FieldSensitive, Interprocedural, PropagateTaint} import soot.{Scene, SootMethod} +import java.util +import scala.collection.JavaConverters.collectionAsScalaIterableConverter + abstract class JDFPTest extends JDFP with Interprocedural with FieldSensitive with PropagateTaint{ def getClassName(): String def getMainMethod(): String @@ -13,12 +16,65 @@ abstract class JDFPTest extends JDFP with Interprocedural with FieldSensitive w override def applicationClassPath(): List[String] = List("target/scala-2.12/test-classes", System.getProperty("user.home")+"/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar") override def getEntryPoints(): List[SootMethod] = { + var mainMethods: List[SootMethod] = findMainMethods() + if (mainMethods.isEmpty) { + mainMethods = findPublicMethods() + } + mainMethods + } + + override def getAnalysisEntryPoint(): util.List[SootMethod] = { val sootClass = Scene.v().getSootClass(getClassName()) - List(sootClass.getMethodByName(getMainMethod())) + val method = sootClass.getMethodByName(getMainMethod()) + val entryPoints = new util.ArrayList[SootMethod]() + entryPoints.add(method) + entryPoints } override def getIncludeList(): List[String] = List( // "java.lang.*", // "java.util.*" ) + + + def findMainMethods(): List[SootMethod] = { + val mainMethods = new util.ArrayList[SootMethod]() + + val classes = Scene.v().getApplicationClasses + val it = classes.iterator() + while (it.hasNext) { + val sootClass = it.next() + for (method <- sootClass.getMethods.asScala) { + if (isMainMethod(method)) { + mainMethods.add(method) + } + } + } + + mainMethods.asScala.toList + } + + private def isMainMethod(method: SootMethod): Boolean = { + method.getName == "main" && + method.isStatic && + method.getReturnType.toString == "void" && + method.getParameterCount == 1 && + method.getParameterType(0).toString == "java.lang.String[]" + } + + def findPublicMethods(): List[SootMethod] = { + val publicMethods = new util.ArrayList[SootMethod]() + + val classes = Scene.v().getApplicationClasses + val it = classes.iterator() + while (it.hasNext) { + val sootClass = it.next() + for (method <- sootClass.getMethods.asScala) { + publicMethods.add(method) + } + } + + publicMethods.asScala.toList + } + } diff --git a/src/test/scala/br/ufpe/cin/soot/TestSuite.scala b/src/test/scala/br/ufpe/cin/soot/TestSuite.scala index a4e90df..e024307 100644 --- a/src/test/scala/br/ufpe/cin/soot/TestSuite.scala +++ b/src/test/scala/br/ufpe/cin/soot/TestSuite.scala @@ -126,11 +126,28 @@ class TestSuite extends FunSuite with BeforeAndAfter { println(pdg.pdgToDotModel()) - assert(pdg.pdg.nodes.size == 9) - assert(pdg.pdg.numberOfEdges() == 13) + assert(pdg.pdg.nodes.size == 15) + assert(pdg.pdg.numberOfEdges() == 20) assert(pdg.reportConflictsPDG().size == 1) } + test("we should correctly compute the number of nodes and edges in the PointerAnalysis sample") { + val className = "samples.PointerAnalysis.Text" + val mainMethod = "generateReport" + + + jdfp = new DFPTest(Array(11), Array(13), className, mainMethod) + jdfp.configureSoot() + jdfp.buildDFP() + jdfp.setPrintDepthVisitedMethods(true) + + //println(jdfp.svgToDotModel()) + println("traversedMethods: " + jdfp.traversedMethods) + + println("conflictingPaths: " + jdfp.svg.findConflictingPaths()) + assert(jdfp.reportConflictsSVG().size == 1) + } + test("we should correctly compute the number of nodes and edges in the PDG4 sample") { val className = "samples.PDG4" val mainMethod = "main" @@ -144,7 +161,7 @@ class TestSuite extends FunSuite with BeforeAndAfter { println(pdg.pdgToDotModel()) assert(pdg.pdg.nodes.size == 9) - assert(pdg.pdg.numberOfEdges() == 9) + assert(pdg.pdg.numberOfEdges() == 8) assert(pdg.reportConflictsPDG().size == 1) } @@ -160,8 +177,8 @@ class TestSuite extends FunSuite with BeforeAndAfter { println(pdg.pdgToDotModel()) - assert(pdg.pdg.nodes.size == 8) - assert(pdg.pdg.numberOfEdges() == 10) + assert(pdg.pdg.nodes.size == 12) + assert(pdg.pdg.numberOfEdges() == 14) assert(pdg.reportConflictsPDG().size >= 1) } @@ -178,7 +195,7 @@ class TestSuite extends FunSuite with BeforeAndAfter { println(pdg.pdgToDotModel()) assert(pdg.pdg.nodes.size == 8) - assert(pdg.pdg.numberOfEdges() == 8) + assert(pdg.pdg.numberOfEdges() == 7) assert(pdg.reportConflictsPDG().size == 0) } @@ -210,7 +227,7 @@ class TestSuite extends FunSuite with BeforeAndAfter { println(pdg.pdgToDotModel()) assert(pdg.pdg.nodes.size == 8) - assert(pdg.pdg.numberOfEdges() == 8) + assert(pdg.pdg.numberOfEdges() == 7) assert(pdg.reportConflictsPDG().size == 2) } @@ -258,7 +275,7 @@ class TestSuite extends FunSuite with BeforeAndAfter { val svfa = new IfElseTest() svfa.configureSoot() svfa.buildSparseValueFlowGraph() - assert(svfa.svg.nodes.size == 17) + assert(svfa.svg.nodes.size == 22) } test("we should correctly compute the number of edges of the IfElseTest sample") { @@ -266,7 +283,7 @@ class TestSuite extends FunSuite with BeforeAndAfter { svfa.configureSoot() svfa.buildSparseValueFlowGraph() println(svfa.svgToDotModel()) - assert(svfa.svg.numberOfEdges() == 19) + assert(svfa.svg.numberOfEdges() == 29) } test("we should find exactly one conflict in this analysis of the IfElseTest sample") {