1
1
package org.usvm.dataflow.ts.infer
2
2
3
- import mu.KotlinLogging
4
3
import org.jacodb.ets.base.EtsArrayAccess
5
4
import org.jacodb.ets.base.EtsAssignStmt
6
5
import org.jacodb.ets.base.EtsBinaryExpr
@@ -22,22 +21,29 @@ import org.jacodb.ets.base.EtsThis
22
21
import org.jacodb.ets.base.EtsUnaryExpr
23
22
import org.jacodb.ets.model.EtsClassSignature
24
23
import org.jacodb.ets.model.EtsMethod
24
+ import kotlin.collections.ArrayDeque
25
25
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
+ }
27
33
28
34
@OptIn(ExperimentalUnsignedTypes ::class )
29
- class StmtAliasInfo (
35
+ class StmtAliasInfoImpl (
30
36
val baseToAlloc : IntArray ,
31
37
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
37
43
38
- const val ELEMENT_ACCESSOR = - 3
44
+ internal const val ELEMENT_ACCESSOR = - 3
39
45
40
- fun merge (first : Int , second : Int ): Int = when {
46
+ internal fun merge (first : Int , second : Int ): Int = when {
41
47
first == NOT_PROCESSED -> second
42
48
second == NOT_PROCESSED -> first
43
49
first == MULTIPLE_EDGE -> MULTIPLE_EDGE
@@ -46,19 +52,19 @@ class StmtAliasInfo(
46
52
else -> MULTIPLE_EDGE
47
53
}
48
54
49
- fun wrap (string : Int , alloc : Int ): ULong {
55
+ internal fun wrap (string : Int , alloc : Int ): ULong {
50
56
return (string.toULong() shl Int .SIZE_BITS ) or alloc.toULong()
51
57
}
52
58
53
- fun unwrap (edge : ULong ): Pair <Int , Int > {
59
+ internal fun unwrap (edge : ULong ): Pair <Int , Int > {
54
60
val string = (edge shr Int .SIZE_BITS ).toInt()
55
61
val allocation = (edge and UInt .MAX_VALUE .toULong()).toInt()
56
62
return Pair (string, allocation)
57
63
}
58
64
}
59
65
60
- fun merge (other : StmtAliasInfo ): StmtAliasInfo {
61
- val merged = StmtAliasInfo (
66
+ internal fun merge (other : StmtAliasInfoImpl ): StmtAliasInfoImpl {
67
+ val merged = StmtAliasInfoImpl (
62
68
baseToAlloc = IntArray (method.bases.size) { NOT_PROCESSED },
63
69
allocToFields = Array (method.allocations.size) { ulongArrayOf() },
64
70
method = method
@@ -71,11 +77,11 @@ class StmtAliasInfo(
71
77
val toFieldsMap = mutableMapOf<Int , Int >()
72
78
allocToFields[i].forEach {
73
79
val (s, a) = unwrap(it)
74
- toFieldsMap.merge(s, a, ::merge)
80
+ toFieldsMap.merge(s, a, Companion ::merge)
75
81
}
76
82
other.allocToFields[i].forEach {
77
83
val (s, a) = unwrap(it)
78
- toFieldsMap.merge(s, a, ::merge)
84
+ toFieldsMap.merge(s, a, Companion ::merge)
79
85
}
80
86
merged.allocToFields[i] = toFieldsMap
81
87
.map { (string, alloc) -> wrap(string, alloc) }
@@ -102,10 +108,9 @@ class StmtAliasInfo(
102
108
nodes.add(MULTIPLE_EDGE )
103
109
strings.add(ELEMENT_ACCESSOR )
104
110
}
105
-
106
111
is FieldAccessor -> {
107
112
val string = method.stringMap[accessor.name]
108
- ? : error(" Unknown field name: ${accessor.name} " )
113
+ ? : error(" Unknown field name" )
109
114
strings.add(string)
110
115
111
116
node = allocToFields[node]
@@ -122,17 +127,17 @@ class StmtAliasInfo(
122
127
return Pair (nodes, strings)
123
128
}
124
129
125
- fun assign (lhv : AccessPath , rhv : AccessPath ): StmtAliasInfo {
130
+ private fun assign (lhv : AccessPath , rhv : AccessPath ): StmtAliasInfoImpl {
126
131
val (rhvNodes, _) = trace(rhv)
127
132
val newAlloc = rhvNodes.last()
128
133
return assign(lhv, newAlloc)
129
134
}
130
135
131
- fun assign (lhv : AccessPath , newAlloc : Int ): StmtAliasInfo {
136
+ private fun assign (lhv : AccessPath , newAlloc : Int ): StmtAliasInfoImpl {
132
137
val (lhvNodes, lhvEdges) = trace(lhv)
133
138
val from = lhvNodes.reversed().getOrNull(1 )
134
139
if (from != null ) {
135
- val updated = StmtAliasInfo (
140
+ val updated = StmtAliasInfoImpl (
136
141
baseToAlloc = baseToAlloc,
137
142
allocToFields = allocToFields.copyOf(),
138
143
method = method,
@@ -155,7 +160,7 @@ class StmtAliasInfo(
155
160
156
161
return updated
157
162
} else {
158
- val updated = StmtAliasInfo (
163
+ val updated = StmtAliasInfoImpl (
159
164
baseToAlloc = baseToAlloc.copyOf(),
160
165
allocToFields = allocToFields,
161
166
method = method,
@@ -169,66 +174,58 @@ class StmtAliasInfo(
169
174
}
170
175
}
171
176
172
- fun applyStmt (stmt : EtsStmt ): StmtAliasInfo ? {
177
+ internal fun applyStmt (stmt : EtsStmt ): StmtAliasInfoImpl {
173
178
if (stmt !is EtsAssignStmt ) {
174
179
return this
175
180
}
176
181
when (val rhv = stmt.rhv) {
177
182
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" )
180
185
return assign(stmt.lhv.toPath(), alloc)
181
186
}
182
-
183
187
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" )
186
190
return assign(stmt.lhv.toPath(), alloc)
187
191
}
188
-
189
192
is EtsInstanceFieldRef , is EtsStaticFieldRef -> {
190
193
val (rhvNodes, _) = trace(rhv.toPath())
191
194
val alloc = rhvNodes.last()
192
195
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
+
195
199
return this
196
200
.assign(rhv.toPath(), fieldAlloc)
197
201
.assign(stmt.lhv.toPath(), fieldAlloc)
198
202
} else {
199
203
return assign(stmt.lhv.toPath(), alloc)
200
204
}
201
205
}
202
-
203
206
is EtsLocal -> {
204
207
return assign(stmt.lhv.toPath(), rhv.toPath())
205
208
}
206
-
207
209
is EtsCastExpr -> {
208
210
return assign(stmt.lhv.toPath(), rhv.arg.toPath())
209
211
}
210
-
211
212
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 " )
214
215
return assign(stmt.lhv.toPath(), imm)
215
216
}
216
-
217
217
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" )
220
220
return assign(stmt.lhv.toPath(), callResult)
221
221
}
222
-
223
222
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" )
226
225
return assign(stmt.lhv.toPath(), new)
227
226
}
228
-
229
227
else -> {
230
- logger.warn(" Could not process rhs in stmt: $stmt " )
231
- return null
228
+ error(" Unprocessable" )
232
229
}
233
230
}
234
231
}
@@ -267,7 +264,7 @@ class StmtAliasInfo(
267
264
get() = edge?.first
268
265
269
266
fun traceContains (other : Int ): Boolean =
270
- alloc == other || (parent?.traceContains(other) == true )
267
+ alloc == other || (parent?.traceContains(other) ? : false )
271
268
}
272
269
273
270
private fun accessors (node : PathNode ): List <Accessor > {
@@ -285,7 +282,7 @@ class StmtAliasInfo(
285
282
return accessors
286
283
}
287
284
288
- fun getAliases (path : AccessPath ): Set <AccessPath > {
285
+ override fun getAliases (path : AccessPath ): Set <AccessPath > {
289
286
val alloc = trace(path).first.last()
290
287
if (alloc == NOT_PROCESSED || alloc == MULTIPLE_EDGE ) {
291
288
return setOf (path)
@@ -319,9 +316,9 @@ class StmtAliasInfo(
319
316
}
320
317
}
321
318
322
- class MethodAliasInfo (
319
+ class MethodAliasInfoImpl (
323
320
val method : EtsMethod ,
324
- ) {
321
+ ) : MethodAliasInfo {
325
322
sealed interface Allocation {
326
323
data class New (val stmt : EtsStmt ) : Allocation
327
324
data class CallResult (val stmt : EtsStmt ) : Allocation
@@ -341,7 +338,7 @@ class MethodAliasInfo(
341
338
val baseMap = mutableMapOf<AccessPathBase , Int >()
342
339
val bases = mutableListOf<AccessPathBase >()
343
340
344
- fun newString (str : String ) {
341
+ private fun newString (str : String ) {
345
342
stringMap.computeIfAbsent(str) {
346
343
strings.add(str)
347
344
stringMap.size
@@ -365,22 +362,16 @@ class MethodAliasInfo(
365
362
is AccessPathBase .Local -> {
366
363
newString(base.name)
367
364
}
368
-
369
365
is AccessPathBase .This -> {
370
366
newAllocation(Allocation .This )
371
367
}
372
-
373
368
is AccessPathBase .Arg -> {
374
369
newAllocation(Allocation .Arg (base.index))
375
370
}
376
-
377
371
is AccessPathBase .Static -> {
378
372
newAllocation(Allocation .Static (base.clazz))
379
373
}
380
-
381
- is AccessPathBase .Const -> {
382
- // TODO ?? may be some non-trivial
383
- }
374
+ is AccessPathBase .Const -> { /* TODO ?? may be some non-trivial */ }
384
375
}
385
376
}
386
377
@@ -390,25 +381,20 @@ class MethodAliasInfo(
390
381
initEntity(entity.instance)
391
382
newString(entity.field.name)
392
383
}
393
-
394
384
is EtsStaticFieldRef -> {
395
385
newBase(AccessPathBase .Static (entity.field.enclosingClass))
396
386
newString(entity.field.name)
397
387
}
398
-
399
388
is EtsArrayAccess -> {
400
389
initEntity(entity.array)
401
390
initEntity(entity.index)
402
391
}
403
-
404
392
is EtsLocal -> {
405
393
newBase(AccessPathBase .Local (entity.name))
406
394
}
407
-
408
395
is EtsParameterRef -> {
409
396
newBase(AccessPathBase .Arg (entity.index))
410
397
}
411
-
412
398
is EtsInstanceCallExpr -> {
413
399
initEntity(entity.instance)
414
400
newString(entity.method.name)
@@ -437,21 +423,16 @@ class MethodAliasInfo(
437
423
is EtsNewExpr , is EtsNewArrayExpr -> {
438
424
newAllocation(Allocation .New (stmt))
439
425
}
440
-
441
426
is EtsParameterRef -> {
442
427
newAllocation(Allocation .Arg (rhv.index))
443
428
}
444
-
445
429
is EtsCallExpr -> {
446
430
newAllocation(Allocation .CallResult (stmt))
447
431
}
448
-
449
432
is EtsInstanceFieldRef , is EtsStaticFieldRef -> {
450
433
newAllocation(Allocation .Imm (stmt))
451
434
}
452
-
453
435
is EtsCastExpr -> {}
454
-
455
436
is EtsConstant , is EtsUnaryExpr , is EtsBinaryExpr , is EtsArrayAccess , is EtsInstanceOfExpr -> {
456
437
newAllocation(Allocation .Expr (stmt))
457
438
}
@@ -469,11 +450,11 @@ class MethodAliasInfo(
469
450
initMaps()
470
451
}
471
452
472
- private val preAliases: MutableMap <EtsStmt , StmtAliasInfo > = hashMapOf ()
453
+ private val preAliases = mutableMapOf <EtsStmt , StmtAliasInfoImpl > ()
473
454
474
455
@OptIn(ExperimentalUnsignedTypes ::class )
475
456
@Suppress(" UNCHECKED_CAST" )
476
- fun computeAliases (): List <StmtAliasInfo > {
457
+ override fun computeAliases (): List <StmtAliasInfo > {
477
458
val visited: MutableSet <EtsStmt > = hashSetOf()
478
459
val order: MutableList <EtsStmt > = mutableListOf ()
479
460
val preds: MutableMap <EtsStmt , MutableList <EtsStmt >> = hashMapOf()
@@ -494,14 +475,14 @@ class MethodAliasInfo(
494
475
postOrderDfs(root)
495
476
order.reverse()
496
477
497
- fun computePreAliases (stmt : EtsStmt ): StmtAliasInfo {
478
+ fun computePreAliases (stmt : EtsStmt ): StmtAliasInfoImpl {
498
479
if (stmt in preAliases) return preAliases.getValue(stmt)
499
480
500
481
val merged = preds[stmt]
501
- ?.mapNotNull { preAliases.getValue(it).applyStmt(it) }
482
+ ?.map { preAliases.getValue(it).applyStmt(it) }
502
483
?.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 },
505
486
allocToFields = Array (allocations.size) { ulongArrayOf() },
506
487
method = this
507
488
)
@@ -510,12 +491,24 @@ class MethodAliasInfo(
510
491
return merged
511
492
}
512
493
513
- val aliases = Array <StmtAliasInfo ?>(method.cfg.stmts.size) { null }
494
+ val aliases = Array <StmtAliasInfoImpl ?>(method.cfg.stmts.size) { null }
514
495
for (stmt in order) {
515
496
aliases[stmt.location.index] = computePreAliases(stmt)
516
497
}
517
498
518
499
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 }
520
513
}
521
514
}
0 commit comments