diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Collector.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Collector.kt
new file mode 100644
index 000000000..025651289
--- /dev/null
+++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Collector.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 UnitTestBot contributors (utbot.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("PropertyName")
+
+package org.jacodb.ets.utils
+
+import org.jacodb.ets.base.EtsEntity
+import org.jacodb.ets.base.EtsStmt
+
+class EntityCollector>(
+ val result: C,
+ val block: (EtsEntity) -> R?,
+) : AbstractHandler() {
+ override fun handle(value: EtsEntity) {
+ val item = block(value)
+ if (item != null) {
+ result += item
+ }
+ }
+
+ override fun handle(stmt: EtsStmt) {
+ // Do nothing.
+ }
+}
+
+fun > EtsEntity.collectEntitiesTo(
+ destination: C,
+ block: (EtsEntity) -> R?,
+): C {
+ accept(EntityCollector(destination, block))
+ return destination
+}
+
+fun > EtsStmt.collectEntitiesTo(
+ destination: C,
+ block: (EtsEntity) -> R?,
+): C {
+ accept(EntityCollector(destination, block))
+ return destination
+}
diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetUses.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetLocals.kt
similarity index 50%
rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetUses.kt
rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetLocals.kt
index 5c73c3bbc..584c8d3f5 100644
--- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetUses.kt
+++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetLocals.kt
@@ -16,11 +16,31 @@
package org.jacodb.ets.utils
+import org.jacodb.ets.base.EtsAssignStmt
import org.jacodb.ets.base.EtsEntity
+import org.jacodb.ets.base.EtsLocal
import org.jacodb.ets.base.EtsStmt
+import org.jacodb.ets.model.EtsMethod
-fun EtsStmt.getUses(): Sequence =
- getOperands().flatMap { sequenceOf(it) + it.getUses() }
+fun EtsMethod.getDeclaredLocals(): Set =
+ cfg.stmts.mapNotNullTo(mutableSetOf()) {
+ if (it is EtsAssignStmt && it.lhv is EtsLocal) {
+ it.lhv
+ } else {
+ null
+ }
+ }
-fun EtsEntity.getUses(): Sequence =
- getOperands().flatMap { sequenceOf(it) + it.getUses() }
+fun EtsMethod.getLocals(): Set {
+ val result = mutableSetOf()
+ cfg.stmts.forEach { it.collectEntitiesTo(result) { it as? EtsLocal } }
+ return result
+}
+
+fun EtsStmt.getLocals(): Set {
+ return collectEntitiesTo(mutableSetOf()) { it as? EtsLocal }
+}
+
+fun EtsEntity.getLocals(): Set {
+ return collectEntitiesTo(mutableSetOf()) { it as? EtsLocal }
+}
diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetValues.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetValues.kt
index 503323b4d..646e712a3 100644
--- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetValues.kt
+++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetValues.kt
@@ -20,8 +20,10 @@ import org.jacodb.ets.base.EtsEntity
import org.jacodb.ets.base.EtsStmt
import org.jacodb.ets.base.EtsValue
-fun EtsStmt.getValues(): Sequence =
- getOperands().filterIsInstance()
+fun EtsStmt.getValues(): Set {
+ return collectEntitiesTo(mutableSetOf()) { it as? EtsValue }
+}
-fun EtsEntity.getValues(): Sequence =
- getOperands().filterIsInstance()
+fun EtsEntity.getValues(): Set {
+ return collectEntitiesTo(mutableSetOf()) { it as? EtsValue }
+}
diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt
new file mode 100644
index 000000000..a84d42b05
--- /dev/null
+++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2022 UnitTestBot contributors (utbot.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jacodb.ets.utils
+
+import org.jacodb.ets.base.EtsAddExpr
+import org.jacodb.ets.base.EtsAndExpr
+import org.jacodb.ets.base.EtsArrayAccess
+import org.jacodb.ets.base.EtsAssignStmt
+import org.jacodb.ets.base.EtsAwaitExpr
+import org.jacodb.ets.base.EtsBitAndExpr
+import org.jacodb.ets.base.EtsBitNotExpr
+import org.jacodb.ets.base.EtsBitOrExpr
+import org.jacodb.ets.base.EtsBitXorExpr
+import org.jacodb.ets.base.EtsBooleanConstant
+import org.jacodb.ets.base.EtsCallStmt
+import org.jacodb.ets.base.EtsCastExpr
+import org.jacodb.ets.base.EtsCommaExpr
+import org.jacodb.ets.base.EtsDeleteExpr
+import org.jacodb.ets.base.EtsDivExpr
+import org.jacodb.ets.base.EtsEntity
+import org.jacodb.ets.base.EtsEqExpr
+import org.jacodb.ets.base.EtsExpExpr
+import org.jacodb.ets.base.EtsGotoStmt
+import org.jacodb.ets.base.EtsGtEqExpr
+import org.jacodb.ets.base.EtsGtExpr
+import org.jacodb.ets.base.EtsIfStmt
+import org.jacodb.ets.base.EtsInExpr
+import org.jacodb.ets.base.EtsInstanceCallExpr
+import org.jacodb.ets.base.EtsInstanceFieldRef
+import org.jacodb.ets.base.EtsInstanceOfExpr
+import org.jacodb.ets.base.EtsLeftShiftExpr
+import org.jacodb.ets.base.EtsLengthExpr
+import org.jacodb.ets.base.EtsLocal
+import org.jacodb.ets.base.EtsLtEqExpr
+import org.jacodb.ets.base.EtsLtExpr
+import org.jacodb.ets.base.EtsMulExpr
+import org.jacodb.ets.base.EtsNegExpr
+import org.jacodb.ets.base.EtsNewArrayExpr
+import org.jacodb.ets.base.EtsNewExpr
+import org.jacodb.ets.base.EtsNopStmt
+import org.jacodb.ets.base.EtsNotEqExpr
+import org.jacodb.ets.base.EtsNotExpr
+import org.jacodb.ets.base.EtsNullConstant
+import org.jacodb.ets.base.EtsNullishCoalescingExpr
+import org.jacodb.ets.base.EtsNumberConstant
+import org.jacodb.ets.base.EtsOrExpr
+import org.jacodb.ets.base.EtsParameterRef
+import org.jacodb.ets.base.EtsPostDecExpr
+import org.jacodb.ets.base.EtsPostIncExpr
+import org.jacodb.ets.base.EtsPreDecExpr
+import org.jacodb.ets.base.EtsPreIncExpr
+import org.jacodb.ets.base.EtsPtrCallExpr
+import org.jacodb.ets.base.EtsRemExpr
+import org.jacodb.ets.base.EtsReturnStmt
+import org.jacodb.ets.base.EtsRightShiftExpr
+import org.jacodb.ets.base.EtsStaticCallExpr
+import org.jacodb.ets.base.EtsStaticFieldRef
+import org.jacodb.ets.base.EtsStmt
+import org.jacodb.ets.base.EtsStrictEqExpr
+import org.jacodb.ets.base.EtsStrictNotEqExpr
+import org.jacodb.ets.base.EtsStringConstant
+import org.jacodb.ets.base.EtsSubExpr
+import org.jacodb.ets.base.EtsSwitchStmt
+import org.jacodb.ets.base.EtsTernaryExpr
+import org.jacodb.ets.base.EtsThis
+import org.jacodb.ets.base.EtsThrowStmt
+import org.jacodb.ets.base.EtsTypeOfExpr
+import org.jacodb.ets.base.EtsUnaryPlusExpr
+import org.jacodb.ets.base.EtsUndefinedConstant
+import org.jacodb.ets.base.EtsUnsignedRightShiftExpr
+import org.jacodb.ets.base.EtsVoidExpr
+import org.jacodb.ets.base.EtsYieldExpr
+
+abstract class AbstractHandler : EtsEntity.Visitor.Default, EtsStmt.Visitor.Default {
+
+ abstract fun handle(value: EtsEntity)
+ abstract fun handle(stmt: EtsStmt)
+
+ final override fun defaultVisit(value: EtsEntity) {
+ handle(value)
+ }
+
+ final override fun defaultVisit(stmt: EtsStmt) {
+ handle(stmt)
+ }
+
+ final override fun visit(stmt: EtsNopStmt) {
+ handle(stmt)
+ }
+
+ final override fun visit(stmt: EtsAssignStmt) {
+ handle(stmt)
+ stmt.lhv.accept(this)
+ stmt.rhv.accept(this)
+ }
+
+ final override fun visit(stmt: EtsCallStmt) {
+ handle(stmt)
+ stmt.expr.accept(this)
+ }
+
+ final override fun visit(stmt: EtsReturnStmt) {
+ handle(stmt)
+ stmt.returnValue?.accept(this)
+ }
+
+ final override fun visit(stmt: EtsThrowStmt) {
+ handle(stmt)
+ stmt.arg.accept(this)
+ }
+
+ final override fun visit(stmt: EtsGotoStmt) {
+ handle(stmt)
+ }
+
+ final override fun visit(stmt: EtsIfStmt) {
+ handle(stmt)
+ stmt.condition.accept(this)
+ }
+
+ final override fun visit(stmt: EtsSwitchStmt) {
+ error("deprecated")
+ }
+
+ final override fun visit(value: EtsLocal) {
+ handle(value)
+ }
+
+ final override fun visit(value: EtsStringConstant) {
+ handle(value)
+ }
+
+ final override fun visit(value: EtsBooleanConstant) {
+ handle(value)
+ }
+
+ final override fun visit(value: EtsNumberConstant) {
+ handle(value)
+ }
+
+ final override fun visit(value: EtsNullConstant) {
+ handle(value)
+ }
+
+ final override fun visit(value: EtsUndefinedConstant) {
+ handle(value)
+ }
+
+ final override fun visit(value: EtsThis) {
+ handle(value)
+ }
+
+ final override fun visit(value: EtsParameterRef) {
+ handle(value)
+ }
+
+ final override fun visit(value: EtsArrayAccess) {
+ handle(value)
+ value.array.accept(this)
+ value.index.accept(this)
+ }
+
+ final override fun visit(value: EtsInstanceFieldRef) {
+ handle(value)
+ value.instance.accept(this)
+ }
+
+ final override fun visit(value: EtsStaticFieldRef) {
+ handle(value)
+ }
+
+ final override fun visit(expr: EtsNewExpr) {
+ handle(expr)
+ }
+
+ final override fun visit(expr: EtsNewArrayExpr) {
+ handle(expr)
+ expr.size.accept(this)
+ }
+
+ final override fun visit(expr: EtsLengthExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsCastExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsInstanceOfExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsDeleteExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsAwaitExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsYieldExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsTypeOfExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsVoidExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsNotExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsBitNotExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsNegExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsUnaryPlusExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsPreIncExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsPreDecExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsPostIncExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsPostDecExpr) {
+ handle(expr)
+ expr.arg.accept(this)
+ }
+
+ final override fun visit(expr: EtsEqExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsNotEqExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsStrictEqExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsStrictNotEqExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsLtExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsLtEqExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsGtExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsGtEqExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsInExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsAddExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsSubExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsMulExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsDivExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsRemExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsExpExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsBitAndExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsBitOrExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsBitXorExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsLeftShiftExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsRightShiftExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsUnsignedRightShiftExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsAndExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsOrExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsNullishCoalescingExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsInstanceCallExpr) {
+ handle(expr)
+ expr.instance.accept(this)
+ expr.args.forEach { it.accept(this) }
+ }
+
+ final override fun visit(expr: EtsStaticCallExpr) {
+ handle(expr)
+ expr.args.forEach { it.accept(this) }
+ }
+
+ final override fun visit(expr: EtsPtrCallExpr) {
+ handle(expr)
+ expr.ptr.accept(this)
+ expr.args.forEach { it.accept(this) }
+ }
+
+ final override fun visit(expr: EtsCommaExpr) {
+ handle(expr)
+ expr.left.accept(this)
+ expr.right.accept(this)
+ }
+
+ final override fun visit(expr: EtsTernaryExpr) {
+ handle(expr)
+ expr.condition.accept(this)
+ expr.thenExpr.accept(this)
+ expr.elseExpr.accept(this)
+ }
+}
diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/CollectorTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/CollectorTest.kt
new file mode 100644
index 000000000..8ef83f512
--- /dev/null
+++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/CollectorTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2022 UnitTestBot contributors (utbot.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jacodb.ets.test
+
+import org.jacodb.ets.base.EtsAddExpr
+import org.jacodb.ets.base.EtsAssignStmt
+import org.jacodb.ets.base.EtsEntity
+import org.jacodb.ets.base.EtsInstLocation
+import org.jacodb.ets.base.EtsLocal
+import org.jacodb.ets.base.EtsNumberConstant
+import org.jacodb.ets.base.EtsStmt
+import org.jacodb.ets.base.EtsType
+import org.jacodb.ets.base.EtsUnknownType
+import org.jacodb.ets.graph.EtsCfg
+import org.jacodb.ets.model.EtsDecorator
+import org.jacodb.ets.model.EtsMethod
+import org.jacodb.ets.model.EtsMethodSignature
+import org.jacodb.ets.model.EtsModifiers
+import org.jacodb.ets.utils.AbstractHandler
+import org.jacodb.ets.utils.EntityCollector
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class CollectorTest {
+
+ private fun createStmt(): EtsStmt {
+ val method = object : EtsMethod {
+ override val signature: EtsMethodSignature
+ get() = TODO("Not yet implemented")
+ override val typeParameters: List
+ get() = TODO("Not yet implemented")
+ override val locals: List
+ get() = TODO("Not yet implemented")
+ override val cfg: EtsCfg
+ get() = TODO("Not yet implemented")
+ override val modifiers: EtsModifiers
+ get() = TODO("Not yet implemented")
+ override val decorators: List
+ get() = TODO("Not yet implemented")
+ }
+
+ val loc = EtsInstLocation(method, -1)
+ val a = EtsLocal("a", EtsUnknownType)
+ val b = EtsLocal("b", EtsUnknownType)
+ val n = EtsNumberConstant(42.0)
+ val rhv = EtsAddExpr(EtsUnknownType, b, n)
+ val stmt = EtsAssignStmt(loc, a, rhv)
+
+ return stmt
+ }
+
+ @Test
+ fun `test AbstractHandler`() {
+ val stmt = createStmt()
+ val result = mutableSetOf()
+ val c = object : AbstractHandler() {
+ override fun handle(value: EtsEntity) {
+ result += value.toString()
+ }
+
+ override fun handle(stmt: EtsStmt) {
+ result += stmt.toString()
+ }
+ }
+ stmt.accept(c)
+ println(result)
+ assertEquals("[a := b + 42.0, a, b + 42.0, b, 42.0]", result.toString())
+ }
+
+ @Test
+ fun `test EntityCollector`() {
+ val stmt = createStmt()
+ val c = EntityCollector(mutableSetOf()) {
+ it.toString()
+ }
+ stmt.accept(c)
+ println(c.result)
+ assertEquals("[a, b + 42.0, b, 42.0]", c.result.toString())
+ }
+}