Skip to content

Commit d5d9536

Browse files
committed
add optional live variables analysis
1 parent b6a409e commit d5d9536

File tree

10 files changed

+404
-115
lines changed

10 files changed

+404
-115
lines changed

usvm-python/cpythonadapter/cpython

Submodule cpython updated 4134 files

usvm-ts-dataflow/src/main/kotlin/org/usvm/dataflow/ts/infer/Alias.kt

+68-75
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.usvm.dataflow.ts.infer
22

3-
import mu.KotlinLogging
43
import org.jacodb.ets.base.EtsArrayAccess
54
import org.jacodb.ets.base.EtsAssignStmt
65
import org.jacodb.ets.base.EtsBinaryExpr
@@ -22,22 +21,29 @@ import org.jacodb.ets.base.EtsThis
2221
import org.jacodb.ets.base.EtsUnaryExpr
2322
import org.jacodb.ets.model.EtsClassSignature
2423
import org.jacodb.ets.model.EtsMethod
24+
import kotlin.collections.ArrayDeque
2525

26-
private val logger = KotlinLogging.logger {}
26+
interface StmtAliasInfo {
27+
fun getAliases(path: AccessPath): Set<AccessPath>
28+
}
29+
30+
interface MethodAliasInfo {
31+
fun computeAliases(): List<StmtAliasInfo>
32+
}
2733

2834
@OptIn(ExperimentalUnsignedTypes::class)
29-
class StmtAliasInfo(
35+
class StmtAliasInfoImpl(
3036
val baseToAlloc: IntArray,
3137
val allocToFields: Array<ULongArray>,
32-
val method: MethodAliasInfo,
33-
) {
34-
companion object {
35-
const val NOT_PROCESSED = -1
36-
const val MULTIPLE_EDGE = -2
38+
val method: MethodAliasInfoImpl,
39+
) : StmtAliasInfo {
40+
internal companion object {
41+
internal const val NOT_PROCESSED = -1
42+
internal const val MULTIPLE_EDGE = -2
3743

38-
const val ELEMENT_ACCESSOR = -3
44+
internal const val ELEMENT_ACCESSOR = -3
3945

40-
fun merge(first: Int, second: Int): Int = when {
46+
internal fun merge(first: Int, second: Int): Int = when {
4147
first == NOT_PROCESSED -> second
4248
second == NOT_PROCESSED -> first
4349
first == MULTIPLE_EDGE -> MULTIPLE_EDGE
@@ -46,19 +52,19 @@ class StmtAliasInfo(
4652
else -> MULTIPLE_EDGE
4753
}
4854

49-
fun wrap(string: Int, alloc: Int): ULong {
55+
internal fun wrap(string: Int, alloc: Int): ULong {
5056
return (string.toULong() shl Int.SIZE_BITS) or alloc.toULong()
5157
}
5258

53-
fun unwrap(edge: ULong): Pair<Int, Int> {
59+
internal fun unwrap(edge: ULong): Pair<Int, Int> {
5460
val string = (edge shr Int.SIZE_BITS).toInt()
5561
val allocation = (edge and UInt.MAX_VALUE.toULong()).toInt()
5662
return Pair(string, allocation)
5763
}
5864
}
5965

60-
fun merge(other: StmtAliasInfo): StmtAliasInfo {
61-
val merged = StmtAliasInfo(
66+
internal fun merge(other: StmtAliasInfoImpl): StmtAliasInfoImpl {
67+
val merged = StmtAliasInfoImpl(
6268
baseToAlloc = IntArray(method.bases.size) { NOT_PROCESSED },
6369
allocToFields = Array(method.allocations.size) { ulongArrayOf() },
6470
method = method
@@ -71,11 +77,11 @@ class StmtAliasInfo(
7177
val toFieldsMap = mutableMapOf<Int, Int>()
7278
allocToFields[i].forEach {
7379
val (s, a) = unwrap(it)
74-
toFieldsMap.merge(s, a, ::merge)
80+
toFieldsMap.merge(s, a, Companion::merge)
7581
}
7682
other.allocToFields[i].forEach {
7783
val (s, a) = unwrap(it)
78-
toFieldsMap.merge(s, a, ::merge)
84+
toFieldsMap.merge(s, a, Companion::merge)
7985
}
8086
merged.allocToFields[i] = toFieldsMap
8187
.map { (string, alloc) -> wrap(string, alloc) }
@@ -102,10 +108,9 @@ class StmtAliasInfo(
102108
nodes.add(MULTIPLE_EDGE)
103109
strings.add(ELEMENT_ACCESSOR)
104110
}
105-
106111
is FieldAccessor -> {
107112
val string = method.stringMap[accessor.name]
108-
?: error("Unknown field name: ${accessor.name}")
113+
?: error("Unknown field name")
109114
strings.add(string)
110115

111116
node = allocToFields[node]
@@ -122,17 +127,17 @@ class StmtAliasInfo(
122127
return Pair(nodes, strings)
123128
}
124129

125-
fun assign(lhv: AccessPath, rhv: AccessPath): StmtAliasInfo {
130+
private fun assign(lhv: AccessPath, rhv: AccessPath): StmtAliasInfoImpl {
126131
val (rhvNodes, _) = trace(rhv)
127132
val newAlloc = rhvNodes.last()
128133
return assign(lhv, newAlloc)
129134
}
130135

131-
fun assign(lhv: AccessPath, newAlloc: Int): StmtAliasInfo {
136+
private fun assign(lhv: AccessPath, newAlloc: Int): StmtAliasInfoImpl {
132137
val (lhvNodes, lhvEdges) = trace(lhv)
133138
val from = lhvNodes.reversed().getOrNull(1)
134139
if (from != null) {
135-
val updated = StmtAliasInfo(
140+
val updated = StmtAliasInfoImpl(
136141
baseToAlloc = baseToAlloc,
137142
allocToFields = allocToFields.copyOf(),
138143
method = method,
@@ -155,7 +160,7 @@ class StmtAliasInfo(
155160

156161
return updated
157162
} else {
158-
val updated = StmtAliasInfo(
163+
val updated = StmtAliasInfoImpl(
159164
baseToAlloc = baseToAlloc.copyOf(),
160165
allocToFields = allocToFields,
161166
method = method,
@@ -169,66 +174,58 @@ class StmtAliasInfo(
169174
}
170175
}
171176

172-
fun applyStmt(stmt: EtsStmt): StmtAliasInfo? {
177+
internal fun applyStmt(stmt: EtsStmt): StmtAliasInfoImpl {
173178
if (stmt !is EtsAssignStmt) {
174179
return this
175180
}
176181
when (val rhv = stmt.rhv) {
177182
is EtsParameterRef -> {
178-
val alloc = method.allocationMap[MethodAliasInfo.Allocation.Arg(rhv.index)]
179-
?: error("Unknown parameter ref in stmt: $stmt")
183+
val alloc = method.allocationMap[MethodAliasInfoImpl.Allocation.Arg(rhv.index)]
184+
?: error("Unknown parameter ref")
180185
return assign(stmt.lhv.toPath(), alloc)
181186
}
182-
183187
is EtsThis -> {
184-
val alloc = method.allocationMap[MethodAliasInfo.Allocation.This]
185-
?: error("Unknown this in stmt: $stmt")
188+
val alloc = method.allocationMap[MethodAliasInfoImpl.Allocation.This]
189+
?: error("Uninitialized this")
186190
return assign(stmt.lhv.toPath(), alloc)
187191
}
188-
189192
is EtsInstanceFieldRef, is EtsStaticFieldRef -> {
190193
val (rhvNodes, _) = trace(rhv.toPath())
191194
val alloc = rhvNodes.last()
192195
if (alloc == NOT_PROCESSED) {
193-
val fieldAlloc = method.allocationMap[MethodAliasInfo.Allocation.Imm(stmt)]
194-
?: error("Unknown allocation in stmt: $stmt")
196+
val fieldAlloc = method.allocationMap[MethodAliasInfoImpl.Allocation.Imm(stmt)]
197+
?: error("Unknown allocation")
198+
195199
return this
196200
.assign(rhv.toPath(), fieldAlloc)
197201
.assign(stmt.lhv.toPath(), fieldAlloc)
198202
} else {
199203
return assign(stmt.lhv.toPath(), alloc)
200204
}
201205
}
202-
203206
is EtsLocal -> {
204207
return assign(stmt.lhv.toPath(), rhv.toPath())
205208
}
206-
207209
is EtsCastExpr -> {
208210
return assign(stmt.lhv.toPath(), rhv.arg.toPath())
209211
}
210-
211212
is EtsConstant, is EtsUnaryExpr, is EtsBinaryExpr, is EtsArrayAccess, is EtsInstanceOfExpr -> {
212-
val imm = method.allocationMap[MethodAliasInfo.Allocation.Expr(stmt)]
213-
?: error("Unknown expr in stmt: $stmt")
213+
val imm = method.allocationMap[MethodAliasInfoImpl.Allocation.Expr(stmt)]
214+
?: error("Unknown constant")
214215
return assign(stmt.lhv.toPath(), imm)
215216
}
216-
217217
is EtsCallExpr -> {
218-
val callResult = method.allocationMap[MethodAliasInfo.Allocation.CallResult(stmt)]
219-
?: error("Unknown call in stmt: $stmt")
218+
val callResult = method.allocationMap[MethodAliasInfoImpl.Allocation.CallResult(stmt)]
219+
?: error("Unknown call")
220220
return assign(stmt.lhv.toPath(), callResult)
221221
}
222-
223222
is EtsNewExpr, is EtsNewArrayExpr -> {
224-
val new = method.allocationMap[MethodAliasInfo.Allocation.New(stmt)]
225-
?: error("Unknown new in stmt: $stmt")
223+
val new = method.allocationMap[MethodAliasInfoImpl.Allocation.New(stmt)]
224+
?: error("Unknown new")
226225
return assign(stmt.lhv.toPath(), new)
227226
}
228-
229227
else -> {
230-
logger.warn("Could not process rhs in stmt: $stmt")
231-
return null
228+
error("Unprocessable")
232229
}
233230
}
234231
}
@@ -267,7 +264,7 @@ class StmtAliasInfo(
267264
get() = edge?.first
268265

269266
fun traceContains(other: Int): Boolean =
270-
alloc == other || (parent?.traceContains(other) == true)
267+
alloc == other || (parent?.traceContains(other) ?: false)
271268
}
272269

273270
private fun accessors(node: PathNode): List<Accessor> {
@@ -285,7 +282,7 @@ class StmtAliasInfo(
285282
return accessors
286283
}
287284

288-
fun getAliases(path: AccessPath): Set<AccessPath> {
285+
override fun getAliases(path: AccessPath): Set<AccessPath> {
289286
val alloc = trace(path).first.last()
290287
if (alloc == NOT_PROCESSED || alloc == MULTIPLE_EDGE) {
291288
return setOf(path)
@@ -319,9 +316,9 @@ class StmtAliasInfo(
319316
}
320317
}
321318

322-
class MethodAliasInfo(
319+
class MethodAliasInfoImpl(
323320
val method: EtsMethod,
324-
) {
321+
) : MethodAliasInfo {
325322
sealed interface Allocation {
326323
data class New(val stmt: EtsStmt) : Allocation
327324
data class CallResult(val stmt: EtsStmt) : Allocation
@@ -341,7 +338,7 @@ class MethodAliasInfo(
341338
val baseMap = mutableMapOf<AccessPathBase, Int>()
342339
val bases = mutableListOf<AccessPathBase>()
343340

344-
fun newString(str: String) {
341+
private fun newString(str: String) {
345342
stringMap.computeIfAbsent(str) {
346343
strings.add(str)
347344
stringMap.size
@@ -365,22 +362,16 @@ class MethodAliasInfo(
365362
is AccessPathBase.Local -> {
366363
newString(base.name)
367364
}
368-
369365
is AccessPathBase.This -> {
370366
newAllocation(Allocation.This)
371367
}
372-
373368
is AccessPathBase.Arg -> {
374369
newAllocation(Allocation.Arg(base.index))
375370
}
376-
377371
is AccessPathBase.Static -> {
378372
newAllocation(Allocation.Static(base.clazz))
379373
}
380-
381-
is AccessPathBase.Const -> {
382-
// TODO ?? may be some non-trivial
383-
}
374+
is AccessPathBase.Const -> { /* TODO ?? may be some non-trivial */ }
384375
}
385376
}
386377

@@ -390,25 +381,20 @@ class MethodAliasInfo(
390381
initEntity(entity.instance)
391382
newString(entity.field.name)
392383
}
393-
394384
is EtsStaticFieldRef -> {
395385
newBase(AccessPathBase.Static(entity.field.enclosingClass))
396386
newString(entity.field.name)
397387
}
398-
399388
is EtsArrayAccess -> {
400389
initEntity(entity.array)
401390
initEntity(entity.index)
402391
}
403-
404392
is EtsLocal -> {
405393
newBase(AccessPathBase.Local(entity.name))
406394
}
407-
408395
is EtsParameterRef -> {
409396
newBase(AccessPathBase.Arg(entity.index))
410397
}
411-
412398
is EtsInstanceCallExpr -> {
413399
initEntity(entity.instance)
414400
newString(entity.method.name)
@@ -437,21 +423,16 @@ class MethodAliasInfo(
437423
is EtsNewExpr, is EtsNewArrayExpr -> {
438424
newAllocation(Allocation.New(stmt))
439425
}
440-
441426
is EtsParameterRef -> {
442427
newAllocation(Allocation.Arg(rhv.index))
443428
}
444-
445429
is EtsCallExpr -> {
446430
newAllocation(Allocation.CallResult(stmt))
447431
}
448-
449432
is EtsInstanceFieldRef, is EtsStaticFieldRef -> {
450433
newAllocation(Allocation.Imm(stmt))
451434
}
452-
453435
is EtsCastExpr -> {}
454-
455436
is EtsConstant, is EtsUnaryExpr, is EtsBinaryExpr, is EtsArrayAccess, is EtsInstanceOfExpr -> {
456437
newAllocation(Allocation.Expr(stmt))
457438
}
@@ -469,11 +450,11 @@ class MethodAliasInfo(
469450
initMaps()
470451
}
471452

472-
private val preAliases: MutableMap<EtsStmt, StmtAliasInfo> = hashMapOf()
453+
private val preAliases = mutableMapOf<EtsStmt, StmtAliasInfoImpl>()
473454

474455
@OptIn(ExperimentalUnsignedTypes::class)
475456
@Suppress("UNCHECKED_CAST")
476-
fun computeAliases(): List<StmtAliasInfo> {
457+
override fun computeAliases(): List<StmtAliasInfo> {
477458
val visited: MutableSet<EtsStmt> = hashSetOf()
478459
val order: MutableList<EtsStmt> = mutableListOf()
479460
val preds: MutableMap<EtsStmt, MutableList<EtsStmt>> = hashMapOf()
@@ -494,14 +475,14 @@ class MethodAliasInfo(
494475
postOrderDfs(root)
495476
order.reverse()
496477

497-
fun computePreAliases(stmt: EtsStmt): StmtAliasInfo {
478+
fun computePreAliases(stmt: EtsStmt): StmtAliasInfoImpl {
498479
if (stmt in preAliases) return preAliases.getValue(stmt)
499480

500481
val merged = preds[stmt]
501-
?.mapNotNull { preAliases.getValue(it).applyStmt(it) }
482+
?.map { preAliases.getValue(it).applyStmt(it) }
502483
?.reduceOrNull { a, b -> a.merge(b) }
503-
?: StmtAliasInfo(
504-
baseToAlloc = IntArray(bases.size) { StmtAliasInfo.NOT_PROCESSED },
484+
?: StmtAliasInfoImpl(
485+
baseToAlloc = IntArray(bases.size) { StmtAliasInfoImpl.NOT_PROCESSED },
505486
allocToFields = Array(allocations.size) { ulongArrayOf() },
506487
method = this
507488
)
@@ -510,12 +491,24 @@ class MethodAliasInfo(
510491
return merged
511492
}
512493

513-
val aliases = Array<StmtAliasInfo?>(method.cfg.stmts.size) { null }
494+
val aliases = Array<StmtAliasInfoImpl?>(method.cfg.stmts.size) { null }
514495
for (stmt in order) {
515496
aliases[stmt.location.index] = computePreAliases(stmt)
516497
}
517498

518499
assert(!aliases.contains(null))
519-
return (aliases as Array<StmtAliasInfo>).toList()
500+
return (aliases as Array<StmtAliasInfoImpl>).toList()
501+
}
502+
}
503+
504+
object NoStmtAliasInfo : StmtAliasInfo {
505+
override fun getAliases(path: AccessPath): Set<AccessPath> {
506+
return setOf(path)
507+
}
508+
}
509+
510+
class NoMethodAliasInfo(val method: EtsMethod) : MethodAliasInfo {
511+
override fun computeAliases(): List<StmtAliasInfo> {
512+
return method.cfg.stmts.map { NoStmtAliasInfo }
520513
}
521514
}

0 commit comments

Comments
 (0)