From 8691e423c42da6943ae247739d8d128aaef1897e Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Wed, 5 Feb 2025 17:09:29 +0300 Subject: [PATCH] Add Handler and Collector --- .../kotlin/org/jacodb/ets/utils/Collector.kt | 54 +++ .../ets/utils/{GetUses.kt => GetLocals.kt} | 28 +- .../kotlin/org/jacodb/ets/utils/GetValues.kt | 10 +- .../kotlin/org/jacodb/ets/utils/Handler.kt | 448 ++++++++++++++++++ .../org/jacodb/ets/test/CollectorTest.kt | 94 ++++ 5 files changed, 626 insertions(+), 8 deletions(-) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Collector.kt rename jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/{GetUses.kt => GetLocals.kt} (50%) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt create mode 100644 jacodb-ets/src/test/kotlin/org/jacodb/ets/test/CollectorTest.kt 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()) + } +}