Skip to content

Commit 13df7ac

Browse files
committed
fix: control structure & path probability insights
1 parent 6dc412b commit 13df7ac

20 files changed

+172
-125
lines changed

insight/src/main/kotlin/spp/jetbrains/insight/InsightExtensions.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,3 @@ fun ArtifactElement.getDuration(includingPredictions: Boolean = true): Long? {
3434

3535
return getUserData(InsightKeys.FUNCTION_DURATION.asPsiKey())?.value
3636
}
37-
38-
fun ArtifactElement.getPathExecutionProbability(): InsightValue<Double> {
39-
return getUserData(InsightKeys.PATH_EXECUTION_PROBABILITY.asPsiKey())!!
40-
}

insight/src/main/kotlin/spp/jetbrains/insight/InsightKeys.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ object InsightKeys {
3333
val PATH_EXECUTION_PROBABILITY = SourceKey<InsightValue<Double>>(InsightType.PATH_EXECUTION_PROBABILITY.name)
3434
val PATH_DURATION = SourceKey<InsightValue<Double>>(InsightType.PATH_DURATION.name)
3535
val RECURSIVE_CALL = SourceKey<InsightValue<Boolean>>(InsightType.RECURSIVE_CALL.name)
36+
val CONDITION_EVALUATION = SourceKey<Boolean>("CONDITION_EVALUATION")
3637

3738
val ALL_INSIGHTS: List<SourceKey<out InsightValue<out Any>>> = listOf(
3839
FUNCTION_DURATION,

insight/src/main/kotlin/spp/jetbrains/insight/ProceduralAnalyzer.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ class ProceduralAnalyzer {
113113

114114
if (artifactElement is IfArtifact) {
115115
val bool = path.evaluations[boolIndex.getAndIncrement()]
116+
artifactElement.data.put(InsightKeys.CONDITION_EVALUATION, bool)
117+
116118
if (bool) {
117119
val childArtifacts = processArtifacts[index + 1] as List<Any>
118120
processPath(path, artifactElement.childArtifacts, childArtifacts, boolIndex)

insight/src/main/kotlin/spp/jetbrains/insight/pass/artifact/RandomConditionalPass.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,14 @@ class RandomConditionalPass : ArtifactPass {
6262

6363
else -> 0.0
6464
}.let { if (it > 1.0) 1.0 else if (it < 0.0) 0.0 else it }
65-
ifArtifact.data.put(
66-
InsightKeys.CONTROL_STRUCTURE_PROBABILITY,
67-
InsightValue(InsightType.CONTROL_STRUCTURE_PROBABILITY, probability)
68-
)
65+
66+
val conditionEvaluation = ifArtifact.getData(InsightKeys.CONDITION_EVALUATION)!!
67+
if (conditionEvaluation) {
68+
ifArtifact.data[InsightKeys.CONTROL_STRUCTURE_PROBABILITY] =
69+
InsightValue(InsightType.CONTROL_STRUCTURE_PROBABILITY, probability)
70+
} else {
71+
ifArtifact.data[InsightKeys.CONTROL_STRUCTURE_PROBABILITY] =
72+
InsightValue(InsightType.CONTROL_STRUCTURE_PROBABILITY, 1 - probability)
73+
}
6974
}
7075
}

insight/src/main/kotlin/spp/jetbrains/insight/pass/path/PathProbabilityPass.kt

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -33,73 +33,56 @@ import spp.protocol.insight.InsightValue
3333
*/
3434
class PathProbabilityPass : ProceduralPathPass {
3535

36-
private lateinit var conditionOrder: Iterator<Boolean>
37-
3836
override fun analyze(path: ProceduralPath) {
39-
conditionOrder = path.evaluations.iterator()
40-
4137
path.artifacts.forEach {
38+
it.data[InsightKeys.PATH_EXECUTION_PROBABILITY] =
39+
InsightValue.of(PATH_EXECUTION_PROBABILITY, 1.0)
40+
4241
if (it is IfArtifact) {
43-
analyze(it, conditionOrder.next(), 1.0)
44-
} else {
45-
it.data.put(
46-
InsightKeys.PATH_EXECUTION_PROBABILITY,
47-
InsightValue.of(PATH_EXECUTION_PROBABILITY, 1.0)
48-
)
42+
analyze(it, it.getData(InsightKeys.CONDITION_EVALUATION)!!, 1.0)
4943
}
5044
}
5145
}
5246

5347
private fun analyze(ifArtifact: IfArtifact, condition: Boolean, probability: Double) {
5448
val probability = calculateProbability(ifArtifact, probability, condition)
5549
ifArtifact.childArtifacts.forEach {
50+
it.data[InsightKeys.PATH_EXECUTION_PROBABILITY] =
51+
InsightValue.of(PATH_EXECUTION_PROBABILITY, probability)
52+
5653
if (it is IfArtifact) {
57-
analyze(it, conditionOrder.next(), probability)
58-
} else {
59-
it.data.put(
60-
InsightKeys.PATH_EXECUTION_PROBABILITY,
61-
InsightValue.of(PATH_EXECUTION_PROBABILITY, probability)
62-
)
54+
analyze(it, it.getData(InsightKeys.CONDITION_EVALUATION)!!, probability)
6355
}
6456
}
6557
}
6658

6759
private fun calculateProbability(element: ArtifactElement, baseProbability: Double, condition: Boolean): Double {
6860
var selfProbability = Double.NaN
69-
if (element.getUserData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY.asPsiKey()) != null) {
70-
selfProbability = element.getUserData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY.asPsiKey())!!.value
71-
} else if (element.getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY) != null) {
61+
if (element.getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY) != null) {
7262
selfProbability = element.getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY)!!.value
7363
}
7464

7565
//see if probability can be determined statically
76-
if (element is IfArtifact) {
66+
if (element is IfArtifact && selfProbability.isNaN()) {
7767
val staticProbability = element.getStaticProbability()
7868
if (!staticProbability.isNaN()) {
7969
selfProbability = staticProbability
80-
}
81-
}
8270

83-
if (selfProbability.isNaN()) {
84-
return baseProbability
85-
}
71+
//todo: move to getStaticProbability()
72+
//flip self probability if condition is false
73+
if (!condition) {
74+
selfProbability = 1 - selfProbability
75+
}
8676

87-
//flip self probability if condition is false
88-
if (!condition) {
89-
selfProbability = 1 - selfProbability
77+
element.data[InsightKeys.CONTROL_STRUCTURE_PROBABILITY] =
78+
InsightValue.of(CONTROL_STRUCTURE_PROBABILITY, selfProbability)
79+
}
9080
}
9181

92-
element.putUserData(
93-
InsightKeys.PATH_EXECUTION_PROBABILITY.asPsiKey(),
94-
InsightValue.of(PATH_EXECUTION_PROBABILITY, baseProbability)
95-
)
96-
97-
val childProbability = baseProbability * selfProbability
98-
element.data.put(
99-
InsightKeys.PATH_EXECUTION_PROBABILITY,
100-
InsightValue.of(PATH_EXECUTION_PROBABILITY, childProbability)
101-
)
102-
103-
return childProbability
82+
return if (selfProbability.isNaN()) {
83+
baseProbability
84+
} else {
85+
baseProbability * selfProbability
86+
}
10487
}
10588
}

insight/src/main/kotlin/spp/jetbrains/insight/pass/pathset/SimplifyPathSetPass.kt

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@
1616
*/
1717
package spp.jetbrains.insight.pass.pathset
1818

19-
import spp.jetbrains.artifact.model.ControlStructureArtifact
2019
import spp.jetbrains.artifact.model.IfArtifact
2120
import spp.jetbrains.insight.InsightKeys
2221
import spp.jetbrains.insight.ProceduralPath
2322
import spp.jetbrains.insight.pass.ProceduralPathSetPass
24-
import java.util.*
2523

2624
/**
2725
* Removes paths caused by conditional branches that are never taken.
@@ -31,33 +29,20 @@ class SimplifyPathSetPass : ProceduralPathSetPass {
3129
override fun postProcess(pathSet: Set<ProceduralPath>): Set<ProceduralPath> {
3230
val simplifiedPaths = mutableSetOf<ProceduralPath>()
3331
for (path in pathSet) {
34-
path.artifacts.removeIf {
32+
val possiblePath = path.artifacts.all {
3533
if (it is IfArtifact) {
36-
val probability = it.getData(InsightKeys.PATH_EXECUTION_PROBABILITY)
37-
probability?.value == 0.0 || it.childArtifacts.isEmpty()
34+
val probability = it.getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY)
35+
probability?.value != 0.0
3836
} else {
39-
false
37+
true
4038
}
4139
}
4240

43-
if (path.artifacts.isNotEmpty()) {
41+
if (possiblePath) {
4442
simplifiedPaths.add(path)
4543
}
4644
}
4745

48-
for (path in pathSet.toMutableSet()) {
49-
//sublist check
50-
val pathArtifacts = path.artifacts
51-
val dupePath = simplifiedPaths.any {
52-
it !== path && pathArtifacts.none { it is ControlStructureArtifact }
53-
&& Collections.indexOfSubList(it.artifacts, pathArtifacts) != -1
54-
}
55-
56-
if (dupePath) {
57-
simplifiedPaths.remove(path)
58-
}
59-
}
60-
6146
return simplifiedPaths
6247
}
6348
}

insight/src/main/kotlin/spp/jetbrains/insight/pass/pathset/StaticDfaPathSetPass.kt

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class StaticDfaPathSetPass : ProceduralPathSetPass {
6060
}
6161

6262
listener.constantConditions.forEach {
63-
if (it.key is KotlinAnchor.KotlinExpressionAnchor) {
63+
if (it.key is KotlinAnchor.KotlinExpressionAnchor && it.value != ConstantValue.UNKNOWN) {
6464
val anchor = it.key as KotlinAnchor.KotlinExpressionAnchor
6565
val value = it.value
6666
val expression = anchor.expression
@@ -69,15 +69,19 @@ class StaticDfaPathSetPass : ProceduralPathSetPass {
6969
if (it is IfArtifact) {
7070
if (it.condition?.psiElement == expression) {
7171
val probability = if (value == ConstantValue.TRUE) 1.0 else 0.0
72-
it.data.put(
73-
InsightKeys.CONTROL_STRUCTURE_PROBABILITY,
74-
InsightValue.of(InsightType.CONTROL_STRUCTURE_PROBABILITY, probability)
75-
)
72+
val conditionEvaluation = it.getData(InsightKeys.CONDITION_EVALUATION)!!
73+
if (conditionEvaluation) {
74+
it.data[InsightKeys.CONTROL_STRUCTURE_PROBABILITY] =
75+
InsightValue(InsightType.CONTROL_STRUCTURE_PROBABILITY, probability)
76+
} else {
77+
it.data[InsightKeys.CONTROL_STRUCTURE_PROBABILITY] =
78+
InsightValue(InsightType.CONTROL_STRUCTURE_PROBABILITY, 1 - probability)
79+
}
7680
}
7781
}
7882
}
7983
}
80-
} else if (it.key is JavaExpressionAnchor) {
84+
} else if (it.key is JavaExpressionAnchor && it.value != ConstantValue.UNKNOWN) {
8185
val anchor = it.key as JavaExpressionAnchor
8286
val value = it.value
8387
val expression = anchor.expression
@@ -86,10 +90,14 @@ class StaticDfaPathSetPass : ProceduralPathSetPass {
8690
if (it is IfArtifact) {
8791
if (it.condition?.psiElement == expression) {
8892
val probability = if (value == ConstantValue.TRUE) 1.0 else 0.0
89-
it.data.put(
90-
InsightKeys.CONTROL_STRUCTURE_PROBABILITY,
91-
InsightValue.of(InsightType.CONTROL_STRUCTURE_PROBABILITY, probability)
92-
)
93+
val conditionEvaluation = it.getData(InsightKeys.CONDITION_EVALUATION)!!
94+
if (conditionEvaluation) {
95+
it.data[InsightKeys.CONTROL_STRUCTURE_PROBABILITY] =
96+
InsightValue(InsightType.CONTROL_STRUCTURE_PROBABILITY, probability)
97+
} else {
98+
it.data[InsightKeys.CONTROL_STRUCTURE_PROBABILITY] =
99+
InsightValue(InsightType.CONTROL_STRUCTURE_PROBABILITY, 1 - probability)
100+
}
93101
}
94102
}
95103
}

insight/src/test/kotlin/spp/jetbrains/insight/BranchProbabilityTest.kt

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,11 @@ import com.intellij.testFramework.TestDataPath
2020
import com.intellij.testFramework.fixtures.BasePlatformTestCase
2121
import org.junit.jupiter.api.Test
2222
import spp.jetbrains.artifact.service.getCalls
23-
import spp.jetbrains.artifact.service.getChildIfs
2423
import spp.jetbrains.artifact.service.toArtifact
2524
import spp.jetbrains.marker.js.JavascriptLanguageProvider
2625
import spp.jetbrains.marker.jvm.JVMLanguageProvider
2726
import spp.jetbrains.marker.py.PythonLanguageProvider
2827
import spp.jetbrains.marker.service.*
29-
import spp.protocol.insight.InsightType
30-
import spp.protocol.insight.InsightValue
3128

3229
@TestDataPath("\$CONTENT_ROOT/testData/")
3330
class BranchProbabilityTest : BasePlatformTestCase() {
@@ -55,23 +52,16 @@ class BranchProbabilityTest : BasePlatformTestCase() {
5552
private fun doOneFourthProbability(language: String, extension: String) {
5653
val psi = myFixture.configureByFile("$language/BranchProbability.$extension")
5754

58-
psi.getChildIfs().forEach {
59-
it.putUserData(
60-
InsightKeys.CONTROL_STRUCTURE_PROBABILITY.asPsiKey(),
61-
InsightValue.of(InsightType.CONTROL_STRUCTURE_PROBABILITY, 0.5)
62-
)
63-
}
64-
6555
val callExpression = psi.getCalls().find { it.text.contains("true", true) }!!
6656
val paths = ProceduralAnalyzer().analyzeUp(callExpression.toArtifact()!!)
6757
assertEquals(1, paths.size)
6858

6959
val path = paths.first()
7060
assertEquals(4, path.descendants.size)
71-
assertEquals(1.0, path.descendants[0].getPathExecutionProbability().value)
72-
assertEquals(0.5, path.descendants[0].getData(InsightKeys.PATH_EXECUTION_PROBABILITY)?.value)
73-
assertEquals(0.5, path.descendants[1].getPathExecutionProbability().value)
74-
assertEquals(0.25, path.descendants[1].getData(InsightKeys.PATH_EXECUTION_PROBABILITY)?.value)
61+
assertEquals(1.0, path.descendants[0].getData(InsightKeys.PATH_EXECUTION_PROBABILITY)?.value)
62+
assertEquals(0.5, path.descendants[0].getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY)?.value)
63+
assertEquals(0.5, path.descendants[1].getData(InsightKeys.PATH_EXECUTION_PROBABILITY)?.value)
64+
assertEquals(0.5, path.descendants[1].getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY)?.value)
7565
assertEquals(0.25, path.descendants[2].getData(InsightKeys.PATH_EXECUTION_PROBABILITY)?.value)
7666
}
7767
}

insight/src/test/kotlin/spp/jetbrains/insight/InnerBranchProbabilityTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class InnerBranchProbabilityTest : BasePlatformTestCase() {
6262
val falsePath = ProceduralAnalyzer().apply {
6363
passProvider = InsightPassProvider.FULL_NO_SIMPLIFY
6464
}.analyzeUp(callExpression2.toArtifact()!!)
65-
assertEquals(0.0, falsePath.first().artifacts.last().getData(InsightKeys.PATH_EXECUTION_PROBABILITY)?.value)
65+
assertEquals(1.0, falsePath.first().artifacts.last().getData(InsightKeys.PATH_EXECUTION_PROBABILITY)?.value)
66+
assertEquals(0.0, falsePath.first().artifacts.last().getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY)?.value)
6667
}
6768
}

insight/src/test/kotlin/spp/jetbrains/insight/pass/artifact/RandomConditionalPassTest.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,16 @@ class RandomConditionalPassTest : BasePlatformTestCase() {
5555
private fun doTest(language: String, extension: String) {
5656
val psi = myFixture.configureByFile("$language/RandomConditional.$extension")
5757
val paths = ProceduralAnalyzer().analyze(psi.getFunctions().first().toArtifact()!!)
58-
assertEquals(1, paths.size)
58+
assertEquals(2, paths.size)
5959

60-
val ifArtifact = paths.first().artifacts.first() as IfArtifact
61-
assertEquals(0.26, ifArtifact.getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY)!!.value)
60+
val falsePath = paths.first { !it.conditions.first().first }
61+
val ifArtifactFalse = falsePath.artifacts.first() as IfArtifact
62+
assertEquals(1.0, ifArtifactFalse.getData(InsightKeys.PATH_EXECUTION_PROBABILITY)!!.value)
63+
assertEquals(0.74, ifArtifactFalse.getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY)!!.value)
64+
65+
val truePath = paths.first { it.conditions.first().first }
66+
val ifArtifactTrue = truePath.artifacts.first() as IfArtifact
67+
assertEquals(1.0, ifArtifactTrue.getData(InsightKeys.PATH_EXECUTION_PROBABILITY)!!.value)
68+
assertEquals(0.26, ifArtifactTrue.getData(InsightKeys.CONTROL_STRUCTURE_PROBABILITY)!!.value)
6269
}
6370
}

0 commit comments

Comments
 (0)