@@ -16,7 +16,7 @@ import scala.util.{Failure, Success, Try}
1616private case class ReachableByTask (sink : nodes.TrackingPoint ,
1717 sources : Set [nodes.TrackingPoint ],
1818 table : ResultTable ,
19- initialPath : List [PathElement ] = List (),
19+ initialPath : Vector [PathElement ] = Vector (),
2020 callDepth : Int = 0 )
2121
2222class Engine (context : EngineContext ) {
@@ -31,9 +31,9 @@ class Engine(context: EngineContext) {
3131 * Initialize a pool of workers and return a "completion service" that
3232 * we can query (in a blocking manner) for completed tasks.
3333 * */
34- private def initializeWorkerPool (): ExecutorCompletionService [List [ReachableByResult ]] = {
34+ private def initializeWorkerPool (): ExecutorCompletionService [Vector [ReachableByResult ]] = {
3535 val executorService : ExecutorService = Executors .newWorkStealingPool()
36- new ExecutorCompletionService [List [ReachableByResult ]](executorService)
36+ new ExecutorCompletionService [Vector [ReachableByResult ]](executorService)
3737 }
3838
3939 /**
@@ -67,20 +67,19 @@ class Engine(context: EngineContext) {
6767 result
6868 }
6969
70- private def newTasksFromResults (resultsOfTask : List [ReachableByResult ],
71- sources : Set [nodes.TrackingPoint ]): List [ReachableByTask ] = {
72- tasksForPartialResults (resultsOfTask, sources) ++ tasksForUnresolvedOutArgs(resultsOfTask, sources)
70+ private def newTasksFromResults (resultsOfTask : Vector [ReachableByResult ],
71+ sources : Set [nodes.TrackingPoint ]): Vector [ReachableByTask ] = {
72+ tasksForParams (resultsOfTask, sources) ++ tasksForUnresolvedOutArgs(resultsOfTask, sources)
7373 }
7474
7575 private def submitTask (task : ReachableByTask ): Unit = {
7676 numberOfTasksRunning += 1
7777 completionService.submit(new ReachableByCallable (task, context))
7878 }
7979
80- private def tasksForPartialResults (resultsOfTask : List [ReachableByResult ],
81- sources : Set [nodes.TrackingPoint ]): List [ReachableByTask ] = {
82- val partialResults = resultsOfTask.filter(_.partial)
83- val pathsFromParams = partialResults.map(x => (x.path, x.callDepth))
80+ private def tasksForParams (resultsOfTask : Vector [ReachableByResult ],
81+ sources : Set [nodes.TrackingPoint ]): Vector [ReachableByTask ] = {
82+ val pathsFromParams = resultsOfTask.map(x => (x.path, x.callDepth))
8483 pathsFromParams.flatMap {
8584 case (path, callDepth) =>
8685 val param = path.head.node
@@ -91,12 +90,12 @@ class Engine(context: EngineContext) {
9190 ReachableByTask (arg, sources, new ResultTable , path, callDepth + 1 )
9291 }
9392 }
94- .getOrElse(List ())
93+ .getOrElse(Vector ())
9594 }
9695 }
9796
98- private def tasksForUnresolvedOutArgs (resultsOfTask : List [ReachableByResult ],
99- sources : Set [nodes.TrackingPoint ]): List [ReachableByTask ] = {
97+ private def tasksForUnresolvedOutArgs (resultsOfTask : Vector [ReachableByResult ],
98+ sources : Set [nodes.TrackingPoint ]): Vector [ReachableByTask ] = {
10099
101100 val outArgsAndCalls = resultsOfTask
102101 .map(x => (x.unresolvedArgs.collect { case e : nodes.Expression => e }, x.path, x.callDepth))
@@ -129,8 +128,8 @@ class Engine(context: EngineContext) {
129128
130129object Engine {
131130
132- def expandIn (curNode : nodes.TrackingPoint , path : List [PathElement ])(
133- implicit semantics : Semantics ): List [PathElement ] = {
131+ def expandIn (curNode : nodes.TrackingPoint , path : Vector [PathElement ])(
132+ implicit semantics : Semantics ): Vector [PathElement ] = {
134133 curNode match {
135134 case argument : nodes.Expression =>
136135 val (arguments, nonArguments) = ddgInE(curNode, path).partition(_.outNode().isInstanceOf [nodes.Expression ])
@@ -150,13 +149,13 @@ object Engine {
150149 PathElement (parentNode, outEdgeLabel = outLabel)
151150 }
152151
153- private def ddgInE (dstNode : nodes.TrackingPoint , path : List [PathElement ]): List [Edge ] = {
152+ private def ddgInE (dstNode : nodes.TrackingPoint , path : Vector [PathElement ]): Vector [Edge ] = {
154153 dstNode
155154 .inE(EdgeTypes .REACHING_DEF )
156155 .asScala
157156 .filter(e => e.outNode().isInstanceOf [nodes.TrackingPoint ])
158157 .filter(e => ! path.map(_.node).contains(e.outNode().asInstanceOf [nodes.TrackingPoint ]))
159- .toList
158+ .toVector
160159 }
161160
162161 /**
@@ -230,19 +229,19 @@ case class EngineConfig(var maxCallDepth: Int = 4)
230229 * @param context state of the data flow engine
231230 * */
232231private class ReachableByCallable (task : ReachableByTask , context : EngineContext )
233- extends Callable [List [ReachableByResult ]] {
232+ extends Callable [Vector [ReachableByResult ]] {
234233
235234 import Engine ._
236235
237236 /**
238237 * Entry point of callable.
239238 * */
240- override def call (): List [ReachableByResult ] = {
239+ override def call (): Vector [ReachableByResult ] = {
241240 if (task.callDepth > context.config.maxCallDepth) {
242- List ()
241+ Vector ()
243242 } else {
244243 implicit val sem : Semantics = context.semantics
245- results(List ( PathElement (task.sink)) ++ task.initialPath, task.sources, task.table)
244+ results(PathElement (task.sink) +: task.initialPath, task.sources, task.table)
246245 task.table.get(task.sink).get.map { r =>
247246 r.copy(callDepth = task.callDepth)
248247 }
@@ -261,33 +260,39 @@ private class ReachableByCallable(task: ReachableByTask, context: EngineContext)
261260 * @param path This is a path from a node to the sink. The first node
262261 * of the path is expanded by this method
263262 * */
264- private def results [NodeType <: nodes.TrackingPoint ](path : List [PathElement ],
265- sources : Set [NodeType ],
266- table : ResultTable )(implicit semantics : Semantics ): Unit = {
263+ private def results [NodeType <: nodes.TrackingPoint ](
264+ path : Vector [PathElement ],
265+ sources : Set [NodeType ],
266+ table : ResultTable )(implicit semantics : Semantics ): Vector [ReachableByResult ] = {
267267 val curNode = path.head.node
268268
269- val resultsForParents : List [ReachableByResult ] = {
270- expandIn(curNode, path).flatMap { parent =>
271- table.createFromTable(parent :: path).getOrElse {
272- results(parent :: path, sources, table)
273- table.get(parent.node).get
269+ val resultsForParents : Vector [ReachableByResult ] = {
270+ expandIn(curNode, path).iterator.flatMap { parent =>
271+ val cachedResult = table.createFromTable(parent, path)
272+ if (cachedResult.isDefined) {
273+ cachedResult.get
274+ } else {
275+ results(parent +: path, sources, table)
274276 }
275- }
277+ }.toVector
276278 }
277279
278280 val resultsForCurNode = {
279281 val endStates = if (sources.contains(curNode.asInstanceOf [NodeType ])) {
280282 List (ReachableByResult (path))
281- } else if (curNode.isInstanceOf [nodes.MethodParameterIn ]) {
283+ } else if ((task.callDepth != context.config.maxCallDepth) && curNode.isInstanceOf [nodes.MethodParameterIn ]) {
282284 List (ReachableByResult (path, partial = true ))
283285 } else {
284286 List ()
285287 }
286288
287289 val retsToResolve = curNode match {
288290 case call : nodes.Call =>
289- if (methodsForCall(call).to(Traversal ).internal.nonEmpty && semanticsForCall(call).isEmpty) {
290- List (ReachableByResult (PathElement (path.head.node, resolved = false ) :: path.tail, partial = true ))
291+ if ((task.callDepth != context.config.maxCallDepth) && methodsForCall(call)
292+ .to(Traversal )
293+ .internal
294+ .nonEmpty && semanticsForCall(call).isEmpty) {
295+ List (ReachableByResult (PathElement (path.head.node, resolved = false ) +: path.tail, partial = true ))
291296 } else {
292297 List ()
293298 }
@@ -296,7 +301,30 @@ private class ReachableByCallable(task: ReachableByTask, context: EngineContext)
296301 endStates ++ retsToResolve
297302 }
298303
299- table.add(curNode, resultsForParents ++ resultsForCurNode)
304+ val res = (resultsForParents ++ resultsForCurNode)
305+ .groupBy { x =>
306+ (x.path.headOption ++ x.path.lastOption, x.partial, x.callDepth)
307+ }
308+ .map {
309+ case (_, list) =>
310+ val lenIdPathPairs = list.map(x => (x.path.length, x)).toList
311+ val withMaxLength = (lenIdPathPairs.sortBy(_._1).reverse match {
312+ case Nil => Nil
313+ case h :: t => h :: t.takeWhile(y => y._1 == h._1)
314+ }).map(_._2)
315+
316+ if (withMaxLength.length == 1 ) {
317+ withMaxLength.head
318+ } else {
319+ withMaxLength.minBy { x =>
320+ x.path.map(_.node.id()).mkString(" -" )
321+ }
322+ }
323+ }
324+ .toVector
325+
326+ table.add(curNode, res)
327+ res
300328 }
301329
302330 private def semanticsForCall (call : nodes.Call )(implicit semantics : Semantics ): List [FlowSemantic ] = {
0 commit comments