From f3efaff4c6e5f4bd4c976d76d004acc3bf4dccea Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Wed, 16 Apr 2025 14:52:23 +0300 Subject: [PATCH 1/8] Improve ETS model --- .../org/jacodb/api/common/cfg/CommonInst.kt | 4 - .../org/jacodb/api/common/cfg/CommonValue.kt | 3 +- .../org/jacodb/impl/cfg/JcBlockGraphImpl.kt | 4 +- .../jacodb/impl/cfg/graphs/GraphDominators.kt | 19 +- jacodb-ets/build.gradle.kts | 2 +- .../kotlin/org/jacodb/ets/base/Position.kt | 83 ---- .../org/jacodb/ets/base/StmtPositionInfo.kt | 64 --- .../kotlin/org/jacodb/ets/dsl/BlockCfg.kt | 2 +- .../main/kotlin/org/jacodb/ets/dto/Convert.kt | 438 +++++++----------- .../src/main/kotlin/org/jacodb/ets/dto/Ops.kt | 2 +- .../kotlin/org/jacodb/ets/dto/Signatures.kt | 1 + .../main/kotlin/org/jacodb/ets/dto/Stmts.kt | 25 +- .../main/kotlin/org/jacodb/ets/dto/Types.kt | 50 +- .../main/kotlin/org/jacodb/ets/dto/Values.kt | 9 - .../kotlin/org/jacodb/ets/graph/EtsCfg.kt | 72 --- .../kotlin/org/jacodb/ets/graph/EtsLoop.kt | 115 ----- .../main/kotlin/org/jacodb/ets/model/Base.kt | 31 ++ .../kotlin/org/jacodb/ets/model/CfgBlock.kt | 95 ++++ .../kotlin/org/jacodb/ets/model/CfgLinear.kt | 78 ++++ .../ets/model/{EtsClass.kt => Class.kt} | 21 +- .../{EtsClassCategory.kt => ClassCategory.kt} | 0 .../EtsConstant.kt => model/Constant.kt} | 17 +- .../model/{EtsDecorator.kt => Decorators.kt} | 0 .../{base/EtsEntity.kt => model/Entity.kt} | 11 +- .../org/jacodb/ets/model/EtsBaseModel.kt | 41 -- .../ets/{base/EtsExpr.kt => model/Expr.kt} | 154 +----- .../ets/model/{EtsField.kt => Field.kt} | 15 +- .../jacodb/ets/model/{EtsFile.kt => File.kt} | 7 + .../EtsBytecodeGraph.kt => model/Graph.kt} | 2 +- .../EtsImmediate.kt => model/Immediate.kt} | 2 +- .../{base/EtsLValue.kt => model/LValue.kt} | 2 +- .../ets/{base/EtsLocal.kt => model/Local.kt} | 4 +- .../ets/model/{EtsMethod.kt => Method.kt} | 28 +- .../model/{EtsModifier.kt => Modifiers.kt} | 18 +- .../model/{EtsNamespace.kt => Namespace.kt} | 8 + .../ets/{base/EtsRef.kt => model/Ref.kt} | 75 +-- .../ets/model/{EtsScene.kt => Scene.kt} | 0 .../model/{EtsSignature.kt => Signatures.kt} | 53 +-- .../ets/{base/EtsStmt.kt => model/Stmt.kt} | 79 +--- .../org/jacodb/ets/model/StmtLocation.kt | 49 ++ .../ets/{base/EtsType.kt => model/Type.kt} | 154 +++--- .../kotlin/org/jacodb/ets/model/TypeName.kt | 27 ++ .../ets/{base/EtsValue.kt => model/Value.kt} | 2 +- .../BlockCfgBuilder.kt} | 220 ++------- .../org/jacodb/ets/utils/BlockCfgToDot.kt | 67 +++ .../kotlin/org/jacodb/ets/utils/CallExpr.kt | 4 +- .../kotlin/org/jacodb/ets/utils/Collector.kt | 4 +- .../jacodb/ets/{base => utils}/Constants.kt | 2 +- .../org/jacodb/ets/utils/Constructor.kt | 36 ++ .../org/jacodb/ets/utils/EtsFileDtoToDot.kt | 10 - .../utils/{Utils.kt => EtsFileDtoToText.kt} | 43 -- .../org/jacodb/ets/utils/EtsFileToDot.kt | 22 +- .../org/jacodb/ets/utils/EtsFileToText.kt | 49 ++ .../kotlin/org/jacodb/ets/utils/GetLocals.kt | 8 +- .../org/jacodb/ets/utils/GetOperands.kt | 152 +++--- .../kotlin/org/jacodb/ets/utils/GetValues.kt | 6 +- .../kotlin/org/jacodb/ets/utils/Handler.kt | 177 ++++--- .../org/jacodb/ets/utils/IdentityHashSet.kt | 4 +- .../utils/{EtsCfgExt.kt => LinearCfgExt.kt} | 10 +- .../{EtsCfgToDot.kt => LinearCfgToDot.kt} | 25 +- .../kotlin/org/jacodb/ets/utils/Linearize.kt | 80 ++++ .../kotlin/org/jacodb/ets/utils/StmtToDot.kt | 38 ++ .../kotlin/org/jacodb/ets/utils/ViewDot.kt | 4 + .../org/jacodb/ets/test/CollectorTest.kt | 46 +- .../org/jacodb/ets/test/EtsCfgDslTest.kt | 25 +- .../kotlin/org/jacodb/ets/test/EtsFileTest.kt | 64 +-- .../org/jacodb/ets/test/EtsFromJsonTest.kt | 21 +- jacodb-ets/src/test/resources/.gitignore | 3 +- 68 files changed, 1293 insertions(+), 1693 deletions(-) delete mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/base/Position.kt delete mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/base/StmtPositionInfo.kt delete mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsCfg.kt delete mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsLoop.kt create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Base.kt create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgBlock.kt create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgLinear.kt rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsClass.kt => Class.kt} (74%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsClassCategory.kt => ClassCategory.kt} (100%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsConstant.kt => model/Constant.kt} (85%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsDecorator.kt => Decorators.kt} (100%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsEntity.kt => model/Entity.kt} (91%) delete mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsBaseModel.kt rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsExpr.kt => model/Expr.kt} (83%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsField.kt => Field.kt} (75%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsFile.kt => File.kt} (86%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{graph/EtsBytecodeGraph.kt => model/Graph.kt} (96%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsImmediate.kt => model/Immediate.kt} (95%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsLValue.kt => model/LValue.kt} (95%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsLocal.kt => model/Local.kt} (92%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsMethod.kt => Method.kt} (70%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsModifier.kt => Modifiers.kt} (90%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsNamespace.kt => Namespace.kt} (78%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsRef.kt => model/Ref.kt} (55%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsScene.kt => Scene.kt} (100%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/model/{EtsSignature.kt => Signatures.kt} (72%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsStmt.kt => model/Stmt.kt} (71%) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/model/StmtLocation.kt rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsType.kt => model/Type.kt} (85%) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/model/TypeName.kt rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base/EtsValue.kt => model/Value.kt} (98%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{graph/EtsBlockCfg.kt => utils/BlockCfgBuilder.kt} (51%) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgToDot.kt rename jacodb-ets/src/main/kotlin/org/jacodb/ets/{base => utils}/Constants.kt (97%) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Constructor.kt rename jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/{Utils.kt => EtsFileDtoToText.kt} (55%) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToText.kt rename jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/{EtsCfgExt.kt => LinearCfgExt.kt} (95%) rename jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/{EtsCfgToDot.kt => LinearCfgToDot.kt} (71%) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Linearize.kt create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/StmtToDot.kt diff --git a/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/cfg/CommonInst.kt b/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/cfg/CommonInst.kt index 05dd7f7ec..5237519f1 100644 --- a/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/cfg/CommonInst.kt +++ b/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/cfg/CommonInst.kt @@ -21,15 +21,12 @@ import org.jacodb.api.common.CommonMethod interface CommonInst { val location: CommonInstLocation - // TODO: replace with extension property val method: CommonMethod get() = location.method } interface CommonInstLocation { val method: CommonMethod - // val index: Int - // val lineNumber: Int } interface CommonAssignInst : CommonInst { @@ -37,7 +34,6 @@ interface CommonAssignInst : CommonInst { val rhv: CommonExpr } -// TODO: add 'callExpr: CoreExpr' property interface CommonCallInst : CommonInst interface CommonReturnInst : CommonInst { diff --git a/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/cfg/CommonValue.kt b/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/cfg/CommonValue.kt index f9280859f..6b958a953 100644 --- a/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/cfg/CommonValue.kt +++ b/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/cfg/CommonValue.kt @@ -23,8 +23,7 @@ interface CommonThis : CommonValue interface CommonArgument : CommonValue interface CommonFieldRef : CommonValue { - val instance: CommonValue? // null for static fields - // val classField: CommonField + val instance: CommonValue? } interface CommonArrayAccess : CommonValue { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/JcBlockGraphImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/JcBlockGraphImpl.kt index cd69d769c..ddc8b17e1 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/JcBlockGraphImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/JcBlockGraphImpl.kt @@ -109,8 +109,8 @@ class JcBlockGraphImpl( (block.start.index..block.end.index).map { jcGraph.instructions[it] } override fun block(inst: JcInst): JcBasicBlock { - assert(inst.location.method == jcGraph.method) { - "required method of instruction ${jcGraph.method} but got ${inst.location.method}" + assert(inst.method == jcGraph.method) { + "required method of instruction ${jcGraph.method} but got ${inst.method}" } for (basicBlock in entries) { if (basicBlock.contains(inst)) { diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/graphs/GraphDominators.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/graphs/GraphDominators.kt index 6ea19e0bd..5c2384b8d 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/graphs/GraphDominators.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/graphs/GraphDominators.kt @@ -17,13 +17,8 @@ package org.jacodb.impl.cfg.graphs import org.jacodb.api.common.cfg.BytecodeGraph -import org.jacodb.api.jvm.cfg.JcBasicBlock -import org.jacodb.api.jvm.cfg.JcBlockGraph import org.jacodb.api.jvm.cfg.JcCatchInst -import org.jacodb.api.jvm.cfg.JcGraph -import org.jacodb.api.jvm.cfg.JcInst -import java.util.* - +import java.util.BitSet /** * Calculate dominators for basic blocks. @@ -159,14 +154,6 @@ open class GraphDominators(val graph: BytecodeGraph) { } } -fun JcGraph.findDominators(): GraphDominators { - return GraphDominators(this).also { - it.find() - } -} - -fun JcBlockGraph.findDominators(): GraphDominators { - return GraphDominators(this).also { - it.find() - } +fun BytecodeGraph.findDominators(): GraphDominators { + return GraphDominators(this).also { it.find() } } diff --git a/jacodb-ets/build.gradle.kts b/jacodb-ets/build.gradle.kts index f69e99f08..3349239e9 100644 --- a/jacodb-ets/build.gradle.kts +++ b/jacodb-ets/build.gradle.kts @@ -7,11 +7,11 @@ plugins { dependencies { api(project(":jacodb-api-common")) - api(project(":jacodb-core")) implementation(Libs.kotlin_logging) implementation(Libs.slf4j_simple) implementation(Libs.kotlinx_serialization_json) + implementation(Libs.kotlinx_coroutines_core) implementation(Libs.jdot) testImplementation(kotlin("test")) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/Position.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/Position.kt deleted file mode 100644 index b9578205e..000000000 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/Position.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.base - -interface Position { - val firstLine: Int - val lastLine: Int - val firstColumn: Int - val lastColumn: Int - - companion object { - /** - * A position that represents an unknown location. - */ - val NO_POSITION = object : Position { - override val firstLine = -1 - override val lastLine = -1 - override val firstColumn = -1 - override val lastColumn = -1 - - override fun toString(): String = "NoPosition" - } - } - - data class LinePosition( - val lineNumber: Int, - ) : Position { - override val firstLine: Int - get() = lineNumber - override val lastLine: Int - get() = lineNumber - override val firstColumn: Int - get() = 0 - override val lastColumn: Int - get() = -1 - - override fun toString(): String { - return "[$lineNumber]" - } - } - - data class FullPosition( - override val firstLine: Int, - override val lastLine: Int, - override val firstColumn: Int, - override val lastColumn: Int, - ) : Position { - init { - require(firstLine >= 0) - require(lastLine >= firstLine) - } - - override fun toString(): String = buildString { - append('[') - append(firstLine) - if (firstColumn >= 0) { - append(':') - append(firstColumn) - } - append('-') - append(lastLine) - if (lastColumn >= 0) { - append(':') - append(lastColumn) - } - append(']') - } - } -} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/StmtPositionInfo.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/StmtPositionInfo.kt deleted file mode 100644 index 1cf6ea75e..000000000 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/StmtPositionInfo.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.base - -/** - * Information about the position of a statement. - */ -interface StmtPositionInfo { - val position: Position - - fun getOperandPosition(index: Int): Position? - - companion object { - val NO_POSITION_INFO = object : StmtPositionInfo { - override val position = Position.NO_POSITION - override fun getOperandPosition(index: Int): Position = Position.NO_POSITION - override fun toString(): String = "NoPositionInformation" - } - } -} - -/** - * Simple statement position information that - * only contains the position of the statement. - */ -data class SimpleStmtPositionInfo( - override val position: Position, -) : StmtPositionInfo { - override fun getOperandPosition(index: Int): Position? = null - - override fun toString(): String { - return "stmt at: $position" - } -} - -/** - * Full statement position information that - * contains the position of the statement - * and the positions of its operands. - */ -data class FullStmtPositionInfo( - override val position: Position, - val operandPositions: List, -) : StmtPositionInfo { - override fun getOperandPosition(index: Int): Position = operandPositions[index] - - override fun toString(): String { - return "stmt at: $position, operands at: $operandPositions" - } -} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dsl/BlockCfg.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dsl/BlockCfg.kt index de876ffc5..8fccfaa96 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dsl/BlockCfg.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dsl/BlockCfg.kt @@ -26,7 +26,7 @@ data class Block( data class BlockCfg( val blocks: List, - val successors: Map>, + val successors: Map>, // for IF stmt, successors are (true, false) branches ) fun Program.toBlockCfg(): BlockCfg { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt index 655333ef8..2eb5923df 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt @@ -16,139 +16,134 @@ package org.jacodb.ets.dto -import org.jacodb.ets.base.CONSTRUCTOR_NAME -import org.jacodb.ets.base.EtsAddExpr -import org.jacodb.ets.base.EtsAliasType -import org.jacodb.ets.base.EtsAndExpr -import org.jacodb.ets.base.EtsAnyType -import org.jacodb.ets.base.EtsArrayAccess -import org.jacodb.ets.base.EtsArrayType -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.EtsBooleanType -import org.jacodb.ets.base.EtsCallExpr -import org.jacodb.ets.base.EtsCallStmt -import org.jacodb.ets.base.EtsCastExpr -import org.jacodb.ets.base.EtsClassType -import org.jacodb.ets.base.EtsCommaExpr -import org.jacodb.ets.base.EtsConstant -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.EtsExpr -import org.jacodb.ets.base.EtsFieldRef -import org.jacodb.ets.base.EtsFunctionType -import org.jacodb.ets.base.EtsGenericType -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.EtsInstLocation -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.EtsLiteralType -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.EtsNeverType -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.EtsNullType -import org.jacodb.ets.base.EtsNullishCoalescingExpr -import org.jacodb.ets.base.EtsNumberConstant -import org.jacodb.ets.base.EtsNumberType -import org.jacodb.ets.base.EtsOrExpr -import org.jacodb.ets.base.EtsParameterRef -import org.jacodb.ets.base.EtsPreDecExpr -import org.jacodb.ets.base.EtsPreIncExpr -import org.jacodb.ets.base.EtsPtrCallExpr -import org.jacodb.ets.base.EtsRawEntity -import org.jacodb.ets.base.EtsRawStmt -import org.jacodb.ets.base.EtsRawType -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.EtsStringType -import org.jacodb.ets.base.EtsSubExpr -import org.jacodb.ets.base.EtsSwitchStmt -import org.jacodb.ets.base.EtsThis -import org.jacodb.ets.base.EtsThrowStmt -import org.jacodb.ets.base.EtsTupleType -import org.jacodb.ets.base.EtsType -import org.jacodb.ets.base.EtsTypeOfExpr -import org.jacodb.ets.base.EtsUnaryPlusExpr -import org.jacodb.ets.base.EtsUnclearRefType -import org.jacodb.ets.base.EtsUndefinedConstant -import org.jacodb.ets.base.EtsUndefinedType -import org.jacodb.ets.base.EtsUnionType -import org.jacodb.ets.base.EtsUnknownType -import org.jacodb.ets.base.EtsUnsignedRightShiftExpr -import org.jacodb.ets.base.EtsValue -import org.jacodb.ets.base.EtsVoidType -import org.jacodb.ets.base.EtsYieldExpr -import org.jacodb.ets.graph.EtsCfg +import org.jacodb.ets.model.BasicBlock +import org.jacodb.ets.model.EtsAddExpr +import org.jacodb.ets.model.EtsAliasType +import org.jacodb.ets.model.EtsAndExpr +import org.jacodb.ets.model.EtsAnyType +import org.jacodb.ets.model.EtsArrayAccess +import org.jacodb.ets.model.EtsArrayType +import org.jacodb.ets.model.EtsAssignStmt +import org.jacodb.ets.model.EtsAwaitExpr +import org.jacodb.ets.model.EtsBitAndExpr +import org.jacodb.ets.model.EtsBitNotExpr +import org.jacodb.ets.model.EtsBitOrExpr +import org.jacodb.ets.model.EtsBitXorExpr +import org.jacodb.ets.model.EtsBlockCfg +import org.jacodb.ets.model.EtsBooleanConstant +import org.jacodb.ets.model.EtsBooleanType +import org.jacodb.ets.model.EtsCallExpr +import org.jacodb.ets.model.EtsCallStmt +import org.jacodb.ets.model.EtsCastExpr import org.jacodb.ets.model.EtsClass import org.jacodb.ets.model.EtsClassCategory import org.jacodb.ets.model.EtsClassImpl import org.jacodb.ets.model.EtsClassSignature +import org.jacodb.ets.model.EtsClassType +import org.jacodb.ets.model.EtsConstant import org.jacodb.ets.model.EtsDecorator +import org.jacodb.ets.model.EtsDeleteExpr +import org.jacodb.ets.model.EtsDivExpr +import org.jacodb.ets.model.EtsEntity +import org.jacodb.ets.model.EtsEqExpr +import org.jacodb.ets.model.EtsExpExpr +import org.jacodb.ets.model.EtsExpr import org.jacodb.ets.model.EtsField import org.jacodb.ets.model.EtsFieldImpl +import org.jacodb.ets.model.EtsFieldRef import org.jacodb.ets.model.EtsFieldSignature -import org.jacodb.ets.model.EtsFieldSubSignature import org.jacodb.ets.model.EtsFile import org.jacodb.ets.model.EtsFileSignature +import org.jacodb.ets.model.EtsFunctionType +import org.jacodb.ets.model.EtsGenericType +import org.jacodb.ets.model.EtsGtEqExpr +import org.jacodb.ets.model.EtsGtExpr +import org.jacodb.ets.model.EtsIfStmt +import org.jacodb.ets.model.EtsInExpr +import org.jacodb.ets.model.EtsInstanceCallExpr +import org.jacodb.ets.model.EtsInstanceFieldRef +import org.jacodb.ets.model.EtsInstanceOfExpr +import org.jacodb.ets.model.EtsIntersectionType +import org.jacodb.ets.model.EtsLeftShiftExpr +import org.jacodb.ets.model.EtsLiteralType +import org.jacodb.ets.model.EtsLocal import org.jacodb.ets.model.EtsLocalSignature +import org.jacodb.ets.model.EtsLtEqExpr +import org.jacodb.ets.model.EtsLtExpr import org.jacodb.ets.model.EtsMethod import org.jacodb.ets.model.EtsMethodImpl import org.jacodb.ets.model.EtsMethodParameter import org.jacodb.ets.model.EtsMethodSignature import org.jacodb.ets.model.EtsModifiers +import org.jacodb.ets.model.EtsMulExpr import org.jacodb.ets.model.EtsNamespace import org.jacodb.ets.model.EtsNamespaceSignature +import org.jacodb.ets.model.EtsNegExpr +import org.jacodb.ets.model.EtsNeverType +import org.jacodb.ets.model.EtsNewArrayExpr +import org.jacodb.ets.model.EtsNewExpr +import org.jacodb.ets.model.EtsNopStmt +import org.jacodb.ets.model.EtsNotEqExpr +import org.jacodb.ets.model.EtsNotExpr +import org.jacodb.ets.model.EtsNullConstant +import org.jacodb.ets.model.EtsNullType +import org.jacodb.ets.model.EtsNullishCoalescingExpr +import org.jacodb.ets.model.EtsNumberConstant +import org.jacodb.ets.model.EtsNumberType +import org.jacodb.ets.model.EtsOrExpr +import org.jacodb.ets.model.EtsParameterRef +import org.jacodb.ets.model.EtsPreDecExpr +import org.jacodb.ets.model.EtsPreIncExpr +import org.jacodb.ets.model.EtsPtrCallExpr +import org.jacodb.ets.model.EtsRawEntity +import org.jacodb.ets.model.EtsRawStmt +import org.jacodb.ets.model.EtsRawType +import org.jacodb.ets.model.EtsRemExpr +import org.jacodb.ets.model.EtsReturnStmt +import org.jacodb.ets.model.EtsRightShiftExpr +import org.jacodb.ets.model.EtsStaticCallExpr +import org.jacodb.ets.model.EtsStaticFieldRef +import org.jacodb.ets.model.EtsStmt +import org.jacodb.ets.model.EtsStmtLocation +import org.jacodb.ets.model.EtsStrictEqExpr +import org.jacodb.ets.model.EtsStrictNotEqExpr +import org.jacodb.ets.model.EtsStringConstant +import org.jacodb.ets.model.EtsStringType +import org.jacodb.ets.model.EtsSubExpr +import org.jacodb.ets.model.EtsThis +import org.jacodb.ets.model.EtsThrowStmt +import org.jacodb.ets.model.EtsTupleType +import org.jacodb.ets.model.EtsType +import org.jacodb.ets.model.EtsTypeOfExpr +import org.jacodb.ets.model.EtsUnaryPlusExpr +import org.jacodb.ets.model.EtsUnclearRefType +import org.jacodb.ets.model.EtsUndefinedConstant +import org.jacodb.ets.model.EtsUndefinedType +import org.jacodb.ets.model.EtsUnionType +import org.jacodb.ets.model.EtsUnknownType +import org.jacodb.ets.model.EtsUnsignedRightShiftExpr +import org.jacodb.ets.model.EtsValue +import org.jacodb.ets.model.EtsVoidType +import org.jacodb.ets.model.EtsYieldExpr class EtsMethodBuilder( signature: EtsMethodSignature, typeParameters: List = emptyList(), - locals: List = emptyList(), modifiers: EtsModifiers = EtsModifiers.EMPTY, decorators: List = emptyList(), ) { - private val etsMethod = EtsMethodImpl(signature, typeParameters, locals, modifiers, decorators) + private val method = EtsMethodImpl(signature, typeParameters, modifiers, decorators) - private val currentStmts: MutableList = mutableListOf() + private lateinit var currentStmts: MutableList private var freeTempLocal: Int = 0 - private fun newTempLocal(type: EtsType): EtsLocal { - return EtsLocal("_tmp${freeTempLocal++}", type) + private fun newTempLocal(): EtsLocal { + return EtsLocal("_tmp${freeTempLocal++}") } - private fun loc(): EtsInstLocation { - return EtsInstLocation(etsMethod, currentStmts.size) + private fun loc(): EtsStmtLocation { + return EtsStmtLocation.stub(method) } private var built: Boolean = false @@ -156,16 +151,16 @@ class EtsMethodBuilder( fun build(cfgDto: CfgDto): EtsMethod { require(!built) { "Method has already been built" } val cfg = cfgDto.toEtsCfg() - etsMethod._cfg = cfg + method._cfg = cfg built = true - return etsMethod + return method } private fun ensureLocal(entity: EtsEntity): EtsLocal { if (entity is EtsLocal) { return entity } - val newLocal = newTempLocal(entity.type) + val newLocal = newTempLocal() currentStmts += EtsAssignStmt( location = loc(), lhv = newLocal, @@ -224,7 +219,7 @@ class EtsMethodBuilder( } is ReturnStmtDto -> { - val returnValue = ensureOneAddress(arg.toEtsEntity()) + val returnValue = ensureLocal(arg.toEtsEntity()) EtsReturnStmt( location = loc(), returnValue = returnValue, @@ -239,35 +234,21 @@ class EtsMethodBuilder( } is ThrowStmtDto -> { - val arg = arg.toEtsEntity() + val exception = ensureLocal(arg.toEtsEntity()) EtsThrowStmt( location = loc(), - arg = arg, + exception = exception, ) } - is GotoStmtDto -> { - EtsGotoStmt(location = loc()) - } - is IfStmtDto -> { - val condition = condition.toEtsEntity() + val condition = ensureLocal(condition.toEtsEntity()) EtsIfStmt( location = loc(), condition = condition, ) } - is SwitchStmtDto -> { - val arg = arg.toEtsEntity() - val cases = cases.map { it.toEtsEntity() } - EtsSwitchStmt( - location = loc(), - arg = arg, - cases = cases, - ) - } - is RawStmtDto -> { EtsRawStmt( location = loc(), @@ -283,7 +264,7 @@ class EtsMethodBuilder( is ConstantDto -> toEtsConstant() is NewExprDto -> EtsNewExpr( - type = classType.toEtsType() // TODO: safe cast to ClassType + type = classType.toEtsType(), ) is NewArrayExprDto -> EtsNewArrayExpr( @@ -312,10 +293,6 @@ class EtsMethodBuilder( checkType = checkType.toEtsType(), ) - is LengthExprDto -> EtsLengthExpr( - arg = arg.toEtsEntity(), - ) - is CastExprDto -> EtsCastExpr( arg = arg.toEtsEntity(), type = type.toEtsType(), @@ -326,11 +303,11 @@ class EtsMethodBuilder( // Note: `type` is ignored here! when (op) { Ops.Unary.NOT -> EtsNotExpr(arg) - Ops.Unary.BIT_NOT -> EtsBitNotExpr(arg.type, arg) - Ops.Unary.MINUS -> EtsNegExpr(arg.type, arg) + Ops.Unary.BIT_NOT -> EtsBitNotExpr(arg) + Ops.Unary.MINUS -> EtsNegExpr(arg) Ops.Unary.PLUS -> EtsUnaryPlusExpr(arg) - Ops.Unary.INC -> EtsPreIncExpr(arg.type, arg) - Ops.Unary.DEC -> EtsPreDecExpr(arg.type, arg) + Ops.Unary.INC -> EtsPreIncExpr(arg) + Ops.Unary.DEC -> EtsPreDecExpr(arg) else -> error("Unknown unop: '$op'") } } @@ -338,24 +315,23 @@ class EtsMethodBuilder( is BinaryOperationDto -> { val left = left.toEtsEntity() val right = right.toEtsEntity() - val type = type.toEtsType() + // val type = type.toEtsType() when (op) { - Ops.Binary.ADD -> EtsAddExpr(type, left, right) - Ops.Binary.SUB -> EtsSubExpr(type, left, right) - Ops.Binary.MUL -> EtsMulExpr(type, left, right) - Ops.Binary.DIV -> EtsDivExpr(type, left, right) - Ops.Binary.MOD -> EtsRemExpr(type, left, right) - Ops.Binary.EXP -> EtsExpExpr(type, left, right) - Ops.Binary.BIT_AND -> EtsBitAndExpr(type, left, right) - Ops.Binary.BIT_OR -> EtsBitOrExpr(type, left, right) - Ops.Binary.BIT_XOR -> EtsBitXorExpr(type, left, right) - Ops.Binary.LSH -> EtsLeftShiftExpr(type, left, right) - Ops.Binary.RSH -> EtsRightShiftExpr(type, left, right) - Ops.Binary.URSH -> EtsUnsignedRightShiftExpr(type, left, right) - Ops.Binary.AND -> EtsAndExpr(type, left, right) - Ops.Binary.OR -> EtsOrExpr(type, left, right) - Ops.Binary.NULLISH -> EtsNullishCoalescingExpr(type, left, right) - Ops.Binary.COMMA -> EtsCommaExpr(left, right) // Note: `type` is ignored here! + Ops.Binary.ADD -> EtsAddExpr(left, right) + Ops.Binary.SUB -> EtsSubExpr(left, right) + Ops.Binary.MUL -> EtsMulExpr(left, right) + Ops.Binary.DIV -> EtsDivExpr(left, right) + Ops.Binary.MOD -> EtsRemExpr(left, right) + Ops.Binary.EXP -> EtsExpExpr(left, right) + Ops.Binary.BIT_AND -> EtsBitAndExpr(left, right) + Ops.Binary.BIT_OR -> EtsBitOrExpr(left, right) + Ops.Binary.BIT_XOR -> EtsBitXorExpr(left, right) + Ops.Binary.LSH -> EtsLeftShiftExpr(left, right) + Ops.Binary.RSH -> EtsRightShiftExpr(left, right) + Ops.Binary.URSH -> EtsUnsignedRightShiftExpr(left, right) + Ops.Binary.AND -> EtsAndExpr(left, right) + Ops.Binary.OR -> EtsOrExpr(left, right) + Ops.Binary.NULLISH -> EtsNullishCoalescingExpr(left, right) else -> error("Unknown binop: $op") } } @@ -380,33 +356,30 @@ class EtsMethodBuilder( is InstanceCallExprDto -> EtsInstanceCallExpr( instance = (instance as LocalDto).toEtsLocal(), // safe cast - method = method.toEtsMethodSignature(), + callee = method.toEtsMethodSignature(), args = args.map { ensureLocal(it.toEtsEntity()) }, ) is StaticCallExprDto -> EtsStaticCallExpr( - method = method.toEtsMethodSignature(), + callee = method.toEtsMethodSignature(), args = args.map { ensureLocal(it.toEtsEntity()) }, ) is PtrCallExprDto -> EtsPtrCallExpr( ptr = ensureLocal(ptr.toEtsEntity() as EtsValue), // safe cast - method = method.toEtsMethodSignature(), + callee = method.toEtsMethodSignature(), args = args.map { ensureLocal(it.toEtsEntity()) }, ) - is ThisRefDto -> EtsThis( - type = (type as ClassTypeDto).toEtsClassType(), // safe cast - ) + is ThisRefDto -> EtsThis is ParameterRefDto -> EtsParameterRef( index = index, - type = type.toEtsType(), ) is ArrayRefDto -> EtsArrayAccess( - array = array.toEtsEntity() as EtsValue, // TODO: check whether the cast is safe - index = index.toEtsEntity() as EtsValue, // TODO: check whether the cast is safe + array = ensureLocal(array.toEtsEntity() as EtsValue), // safe cast + index = index.toEtsEntity() as EtsValue, // safe cast type = type.toEtsType(), ) @@ -415,129 +388,68 @@ class EtsMethodBuilder( is RawValueDto -> EtsRawEntity( kind = kind, extra = extra, - type = type.toEtsType(), ) } private fun FieldRefDto.toEtsFieldRef(): EtsFieldRef { - val field = field.toEtsFieldSignature() return when (this) { is InstanceFieldRefDto -> EtsInstanceFieldRef( instance = (instance as LocalDto).toEtsLocal(), // safe cast - field = field, + field = field.toEtsFieldSignature(), + type = type.toEtsType(), ) is StaticFieldRefDto -> EtsStaticFieldRef( - field = field, + field = field.toEtsFieldSignature(), + type = type.toEtsType(), ) } } - private fun CfgDto.toEtsCfg(): EtsCfg { - require(blocks.isNotEmpty()) { - "Method body should contain at least return stmt" - } - - val visited: MutableSet = hashSetOf(0) - val queue: ArrayDeque = ArrayDeque() - queue.add(0) - - val blocks = blocks.associateBy { it.id } - val blockStart: MutableMap = hashMapOf() - val blockEnd: MutableMap = hashMapOf() - - while (queue.isNotEmpty()) { - val block = blocks[queue.removeFirst()]!! - blockStart[block.id] = currentStmts.size - if (block.stmts.isNotEmpty()) { - for (stmt in block.stmts) { - currentStmts += stmt.toEtsStmt() - } - } else { - currentStmts += EtsNopStmt(loc()) - } - blockEnd[block.id] = currentStmts.lastIndex - check(blockStart[block.id]!! <= blockEnd[block.id]!!) - - for (next in block.successors) { - if (visited.add(next)) { - queue.addLast(next) - } - } + private fun CfgDto.toEtsCfg(): EtsBlockCfg { + if (blocks.isEmpty()) { + return EtsBlockCfg.EMPTY } - val successorMap: MutableMap> = hashMapOf() - for (block in this.blocks) { - val startId = blockStart[block.id]!! - val endId = blockEnd[block.id]!! - for (i in startId until endId) { - successorMap[currentStmts[i]] = listOf(currentStmts[i + 1]) + val blocks = this.blocks.map { block -> + currentStmts = mutableListOf() + for (stmt in block.stmts) { + currentStmts += stmt.toEtsStmt() } - successorMap[currentStmts[endId]] = block.successors.mapNotNull { blockId -> - blockStart[blockId]?.let { currentStmts[it] } + if (currentStmts.isEmpty()) { + currentStmts += EtsNopStmt(location = loc()) } + BasicBlock(block.id, currentStmts) } + // Note: in DTO, successors for IF stmts are (false, true) branches, + // however in all our CFGs we use (true, false) order. + val successors = this.blocks.associate { it.id to it.successors.asReversed() } - return EtsCfg( - stmts = currentStmts, - successorMap = successorMap, + return EtsBlockCfg( + blocks = blocks, + successors = successors, ) } } fun ClassDto.toEtsClass(): EtsClass { - fun defaultConstructorDto(classSignatureDto: ClassSignatureDto): MethodDto { - val zeroBlock = BasicBlockDto( - id = 0, - successors = emptyList(), - predecessors = emptyList(), - stmts = listOf( - ReturnVoidStmtDto, - ), - ) - val cfg = CfgDto(blocks = listOf(zeroBlock)) - val body = BodyDto(locals = emptyList(), cfg = cfg) - val signature = MethodSignatureDto( - declaringClass = classSignatureDto, - name = CONSTRUCTOR_NAME, - parameters = emptyList(), - returnType = ClassTypeDto(classSignatureDto), - ) - return MethodDto( - signature = signature, - modifiers = 0, - decorators = emptyList(), - typeParameters = emptyList(), - body = body, - ) - } - val signature = signature.toEtsClassSignature() val superClassSignature = superClassName?.takeIf { it != "" }?.let { name -> EtsClassSignature( name = name, - file = EtsFileSignature.DEFAULT, + file = EtsFileSignature.UNKNOWN, ) } val implementedInterfaces = implementedInterfaceNames.map { name -> EtsClassSignature( name = name, - file = EtsFileSignature.DEFAULT, + file = EtsFileSignature.UNKNOWN, ) } - val fields = fields.map { it.toEtsField() } - - val (methodDtos, ctorDtos) = methods.partition { it.signature.name != CONSTRUCTOR_NAME } - check(ctorDtos.size <= 1) { "Class should not have multiple constructors" } - val ctorDto = ctorDtos.firstOrNull() ?: defaultConstructorDto(this.signature) - - val methods = methodDtos.map { it.toEtsMethod() } - val ctor = ctorDto.toEtsMethod() - + val methods = methods.map { it.toEtsMethod() } val category = category.toEtsClassCategory() val typeParameters = typeParameters?.map { it.toEtsType() } ?: emptyList() - val modifiers = EtsModifiers(modifiers) val decorators = decorators.map { it.toEtsDecorator() } @@ -545,7 +457,6 @@ fun ClassDto.toEtsClass(): EtsClass { signature = signature, fields = fields, methods = methods, - ctor = ctor, category = category, superClass = superClassSignature, implementedInterfaces = implementedInterfaces, @@ -574,14 +485,18 @@ fun TypeDto.toEtsType(): EtsType = when (this) { is ClassTypeDto -> toEtsClassType() is FunctionTypeDto -> EtsFunctionType( - method = signature.toEtsMethodSignature(), + signature = signature.toEtsMethodSignature(), typeParameters = typeParameters.map { it.toEtsType() }, ) is GenericTypeDto -> EtsGenericType( - name = name, - defaultType = defaultType?.toEtsType(), + typeName = name, constraint = constraint?.toEtsType(), + defaultType = this@toEtsType.defaultType?.toEtsType(), + ) + + is IntersectionTypeDto -> EtsIntersectionType( + types = types.map { it.toEtsType() }, ) is LiteralTypeDto -> EtsLiteralType( @@ -640,18 +555,8 @@ fun ConstantDto.toEtsConstant(): EtsConstant { EtsUndefinedType -> EtsUndefinedConstant - else -> object : EtsConstant { - override val type: EtsType = EtsUnknownType - - override fun toString(): String = "Unknown($value)" - - override fun accept(visitor: EtsValue.Visitor): R { - if (visitor is EtsValue.Visitor.Default) { - return visitor.defaultVisit(this) - } - error("Cannot handle $this") - } - } + // Note: we simply use StringConstant for all other types + else -> EtsStringConstant(value = this.value) } } @@ -681,10 +586,8 @@ fun ClassSignatureDto.toEtsClassSignature(): EtsClassSignature { fun FieldSignatureDto.toEtsFieldSignature(): EtsFieldSignature { return EtsFieldSignature( enclosingClass = declaringClass.toEtsClassSignature(), - sub = EtsFieldSubSignature( - name = name, - type = type.toEtsType(), - ), + name = name, + type = type.toEtsType(), ) } @@ -718,13 +621,9 @@ fun MethodDto.toEtsMethod(): EtsMethod { val modifiers = EtsModifiers(modifiers) val decorators = decorators.map { it.toEtsDecorator() } if (body != null) { - val locals = body.locals.map { - it.toEtsLocal() - } val builder = EtsMethodBuilder( signature = signature, typeParameters = typeParameters, - locals = locals, modifiers = modifiers, decorators = decorators, ) @@ -733,7 +632,6 @@ fun MethodDto.toEtsMethod(): EtsMethod { return EtsMethodImpl( signature = signature, typeParameters = typeParameters, - locals = emptyList(), modifiers = modifiers, decorators = decorators, ) @@ -744,10 +642,8 @@ fun FieldDto.toEtsField(): EtsField { return EtsFieldImpl( signature = EtsFieldSignature( enclosingClass = signature.declaringClass.toEtsClassSignature(), - sub = EtsFieldSubSignature( - name = signature.name, - type = signature.type.toEtsType(), - ), + name = signature.name, + type = signature.type.toEtsType() ), modifiers = EtsModifiers(modifiers), isOptional = isOptional, diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Ops.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Ops.kt index 0635ed78a..1d12caeb8 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Ops.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Ops.kt @@ -45,7 +45,7 @@ object Ops { const val AND = "&&" const val OR = "||" const val NULLISH = "??" - const val COMMA = "," + // const val COMMA = "," // const val AS = "as" } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Signatures.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Signatures.kt index c265a63fc..42479d9b3 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Signatures.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Signatures.kt @@ -31,6 +31,7 @@ data class NamespaceSignatureDto( val declaringFile: FileSignatureDto, val declaringNamespace: NamespaceSignatureDto? = null, ) + @Serializable data class ClassSignatureDto( val name: String, diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Stmts.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Stmts.kt index 56f0da981..9bdb9f7ac 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Stmts.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Stmts.kt @@ -54,41 +54,24 @@ data class CallStmtDto( val expr: CallExprDto, ) : StmtDto -@Serializable -sealed interface TerminatingStmtDto : StmtDto - @Serializable @SerialName("ReturnVoidStmt") -data object ReturnVoidStmtDto : TerminatingStmtDto +data object ReturnVoidStmtDto : StmtDto @Serializable @SerialName("ReturnStmt") data class ReturnStmtDto( val arg: ValueDto, -) : TerminatingStmtDto +) : StmtDto @Serializable @SerialName("ThrowStmt") data class ThrowStmtDto( val arg: ValueDto, -) : TerminatingStmtDto - -@Serializable -sealed interface BranchingStmtDto : StmtDto - -@Serializable -@SerialName("GotoStmt") -data object GotoStmtDto : BranchingStmtDto +) : StmtDto @Serializable @SerialName("IfStmt") data class IfStmtDto( val condition: ConditionExprDto, -) : BranchingStmtDto - -@Serializable -@SerialName("SwitchStmt") -data class SwitchStmtDto( - val arg: ValueDto, - val cases: List, -) : BranchingStmtDto +) : StmtDto diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Types.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Types.kt index e7ad33a76..2f1d2f569 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Types.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Types.kt @@ -45,6 +45,22 @@ data object AnyTypeDto : TypeDto @SerialName("UnknownType") data object UnknownTypeDto : TypeDto +@Serializable +@SerialName("GenericType") +data class GenericTypeDto( + val name: String, + val constraint: TypeDto? = null, + val defaultType: TypeDto? = null, +) : TypeDto + +@Serializable +@SerialName("AliasType") +data class AliasTypeDto( + val name: String, + val originalType: TypeDto, + val signature: LocalSignatureDto, +) : TypeDto + @Serializable @SerialName("VoidType") data object VoidTypeDto : TypeDto @@ -60,8 +76,8 @@ data class UnionTypeDto( ) : TypeDto @Serializable -@SerialName("TupleType") -data class TupleTypeDto( +@SerialName("IntersectionType") +data class IntersectionTypeDto( val types: List, ) : TypeDto @@ -131,9 +147,9 @@ data class ClassTypeDto( ) : TypeDto @Serializable -@SerialName("FunctionType") -data class FunctionTypeDto( - val signature: MethodSignatureDto, +@SerialName("UnclearReferenceType") +data class UnclearReferenceTypeDto( + val name: String, val typeParameters: List = emptyList(), ) : TypeDto @@ -145,24 +161,14 @@ data class ArrayTypeDto( ) : TypeDto @Serializable -@SerialName("UnclearReferenceType") -data class UnclearReferenceTypeDto( - val name: String, - val typeParameters: List = emptyList(), -) : TypeDto - -@Serializable -@SerialName("GenericType") -data class GenericTypeDto( - val name: String, - val defaultType: TypeDto? = null, - val constraint: TypeDto? = null, +@SerialName("TupleType") +data class TupleTypeDto( + val types: List, ) : TypeDto @Serializable -@SerialName("AliasType") -data class AliasTypeDto( - val name: String, - val originalType: TypeDto, - val signature: LocalSignatureDto, +@SerialName("FunctionType") +data class FunctionTypeDto( + val signature: MethodSignatureDto, + val typeParameters: List = emptyList(), ) : TypeDto diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Values.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Values.kt index 7bc3e2fc5..6212be5c0 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Values.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Values.kt @@ -190,15 +190,6 @@ data class InstanceOfExprDto( get() = BooleanTypeDto } -@Serializable -@SerialName("LengthExpr") -data class LengthExprDto( - val arg: ValueDto, -) : ExprDto { - override val type: TypeDto - get() = NumberTypeDto -} - @Serializable @SerialName("CastExpr") data class CastExprDto( diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsCfg.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsCfg.kt deleted file mode 100644 index 3b7114d24..000000000 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsCfg.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.graph - -import org.jacodb.ets.base.EtsStmt -import org.jacodb.ets.base.EtsTerminatingStmt -import org.jacodb.impl.cfg.graphs.GraphDominators - -class EtsCfg( - val stmts: List, - private val successorMap: Map>, // Note: EtsIfStmt successors are (false, true) branches -) : EtsBytecodeGraph { - - private val predecessorMap: Map> by lazy { - val map: MutableMap> = hashMapOf() - for ((stmt, nexts) in successorMap) { - for (next in nexts) { - map.getOrPut(next) { hashSetOf() } += stmt - } - } - map - } - - override fun throwers(node: EtsStmt): Set { - TODO("Current version of IR does not contain try catch blocks") - } - - override fun catchers(node: EtsStmt): Set { - TODO("Current version of IR does not contain try catch blocks") - } - - override val instructions: List - get() = stmts - - override val entries: List - get() = listOfNotNull(stmts.firstOrNull()) - - override val exits: List = - instructions.filterIsInstance() - - override fun successors(node: EtsStmt): Set { - return successorMap[node]!!.toSet() - } - - override fun predecessors(node: EtsStmt): Set { - return predecessorMap[node].orEmpty() - } - - companion object { - val EMPTY: EtsCfg by lazy { - EtsCfg(emptyList(), emptyMap()) - } - } -} - -fun EtsCfg.findDominators(): GraphDominators { - return GraphDominators(this).also { it.find() } -} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsLoop.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsLoop.kt deleted file mode 100644 index 672d117e0..000000000 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsLoop.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.graph - -import org.jacodb.ets.base.EtsStmt -import java.util.ArrayDeque -import kotlin.LazyThreadSafetyMode.PUBLICATION - -class EtsLoop( - val graph: EtsCfg, - val head: EtsStmt, - val instructions: List, -) { - val exits: Collection by lazy(PUBLICATION) { - val result = hashSetOf() - for (s in instructions) { - graph.successors(s).forEach { - if (!instructions.contains(it)) { - result.add(s) - } - } - } - result - } - - val backJump: EtsStmt get() = instructions.last() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as EtsLoop - - if (head != other.head) return false - if (instructions != other.instructions) return false - - return true - } - - override fun hashCode(): Int { - var result = head.hashCode() - result = 31 * result + instructions.hashCode() - return result - } -} - -val EtsCfg.loops: Set - get() { - val finder = findDominators() - val loops = HashMap>() - instructions.forEach { inst -> - val dominators = finder.dominators(inst) - - val headers = arrayListOf() - successors(inst).forEach { - if (dominators.contains(it)) { - headers.add(it) - } - } - headers.forEach { header -> - val loopBody = loopBodyOf(header, inst) - loops[header] = loops[header]?.union(loopBody) ?: loopBody - } - } - return loops.map { (key, value) -> - newLoop(key, value) - }.toSet() - } - -private fun EtsCfg.newLoop(head: EtsStmt, loopStatements: MutableList): EtsLoop { - // put header to the top - loopStatements.remove(head) - loopStatements.add(0, head) - - // last statement - val backJump = loopStatements.last() - // must branch back to the head - assert(successors(backJump).contains(head)) - return EtsLoop(this, head = head, instructions = loopStatements) -} - -private fun EtsCfg.loopBodyOf(header: EtsStmt, inst: EtsStmt): MutableList { - val loopBody = arrayListOf(header) - val stack = ArrayDeque().also { - it.push(inst) - } - while (!stack.isEmpty()) { - val next = stack.pop() - if (!loopBody.contains(next)) { - loopBody.add(0, next) - predecessors(next).forEach { stack.push(it) } - } - } - assert(inst === header && loopBody.size == 1 || loopBody[loopBody.size - 2] === inst) - assert(loopBody[loopBody.size - 1] === header) - return loopBody -} - -private fun MutableList.union(another: List): MutableList = apply { - addAll(another.filter { !contains(it) }) -} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Base.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Base.kt new file mode 100644 index 000000000..ebeb5f75c --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Base.kt @@ -0,0 +1,31 @@ +/* + * 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.model + +interface Base : WithModifiers { + val modifiers: EtsModifiers + val decorators: List + + // In TS, if "public" modifier is not specified, + // an entity considered public if it is not private and not protected. + override val isPublic: Boolean + get() = super.isPublic || (!isPrivate && !isProtected) + + override fun hasModifier(modifier: EtsModifier): Boolean = modifiers.hasModifier(modifier) + + fun hasDecorator(decorator: EtsDecorator): Boolean = decorators.contains(decorator) +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgBlock.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgBlock.kt new file mode 100644 index 000000000..094c425f2 --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgBlock.kt @@ -0,0 +1,95 @@ +/* + * 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.model + +import org.jacodb.ets.utils.linearize + +data class BasicBlock( + val id: Int, + val statements: List, +) { + init { + require(statements.isNotEmpty()) { "Empty block $id" } + } +} + +class EtsBlockCfg( + val blocks: List, + val successors: Map>, // for 'if-stmt' block, successors are (true, false) branches +) : EtsBytecodeGraph { + init { + for (block in blocks) { + require(block.statements.isNotEmpty()) { "Empty block ${block.id}" } + } + for ((i, block) in blocks.withIndex()) { + require(block.id == i) { "Block id ${block.id} mismatch index $i" } + } + for ((id, successorIds) in successors) { + require(id in 0..blocks.size) { "Block id $id is out of bounds" } + for (s in successorIds) { + require(s in 0..blocks.size) { "Successor $s is out of bounds" } + } + } + } + + val linear: EtsLinearCfg by lazy { + linearize() + } + + val stmts: List + get() = linear.stmts + + override val instructions: List + get() = linear.instructions + override val entries: List + get() = linear.entries + override val exits: List + get() = linear.exits + + override fun successors(stmt: EtsStmt): Set = linear.successors(stmt) + override fun predecessors(stmt: EtsStmt): Set = linear.predecessors(stmt) + override fun throwers(node: EtsStmt): Set = linear.throwers(node) + override fun catchers(node: EtsStmt): Set = linear.catchers(node) + + // val stmts: List by lazy { + // val queue = ArrayDeque() + // val visited: MutableSet = hashSetOf() + // val result = mutableListOf() + // + // if (blocks.isNotEmpty()) { + // queue += blocks.first() + // } + // + // while (queue.isNotEmpty()) { + // val block = queue.removeFirst() + // if (visited.add(block)) { + // result += block.statements + // for (s in successors[block.id].orEmpty()) { + // queue += blocks[s] + // } + // } + // } + // + // result + // } + + companion object { + val EMPTY: EtsBlockCfg by lazy { + EtsBlockCfg(emptyList(), emptyMap()) + } + } +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgLinear.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgLinear.kt new file mode 100644 index 000000000..9bed8691a --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgLinear.kt @@ -0,0 +1,78 @@ +/* + * 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.model + +class EtsLinearCfg( + val stmts: List, + val successors: List>, // for 'if-stmt', successors are (true, false) branches +) : EtsBytecodeGraph { + + val predecessors: List> by lazy { + val result: List> = List(stmts.size) { hashSetOf() } + for ((index, stmt) in stmts.withIndex()) { + for (next in successors[index]) { + result[next].add(index) + } + } + result + } + + override val instructions: List + get() = stmts + override val entries: List = + stmts.take(1) + override val exits: List = + stmts.filterIsInstance() + + private val successorsCache: MutableMap> = hashMapOf() + + override fun successors(node: EtsStmt): Set { + return successorsCache.computeIfAbsent(node) { + successors[node.location.index].mapTo(linkedSetOf()) { stmts[it] } + } + } + + private val predecessorsCache: MutableMap> = hashMapOf() + + override fun predecessors(node: EtsStmt): Set { + return predecessorsCache.computeIfAbsent(node) { + require(node.location.index >= 0) { + "Invalid index: ${node.location.index}" + } + for (p in predecessors[node.location.index]) { + require(p >= 0) { + "Invalid predecessor index: $p" + } + } + predecessors[node.location.index].mapTo(hashSetOf()) { stmts[it] } + } + } + + override fun throwers(node: EtsStmt): Set { + TODO("Current version of IR does not contain try catch blocks") + } + + override fun catchers(node: EtsStmt): Set { + TODO("Current version of IR does not contain try catch blocks") + } + + companion object { + val EMPTY: EtsLinearCfg by lazy { + EtsLinearCfg(emptyList(), emptyList()) + } + } +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsClass.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Class.kt similarity index 74% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsClass.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Class.kt index 74fc77ff1..7ae302381 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsClass.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Class.kt @@ -16,9 +16,10 @@ package org.jacodb.ets.model -import org.jacodb.ets.base.EtsType +import org.jacodb.ets.utils.CONSTRUCTOR_NAME +import org.jacodb.ets.utils.createConstructor -interface EtsClass : EtsBaseModel { +interface EtsClass : Base { val signature: EtsClassSignature val typeParameters: List val fields: List @@ -28,6 +29,9 @@ interface EtsClass : EtsBaseModel { val superClass: EtsClassSignature? val implementedInterfaces: List + val declaringFile: EtsFile? + val declaringNamespace: EtsNamespace? + val name: String get() = signature.name } @@ -36,18 +40,25 @@ class EtsClassImpl( override val signature: EtsClassSignature, override val fields: List, override val methods: List, - override val ctor: EtsMethod, override val category: EtsClassCategory = EtsClassCategory.CLASS, override val superClass: EtsClassSignature? = null, override val implementedInterfaces: List = emptyList(), override val typeParameters: List = emptyList(), - override val modifiers: EtsModifiers = EtsModifiers.EMPTY, + override val modifiers: EtsModifiers = EtsModifiers.Companion.EMPTY, override val decorators: List = emptyList(), ) : EtsClass { init { - require(ctor !in methods) + fields.forEach { (it as EtsFieldImpl).declaringClass = this } + methods.forEach { (it as EtsMethodImpl).enclosingClass = this } } + override var declaringFile: EtsFile? = null + override var declaringNamespace: EtsNamespace? = null + + override val ctor: EtsMethod = + methods.firstOrNull { method -> method.name == CONSTRUCTOR_NAME } + ?: createConstructor(signature) + override fun toString(): String { return signature.toString() } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsClassCategory.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/ClassCategory.kt similarity index 100% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsClassCategory.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/ClassCategory.kt diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsConstant.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt similarity index 85% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsConstant.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt index f81daf77a..3a7c7f4b2 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsConstant.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt @@ -14,16 +14,13 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model interface EtsConstant : EtsImmediate data class EtsStringConstant( val value: String, ) : EtsConstant { - override val type: EtsType - get() = EtsStringType - override fun toString(): String { return "\"$value\"" } @@ -36,9 +33,6 @@ data class EtsStringConstant( data class EtsBooleanConstant( val value: Boolean, ) : EtsConstant { - override val type: EtsType - get() = EtsBooleanType - override fun toString(): String { return if (value) "true" else "false" } @@ -56,9 +50,6 @@ data class EtsBooleanConstant( data class EtsNumberConstant( val value: Double, ) : EtsConstant { - override val type: EtsType - get() = EtsNumberType - override fun toString(): String { return value.toString() } @@ -69,9 +60,6 @@ data class EtsNumberConstant( } object EtsNullConstant : EtsConstant { - override val type: EtsType - get() = EtsNullType - override fun toString(): String = "null" override fun accept(visitor: EtsValue.Visitor): R { @@ -80,9 +68,6 @@ object EtsNullConstant : EtsConstant { } object EtsUndefinedConstant : EtsConstant { - override val type: EtsType - get() = EtsUndefinedType - override fun toString(): String = "undefined" override fun accept(visitor: EtsValue.Visitor): R { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsDecorator.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Decorators.kt similarity index 100% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsDecorator.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Decorators.kt diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsEntity.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt similarity index 91% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsEntity.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt index 68f682ee5..cbb8c07c8 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsEntity.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt @@ -14,15 +14,14 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonExpr interface EtsEntity : CommonExpr { - val type: EtsType override val typeName: String - get() = type.typeName + get() = error("Not supported") interface Visitor : EtsValue.Visitor, @@ -52,10 +51,12 @@ interface EtsEntity : CommonExpr { data class EtsRawEntity( val kind: String, val extra: Map = emptyMap(), - override val type: EtsType, ) : EtsEntity { + // override val type: EtsType + // get() = EtsUnknownType + override fun toString(): String { - return "$kind $extra: $type" + return "$kind $extra" } override fun accept(visitor: EtsEntity.Visitor): R { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsBaseModel.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsBaseModel.kt deleted file mode 100644 index 731e89982..000000000 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsBaseModel.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.model - -interface EtsBaseModel { - val modifiers: EtsModifiers - val decorators: List - - val isPrivate: Boolean get() = modifiers.isPrivate - val isProtected: Boolean get() = modifiers.isProtected - val isPublic: Boolean get() = modifiers.isPublic - val isExport: Boolean get() = modifiers.isExport - val isStatic: Boolean get() = modifiers.isStatic - val isAbstract: Boolean get() = modifiers.isAbstract - val isAsync: Boolean get() = modifiers.isAsync - val isConst: Boolean get() = modifiers.isConst - val isAccessor: Boolean get() = modifiers.isAccessor - val isDefault: Boolean get() = modifiers.isDefault - val isIn: Boolean get() = modifiers.isIn - val isReadonly: Boolean get() = modifiers.isReadonly - val isOut: Boolean get() = modifiers.isOut - val isOverride: Boolean get() = modifiers.isOverride - val isDeclare: Boolean get() = modifiers.isDeclare - - fun hasModifier(modifier: EtsModifier): Boolean = modifiers.hasModifier(modifier) - fun hasDecorator(decorator: EtsDecorator): Boolean = decorators.contains(decorator) -} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsExpr.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt similarity index 83% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsExpr.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt index 8751d607e..637799e07 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsExpr.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt @@ -14,16 +14,15 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonCallExpr -import org.jacodb.ets.model.EtsMethodSignature +import org.jacodb.api.common.cfg.CommonInstanceCallExpr interface EtsExpr : EtsEntity { interface Visitor { fun visit(expr: EtsNewExpr): R fun visit(expr: EtsNewArrayExpr): R - fun visit(expr: EtsLengthExpr): R fun visit(expr: EtsCastExpr): R fun visit(expr: EtsInstanceOfExpr): R @@ -79,14 +78,9 @@ interface EtsExpr : EtsEntity { fun visit(expr: EtsStaticCallExpr): R fun visit(expr: EtsPtrCallExpr): R - // Other - fun visit(expr: EtsCommaExpr): R - fun visit(expr: EtsTernaryExpr): R - interface Default : Visitor { override fun visit(expr: EtsNewExpr): R = defaultVisit(expr) override fun visit(expr: EtsNewArrayExpr): R = defaultVisit(expr) - override fun visit(expr: EtsLengthExpr): R = defaultVisit(expr) override fun visit(expr: EtsCastExpr): R = defaultVisit(expr) override fun visit(expr: EtsInstanceOfExpr): R = defaultVisit(expr) @@ -136,9 +130,6 @@ interface EtsExpr : EtsEntity { override fun visit(expr: EtsStaticCallExpr): R = defaultVisit(expr) override fun visit(expr: EtsPtrCallExpr): R = defaultVisit(expr) - override fun visit(expr: EtsCommaExpr): R = defaultVisit(expr) - override fun visit(expr: EtsTernaryExpr): R = defaultVisit(expr) - fun defaultVisit(expr: EtsExpr): R } } @@ -151,10 +142,10 @@ interface EtsExpr : EtsEntity { } data class EtsNewExpr( - override val type: EtsType, + val type: EtsType, ) : EtsExpr { override fun toString(): String { - return "new ${type.typeName}" + return "new $type" } override fun accept(visitor: EtsExpr.Visitor): R { @@ -166,26 +157,8 @@ data class EtsNewArrayExpr( val elementType: EtsType, val size: EtsEntity, ) : EtsExpr { - override val type: EtsType - get() = EtsArrayType(elementType, 1) - override fun toString(): String { - return "new Array<${elementType.typeName}>($size)" - } - - override fun accept(visitor: EtsExpr.Visitor): R { - return visitor.visit(this) - } -} - -data class EtsLengthExpr( - val arg: EtsEntity, -) : EtsExpr { - override val type: EtsType - get() = EtsNumberType - - override fun toString(): String { - return "${arg}.length" + return "new Array<$elementType>($size)" } override fun accept(visitor: EtsExpr.Visitor): R { @@ -195,7 +168,7 @@ data class EtsLengthExpr( data class EtsCastExpr( val arg: EtsEntity, - override val type: EtsType, + val type: EtsType, ) : EtsExpr { override fun toString(): String { return "$arg as $type" @@ -210,9 +183,6 @@ data class EtsInstanceOfExpr( val arg: EtsEntity, val checkType: EtsType, ) : EtsExpr { - override val type: EtsType - get() = EtsBooleanType - override fun toString(): String { return "$arg instanceof $checkType" } @@ -229,9 +199,6 @@ interface EtsUnaryExpr : EtsExpr { data class EtsDeleteExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { - override val type: EtsType - get() = EtsBooleanType - override fun toString(): String { return "delete $arg" } @@ -244,9 +211,6 @@ data class EtsDeleteExpr( data class EtsAwaitExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { - override val type: EtsType - get() = arg.type - override fun toString(): String { return "await $arg" } @@ -259,9 +223,6 @@ data class EtsAwaitExpr( data class EtsYieldExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { - override val type: EtsType - get() = arg.type - override fun toString(): String { return "yield $arg" } @@ -274,9 +235,6 @@ data class EtsYieldExpr( data class EtsTypeOfExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { - override val type: EtsType - get() = EtsStringType - override fun toString(): String { return "typeof $arg" } @@ -289,9 +247,6 @@ data class EtsTypeOfExpr( data class EtsVoidExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { - override val type: EtsType - get() = EtsUndefinedType - override fun toString(): String { return "void $arg" } @@ -304,9 +259,6 @@ data class EtsVoidExpr( data class EtsNotExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { - override val type: EtsType - get() = EtsBooleanType - override fun toString(): String { return "!$arg" } @@ -317,7 +269,6 @@ data class EtsNotExpr( } data class EtsBitNotExpr( - override val type: EtsType, override val arg: EtsEntity, ) : EtsUnaryExpr { override fun toString(): String { @@ -330,7 +281,6 @@ data class EtsBitNotExpr( } data class EtsNegExpr( - override val type: EtsType, override val arg: EtsEntity, ) : EtsUnaryExpr { override fun toString(): String { @@ -345,9 +295,6 @@ data class EtsNegExpr( data class EtsUnaryPlusExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { - override val type: EtsType - get() = EtsNumberType - override fun toString(): String { return "+$arg" } @@ -358,7 +305,6 @@ data class EtsUnaryPlusExpr( } data class EtsPreIncExpr( - override val type: EtsType, override val arg: EtsEntity, ) : EtsUnaryExpr { override fun toString(): String { @@ -371,7 +317,6 @@ data class EtsPreIncExpr( } data class EtsPreDecExpr( - override val type: EtsType, override val arg: EtsEntity, ) : EtsUnaryExpr { override fun toString(): String { @@ -384,7 +329,6 @@ data class EtsPreDecExpr( } data class EtsPostIncExpr( - override val type: EtsType, override val arg: EtsEntity, ) : EtsUnaryExpr { override fun toString(): String { @@ -397,7 +341,6 @@ data class EtsPostIncExpr( } data class EtsPostDecExpr( - override val type: EtsType, override val arg: EtsEntity, ) : EtsUnaryExpr { override fun toString(): String { @@ -414,10 +357,7 @@ interface EtsBinaryExpr : EtsExpr { val right: EtsEntity } -interface EtsRelationExpr : EtsBinaryExpr { - override val type: EtsType - get() = EtsBooleanType -} +interface EtsRelationExpr : EtsBinaryExpr data class EtsEqExpr( override val left: EtsEntity, @@ -527,9 +467,6 @@ data class EtsInExpr( override val left: EtsEntity, override val right: EtsEntity, ) : EtsRelationExpr { - override val type: EtsType - get() = EtsBooleanType - override fun toString(): String { return "$left in $right" } @@ -542,7 +479,6 @@ data class EtsInExpr( interface EtsArithmeticExpr : EtsBinaryExpr data class EtsAddExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsArithmeticExpr { @@ -556,7 +492,6 @@ data class EtsAddExpr( } data class EtsSubExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsArithmeticExpr { @@ -570,7 +505,6 @@ data class EtsSubExpr( } data class EtsMulExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsArithmeticExpr { @@ -584,7 +518,6 @@ data class EtsMulExpr( } data class EtsDivExpr( - override val type: EtsType, // EtsNumberType override val left: EtsEntity, override val right: EtsEntity, ) : EtsArithmeticExpr { @@ -598,7 +531,6 @@ data class EtsDivExpr( } data class EtsRemExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsArithmeticExpr { @@ -612,7 +544,6 @@ data class EtsRemExpr( } data class EtsExpExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsArithmeticExpr { @@ -628,7 +559,6 @@ data class EtsExpExpr( interface EtsBitwiseExpr : EtsBinaryExpr data class EtsBitAndExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsBitwiseExpr { @@ -642,7 +572,6 @@ data class EtsBitAndExpr( } data class EtsBitOrExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsBitwiseExpr { @@ -656,7 +585,6 @@ data class EtsBitOrExpr( } data class EtsBitXorExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsBitwiseExpr { @@ -670,7 +598,6 @@ data class EtsBitXorExpr( } data class EtsLeftShiftExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsBitwiseExpr { @@ -685,7 +612,6 @@ data class EtsLeftShiftExpr( // Sign-propagating right shift data class EtsRightShiftExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsBitwiseExpr { @@ -700,7 +626,6 @@ data class EtsRightShiftExpr( // Zero-fill right shift data class EtsUnsignedRightShiftExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsBitwiseExpr { @@ -716,7 +641,6 @@ data class EtsUnsignedRightShiftExpr( interface EtsLogicalExpr : EtsBinaryExpr data class EtsAndExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsLogicalExpr { @@ -730,7 +654,6 @@ data class EtsAndExpr( } data class EtsOrExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsLogicalExpr { @@ -744,7 +667,6 @@ data class EtsOrExpr( } data class EtsNullishCoalescingExpr( - override val type: EtsType, override val left: EtsEntity, override val right: EtsEntity, ) : EtsLogicalExpr { @@ -758,20 +680,17 @@ data class EtsNullishCoalescingExpr( } interface EtsCallExpr : EtsExpr, CommonCallExpr { - val method: EtsMethodSignature - override val args: List - - override val type: EtsType - get() = method.returnType + val callee: EtsMethodSignature + override val args: List } data class EtsInstanceCallExpr( - val instance: EtsLocal, - override val method: EtsMethodSignature, - override val args: List, -) : EtsCallExpr { + override val instance: EtsLocal, + override val callee: EtsMethodSignature, + override val args: List, +) : EtsCallExpr, CommonInstanceCallExpr { override fun toString(): String { - return "$instance.${method.name}(${args.joinToString()})" + return "$instance.${callee.name}(${args.joinToString()})" } override fun accept(visitor: EtsExpr.Visitor): R { @@ -780,11 +699,11 @@ data class EtsInstanceCallExpr( } data class EtsStaticCallExpr( - override val method: EtsMethodSignature, - override val args: List, + override val callee: EtsMethodSignature, + override val args: List, ) : EtsCallExpr { override fun toString(): String { - return "${method.enclosingClass.name}.${method.name}(${args.joinToString()})" + return "${callee.enclosingClass.name}.${callee.name}(${args.joinToString()})" } override fun accept(visitor: EtsExpr.Visitor): R { @@ -793,43 +712,12 @@ data class EtsStaticCallExpr( } data class EtsPtrCallExpr( - val ptr: EtsLocal, - override val method: EtsMethodSignature, - override val args: List, + val ptr: EtsValue, // Local or FieldRef + override val callee: EtsMethodSignature, + override val args: List, ) : EtsCallExpr { override fun toString(): String { - return "${ptr}.${method.name}(${args.joinToString()})" - } - - override fun accept(visitor: EtsExpr.Visitor): R { - return visitor.visit(this) - } -} - -data class EtsCommaExpr( - override val left: EtsEntity, - override val right: EtsEntity, -) : EtsBinaryExpr { - override val type: EtsType - get() = right.type - - override fun toString(): String { - return "$left, $right" - } - - override fun accept(visitor: EtsExpr.Visitor): R { - return visitor.visit(this) - } -} - -data class EtsTernaryExpr( - override val type: EtsType, - val condition: EtsEntity, - val thenExpr: EtsEntity, - val elseExpr: EtsEntity, -) : EtsExpr { - override fun toString(): String { - return "$condition ? $thenExpr : $elseExpr" + return "${ptr}.${callee.name}(${args.joinToString()})" } override fun accept(visitor: EtsExpr.Visitor): R { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsField.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Field.kt similarity index 75% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsField.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Field.kt index 326faf74d..d1f895eca 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsField.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Field.kt @@ -16,14 +16,11 @@ package org.jacodb.ets.model -import org.jacodb.ets.base.EtsType - -// for '!' field marker ("definitely assigned field"), see https://www.typescriptlang.org/docs/handbook/2/classes.html#--strictpropertyinitialization - -interface EtsField { - val enclosingClass: EtsClass? +interface EtsField : Base { val signature: EtsFieldSignature + val declaringClass: EtsClass? + val name: String get() = signature.name @@ -33,12 +30,14 @@ interface EtsField { class EtsFieldImpl( override val signature: EtsFieldSignature, - val modifiers: EtsModifiers = EtsModifiers.EMPTY, + override val modifiers: EtsModifiers = EtsModifiers.Companion.EMPTY, val isOptional: Boolean = false, // '?' val isDefinitelyAssigned: Boolean = false, // '!' ) : EtsField { + override var declaringClass: EtsClass? = null - override var enclosingClass: EtsClass? = null + override val decorators: List + get() = error("Fields do not have decorators") override fun toString(): String { return signature.toString() diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsFile.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/File.kt similarity index 86% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsFile.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/File.kt index 606277d2f..818bfccde 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsFile.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/File.kt @@ -21,6 +21,13 @@ class EtsFile( val classes: List, val namespaces: List, ) { + init { + classes.forEach { (it as EtsClassImpl).declaringFile = this } + namespaces.forEach { it.declaringFile = this } + } + + var scene: EtsScene? = null + val name: String get() = signature.fileName val projectName: String diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsBytecodeGraph.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Graph.kt similarity index 96% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsBytecodeGraph.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Graph.kt index e53cc3771..0b0c8e18d 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsBytecodeGraph.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Graph.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.ets.graph +package org.jacodb.ets.model import org.jacodb.api.common.cfg.BytecodeGraph diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsImmediate.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Immediate.kt similarity index 95% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsImmediate.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Immediate.kt index ce70c7b90..0c3d7233e 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsImmediate.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Immediate.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model interface EtsImmediate : EtsValue diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsLValue.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/LValue.kt similarity index 95% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsLValue.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/LValue.kt index 694d2356f..2e40753af 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsLValue.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/LValue.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model interface EtsLValue : EtsValue diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsLocal.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt similarity index 92% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsLocal.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt index 120263d2d..c580522d4 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsLocal.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model data class EtsLocal( val name: String, - override val type: EtsType, + var type: EtsType = EtsUnknownType, ) : EtsImmediate, EtsLValue { override fun toString(): String { return name diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsMethod.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Method.kt similarity index 70% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsMethod.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Method.kt index a2410b529..ab95dc3d0 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsMethod.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Method.kt @@ -19,22 +19,13 @@ package org.jacodb.ets.model import org.jacodb.api.common.CommonMethod -import org.jacodb.ets.base.EtsLocal -import org.jacodb.ets.base.EtsType -import org.jacodb.ets.graph.EtsCfg -interface EtsMethod : EtsBaseModel, CommonMethod { +interface EtsMethod : Base, CommonMethod { val signature: EtsMethodSignature val typeParameters: List - val locals: List - val cfg: EtsCfg + val cfg: EtsBlockCfg - val enclosingClass: EtsClassSignature - get() = signature.enclosingClass - - // If not specified, entity is public if not private and not protected - override val isPublic: Boolean - get() = super.isPublic || (!isPrivate && !isProtected) + val enclosingClass: EtsClass? override val name: String get() = signature.name @@ -45,7 +36,7 @@ interface EtsMethod : EtsBaseModel, CommonMethod { override val returnType: EtsType get() = signature.returnType - override fun flowGraph(): EtsCfg { + override fun flowGraph(): EtsBytecodeGraph { return cfg } } @@ -53,14 +44,15 @@ interface EtsMethod : EtsBaseModel, CommonMethod { class EtsMethodImpl( override val signature: EtsMethodSignature, override val typeParameters: List = emptyList(), - override val locals: List = emptyList(), - override val modifiers: EtsModifiers = EtsModifiers.EMPTY, + override val modifiers: EtsModifiers = EtsModifiers.Companion.EMPTY, override val decorators: List = emptyList(), ) : EtsMethod { - var _cfg: EtsCfg? = null + var _cfg: EtsBlockCfg? = null + + override val cfg: EtsBlockCfg + get() = _cfg ?: EtsBlockCfg.EMPTY - override val cfg: EtsCfg - get() = _cfg ?: EtsCfg.EMPTY + override var enclosingClass: EtsClass? = null override fun toString(): String { return signature.toString() diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsModifier.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Modifiers.kt similarity index 90% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsModifier.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Modifiers.kt index cf584b780..7709e1117 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsModifier.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Modifiers.kt @@ -34,12 +34,7 @@ enum class EtsModifier(val value: Int, val string: String) { DECLARE(1 shl 14, "declare"); } -@JvmInline -value class EtsModifiers(val mask: Int) { - companion object { - val EMPTY = EtsModifiers(0) - } - +interface WithModifiers { val isPrivate: Boolean get() = hasModifier(EtsModifier.PRIVATE) val isProtected: Boolean get() = hasModifier(EtsModifier.PROTECTED) val isPublic: Boolean get() = hasModifier(EtsModifier.PUBLIC) @@ -56,5 +51,14 @@ value class EtsModifiers(val mask: Int) { val isOverride: Boolean get() = hasModifier(EtsModifier.OVERRIDE) val isDeclare: Boolean get() = hasModifier(EtsModifier.DECLARE) - fun hasModifier(modifier: EtsModifier): Boolean = (mask and modifier.value) != 0 + fun hasModifier(modifier: EtsModifier): Boolean +} + +@JvmInline +value class EtsModifiers(val mask: Int) : WithModifiers { + companion object { + val EMPTY = EtsModifiers(0) + } + + override fun hasModifier(modifier: EtsModifier): Boolean = (mask and modifier.value) != 0 } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsNamespace.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Namespace.kt similarity index 78% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsNamespace.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Namespace.kt index 17ab2d0bc..f7a9fb1ea 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsNamespace.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Namespace.kt @@ -21,6 +21,14 @@ class EtsNamespace( val classes: List, val namespaces: List, ) { + init { + classes.forEach { (it as EtsClassImpl).declaringNamespace = this } + namespaces.forEach { it.declaringNamespace = this } + } + + var declaringFile: EtsFile? = null + var declaringNamespace: EtsNamespace? = null + val allClasses: List get() = classes + namespaces.flatMap { it.allClasses } } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsRef.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt similarity index 55% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsRef.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt index c6855b03e..c990d26ff 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsRef.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt @@ -14,17 +14,16 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonArgument +import org.jacodb.api.common.cfg.CommonArrayAccess +import org.jacodb.api.common.cfg.CommonFieldRef import org.jacodb.api.common.cfg.CommonThis -import org.jacodb.ets.model.EtsFieldSignature interface EtsRef : EtsValue -data class EtsThis( - override val type: EtsClassType, -) : EtsRef, CommonThis { +data object EtsThis : EtsRef, EtsImmediate, CommonThis { override fun toString(): String = "this" override fun accept(visitor: EtsValue.Visitor): R { @@ -34,7 +33,6 @@ data class EtsThis( data class EtsParameterRef( val index: Int, - override val type: EtsType, ) : EtsRef, CommonArgument { override fun toString(): String { return "arg$index" @@ -45,53 +43,11 @@ data class EtsParameterRef( } } -// data class EtsCaughtExceptionRef( -// override val type: EtsType, -// ) : EtsValue { -// override fun toString(): String { -// return "catch($type)" -// } -// -// override fun accept(visitor: EtsValue.Visitor): R { -// return visitor.visit(this) -// } -// } -// -// data class EtsGlobalRef( -// val name: String, -// val ref: EtsValue?, // TODO: check whether it could be EtsEntity at best -// ) : EtsValue { -// override val type: EtsType -// get() = ref?.type ?: EtsUnknownType -// -// override fun toString(): String { -// return "global $name" -// } -// -// override fun accept(visitor: EtsValue.Visitor): R { -// return visitor.visit(this) -// } -// } -// -// data class EtsClosureFieldRef( -// val base: EtsLocal, -// val fieldName: String, -// override val type: EtsType, -// ) : EtsValue { -// override fun toString(): String { -// return "$base.$fieldName" -// } -// -// override fun accept(visitor: EtsValue.Visitor): R { -// return visitor.visit(this) -// } -// } - data class EtsArrayAccess( - val array: EtsValue, - val index: EtsValue, - override val type: EtsType, -) : EtsRef, EtsLValue { + override val array: EtsLocal, + override val index: EtsValue, + val type: EtsType, // = EtsUnknownType, +) : EtsRef, EtsLValue, CommonArrayAccess { override fun toString(): String { return "$array[$index]" } @@ -101,19 +57,19 @@ data class EtsArrayAccess( } } -interface EtsFieldRef : EtsRef, EtsLValue { +interface EtsFieldRef : EtsRef, EtsLValue, CommonFieldRef { + override val instance: EtsLocal? val field: EtsFieldSignature - - override val type: EtsType - get() = this.field.type + val type: EtsType } data class EtsInstanceFieldRef( - val instance: EtsLocal, + override val instance: EtsLocal, override val field: EtsFieldSignature, + override val type: EtsType, // = EtsUnknownType, ) : EtsFieldRef { override fun toString(): String { - return "$instance.${field.name}" + return "${instance}.${field.name}" } override fun accept(visitor: EtsValue.Visitor): R { @@ -123,7 +79,10 @@ data class EtsInstanceFieldRef( data class EtsStaticFieldRef( override val field: EtsFieldSignature, + override val type: EtsType, // = EtsUnknownType, ) : EtsFieldRef { + override val instance get() = null + override fun toString(): String { return "${field.enclosingClass.name}.${field.name}" } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsScene.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Scene.kt similarity index 100% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsScene.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Scene.kt diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsSignature.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Signatures.kt similarity index 72% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsSignature.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Signatures.kt index 94867fbe8..220e556dc 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/EtsSignature.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Signatures.kt @@ -17,16 +17,10 @@ package org.jacodb.ets.model import org.jacodb.api.common.CommonMethodParameter -import org.jacodb.ets.base.EtsType -import org.jacodb.ets.base.UNKNOWN_CLASS_NAME -import org.jacodb.ets.base.UNKNOWN_FILE_NAME -import org.jacodb.ets.base.UNKNOWN_NAMESPACE_NAME -import org.jacodb.ets.base.UNKNOWN_PROJECT_NAME - -/** - * Precompiled [Regex] for `.d.ts` and `.ts` file extensions. - */ -private val REGEX_TS_SUFFIX: Regex = """(\.d\.ts|\.ts)$""".toRegex() +import org.jacodb.ets.utils.UNKNOWN_CLASS_NAME +import org.jacodb.ets.utils.UNKNOWN_FILE_NAME +import org.jacodb.ets.utils.UNKNOWN_NAMESPACE_NAME +import org.jacodb.ets.utils.UNKNOWN_PROJECT_NAME data class EtsFileSignature( val projectName: String, @@ -39,7 +33,15 @@ data class EtsFileSignature( } companion object { - val DEFAULT = EtsFileSignature(projectName = UNKNOWN_PROJECT_NAME, fileName = UNKNOWN_FILE_NAME) + /** + * Precompiled [Regex] for `.d.ts` and `.ts` file extensions. + */ + private val REGEX_TS_SUFFIX = """(\.d\.ts|\.ts)$""".toRegex() + + val UNKNOWN = EtsFileSignature( + projectName = UNKNOWN_PROJECT_NAME, + fileName = UNKNOWN_FILE_NAME, + ) } } @@ -57,14 +59,17 @@ data class EtsNamespaceSignature( } companion object { - val DEFAULT = EtsNamespaceSignature(name = UNKNOWN_NAMESPACE_NAME, file = EtsFileSignature.DEFAULT) + val DEFAULT = EtsNamespaceSignature( + name = UNKNOWN_NAMESPACE_NAME, + file = EtsFileSignature.UNKNOWN, + ) } } data class EtsClassSignature( val name: String, val file: EtsFileSignature, - val namespace: EtsNamespaceSignature? = null, + val namespace: EtsNamespaceSignature? = null, // TODO: TsNamespaceSignature ) { override fun toString(): String { return if (namespace != null) { @@ -75,35 +80,25 @@ data class EtsClassSignature( } companion object { - val DEFAULT = EtsClassSignature(name = UNKNOWN_CLASS_NAME, file = EtsFileSignature.DEFAULT) + val UNKNOWN = EtsClassSignature( + name = UNKNOWN_CLASS_NAME, + file = EtsFileSignature.UNKNOWN, + ) } } data class EtsFieldSignature( val enclosingClass: EtsClassSignature, - val sub: EtsFieldSubSignature, -) { - val name: String - get() = sub.name - - val type: EtsType - get() = sub.type - - override fun toString(): String { - return "${enclosingClass.name}::$sub" - } -} - -data class EtsFieldSubSignature( val name: String, val type: EtsType, ) { override fun toString(): String { - return "$name: $type" + return "${enclosingClass.name}::$name: $type" } } data class EtsMethodSignature( + // TODO: rename to 'declaringClass' to distinguish from 'enclosingClass' in Method val enclosingClass: EtsClassSignature, val name: String, val parameters: List, diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsStmt.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Stmt.kt similarity index 71% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsStmt.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Stmt.kt index fa27946f6..834ac3eb6 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsStmt.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Stmt.kt @@ -14,21 +14,16 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonAssignInst +import org.jacodb.api.common.cfg.CommonCallInst +import org.jacodb.api.common.cfg.CommonIfInst import org.jacodb.api.common.cfg.CommonInst -import org.jacodb.api.common.cfg.CommonInstLocation import org.jacodb.api.common.cfg.CommonReturnInst -import org.jacodb.ets.model.EtsMethod - -data class EtsInstLocation( - override val method: EtsMethod, - val index: Int, -) : CommonInstLocation interface EtsStmt : CommonInst { - override val location: EtsInstLocation + override val location: EtsStmtLocation override val method: EtsMethod get() = location.method @@ -36,12 +31,10 @@ interface EtsStmt : CommonInst { interface Visitor { fun visit(stmt: EtsNopStmt): R fun visit(stmt: EtsAssignStmt): R - fun visit(stmt: EtsCallStmt): R fun visit(stmt: EtsReturnStmt): R fun visit(stmt: EtsThrowStmt): R - fun visit(stmt: EtsGotoStmt): R fun visit(stmt: EtsIfStmt): R - fun visit(stmt: EtsSwitchStmt): R + fun visit(stmt: EtsCallStmt): R fun visit(stmt: EtsRawStmt): R { if (this is Default) { @@ -53,12 +46,10 @@ interface EtsStmt : CommonInst { interface Default : Visitor { override fun visit(stmt: EtsNopStmt): R = defaultVisit(stmt) override fun visit(stmt: EtsAssignStmt): R = defaultVisit(stmt) - override fun visit(stmt: EtsCallStmt): R = defaultVisit(stmt) override fun visit(stmt: EtsReturnStmt): R = defaultVisit(stmt) override fun visit(stmt: EtsThrowStmt): R = defaultVisit(stmt) - override fun visit(stmt: EtsGotoStmt): R = defaultVisit(stmt) override fun visit(stmt: EtsIfStmt): R = defaultVisit(stmt) - override fun visit(stmt: EtsSwitchStmt): R = defaultVisit(stmt) + override fun visit(stmt: EtsCallStmt): R = defaultVisit(stmt) override fun visit(stmt: EtsRawStmt): R = defaultVisit(stmt) fun defaultVisit(stmt: EtsStmt): R @@ -69,7 +60,7 @@ interface EtsStmt : CommonInst { } data class EtsRawStmt( - override val location: EtsInstLocation, + override val location: EtsStmtLocation, val kind: String, val extra: Map = emptyMap(), ) : EtsStmt { @@ -83,7 +74,7 @@ data class EtsRawStmt( } data class EtsNopStmt( - override val location: EtsInstLocation, + override val location: EtsStmtLocation, ) : EtsStmt { override fun toString(): String = "nop" @@ -93,8 +84,8 @@ data class EtsNopStmt( } data class EtsAssignStmt( - override val location: EtsInstLocation, - override val lhv: EtsValue, + override val location: EtsStmtLocation, + override val lhv: EtsLValue, override val rhv: EtsEntity, ) : EtsStmt, CommonAssignInst { override fun toString(): String { @@ -106,23 +97,10 @@ data class EtsAssignStmt( } } -data class EtsCallStmt( - override val location: EtsInstLocation, - val expr: EtsCallExpr, -) : EtsStmt { - override fun toString(): String { - return expr.toString() - } - - override fun accept(visitor: EtsStmt.Visitor): R { - return visitor.visit(this) - } -} - interface EtsTerminatingStmt : EtsStmt data class EtsReturnStmt( - override val location: EtsInstLocation, + override val location: EtsStmtLocation, override val returnValue: EtsValue?, ) : EtsTerminatingStmt, CommonReturnInst { override fun toString(): String { @@ -139,11 +117,11 @@ data class EtsReturnStmt( } data class EtsThrowStmt( - override val location: EtsInstLocation, - val arg: EtsEntity, + override val location: EtsStmtLocation, + val exception: EtsLocal, ) : EtsTerminatingStmt { override fun toString(): String { - return "throw $arg" + return "throw $exception" } override fun accept(visitor: EtsStmt.Visitor): R { @@ -151,22 +129,10 @@ data class EtsThrowStmt( } } -interface EtsBranchingStmt : EtsStmt - -class EtsGotoStmt( - override val location: EtsInstLocation, -) : EtsBranchingStmt { - override fun toString(): String = "goto" - - override fun accept(visitor: EtsStmt.Visitor): R { - return visitor.visit(this) - } -} - data class EtsIfStmt( - override val location: EtsInstLocation, - val condition: EtsEntity, -) : EtsBranchingStmt { + override val location: EtsStmtLocation, + val condition: EtsLocal, +) : EtsStmt, CommonIfInst { override fun toString(): String { return "if ($condition)" } @@ -176,13 +142,12 @@ data class EtsIfStmt( } } -data class EtsSwitchStmt( - override val location: EtsInstLocation, - val arg: EtsEntity, - val cases: List, -) : EtsBranchingStmt { +data class EtsCallStmt( + override val location: EtsStmtLocation, + val expr: EtsCallExpr, +) : EtsStmt, CommonCallInst { override fun toString(): String { - return "switch ($arg)" + return expr.toString() } override fun accept(visitor: EtsStmt.Visitor): R { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/StmtLocation.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/StmtLocation.kt new file mode 100644 index 000000000..d9ec452eb --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/StmtLocation.kt @@ -0,0 +1,49 @@ +/* + * 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.model + +import org.jacodb.api.common.cfg.CommonInstLocation + +data class EtsStmtLocation( + override val method: EtsMethod, + var index: Int, +) : CommonInstLocation { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as EtsStmtLocation + + if (method != other.method) return false + // if (index == -1 || other.index == -1) return false + if (index != other.index) return false + + return true + } + + override fun hashCode(): Int { + var result = method.hashCode() + result = 31 * result + index.hashCode() + return result + } + + companion object { + fun stub(method: EtsMethod): EtsStmtLocation { + return EtsStmtLocation(method, -1) + } + } +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsType.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Type.kt similarity index 85% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsType.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Type.kt index b0cabd28d..8b237ca92 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsType.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Type.kt @@ -14,25 +14,24 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model import org.jacodb.api.common.CommonType -import org.jacodb.api.common.CommonTypeName -import org.jacodb.ets.model.EtsClassSignature -import org.jacodb.ets.model.EtsLocalSignature -import org.jacodb.ets.model.EtsMethodSignature -interface EtsType : CommonType, CommonTypeName { - override val typeName: String +interface EtsType : TypeName, CommonType { override val nullable: Boolean? - get() = false + get() = true interface Visitor { fun visit(type: EtsAnyType): R fun visit(type: EtsUnknownType): R fun visit(type: EtsUnionType): R - fun visit(type: EtsTupleType): R + fun visit(type: EtsIntersectionType): R + fun visit(type: EtsGenericType): R + fun visit(type: EtsAliasType): R + + // Primitive fun visit(type: EtsBooleanType): R fun visit(type: EtsNumberType): R fun visit(type: EtsStringType): R @@ -41,12 +40,13 @@ interface EtsType : CommonType, CommonTypeName { fun visit(type: EtsVoidType): R fun visit(type: EtsNeverType): R fun visit(type: EtsLiteralType): R + + // Ref fun visit(type: EtsClassType): R - fun visit(type: EtsFunctionType): R - fun visit(type: EtsArrayType): R fun visit(type: EtsUnclearRefType): R - fun visit(type: EtsGenericType): R - fun visit(type: EtsAliasType): R + fun visit(type: EtsArrayType): R + fun visit(type: EtsTupleType): R + fun visit(type: EtsFunctionType): R fun visit(type: EtsRawType): R { if (this is Default) { @@ -59,7 +59,10 @@ interface EtsType : CommonType, CommonTypeName { override fun visit(type: EtsAnyType): R = defaultVisit(type) override fun visit(type: EtsUnknownType): R = defaultVisit(type) override fun visit(type: EtsUnionType): R = defaultVisit(type) - override fun visit(type: EtsTupleType): R = defaultVisit(type) + override fun visit(type: EtsIntersectionType): R = defaultVisit(type) + override fun visit(type: EtsGenericType): R = defaultVisit(type) + override fun visit(type: EtsAliasType): R = defaultVisit(type) + override fun visit(type: EtsBooleanType): R = defaultVisit(type) override fun visit(type: EtsNumberType): R = defaultVisit(type) override fun visit(type: EtsStringType): R = defaultVisit(type) @@ -68,13 +71,12 @@ interface EtsType : CommonType, CommonTypeName { override fun visit(type: EtsVoidType): R = defaultVisit(type) override fun visit(type: EtsNeverType): R = defaultVisit(type) override fun visit(type: EtsLiteralType): R = defaultVisit(type) + override fun visit(type: EtsClassType): R = defaultVisit(type) - override fun visit(type: EtsFunctionType): R = defaultVisit(type) - override fun visit(type: EtsArrayType): R = defaultVisit(type) override fun visit(type: EtsUnclearRefType): R = defaultVisit(type) - override fun visit(type: EtsGenericType): R = defaultVisit(type) - override fun visit(type: EtsAliasType): R = defaultVisit(type) - override fun visit(type: EtsRawType): R = defaultVisit(type) + override fun visit(type: EtsArrayType): R = defaultVisit(type) + override fun visit(type: EtsTupleType): R = defaultVisit(type) + override fun visit(type: EtsFunctionType): R = defaultVisit(type) fun defaultVisit(type: EtsType): R } @@ -125,7 +127,13 @@ data class EtsUnionType( val types: List, ) : EtsType { override val typeName: String - get() = types.joinToString(separator = " | ") { it.typeName } + get() = types.joinToString(separator = " | ") { + if (it is EtsUnionType || it is EtsIntersectionType) { + "(${it.typeName})" + } else { + it.typeName + } + } override fun toString(): String = typeName @@ -134,11 +142,17 @@ data class EtsUnionType( } } -data class EtsTupleType( +data class EtsIntersectionType( val types: List, ) : EtsType { override val typeName: String - get() = types.joinToString(prefix = "[", postfix = "]") { it.typeName } + get() = types.joinToString(separator = " & ") { + if (it is EtsUnionType || it is EtsIntersectionType) { + "(${it.typeName})" + } else { + it.typeName + } + } override fun toString(): String = typeName @@ -147,6 +161,39 @@ data class EtsTupleType( } } +data class EtsGenericType( + override val typeName: String, + val constraint: EtsType? = null, + val defaultType: EtsType? = null, +) : EtsType { + override fun toString(): String { + return typeName + + (constraint?.let { " extends $it" } ?: "") + + (defaultType?.let { " = $it" } ?: "") + } + + override fun accept(visitor: EtsType.Visitor): R { + return visitor.visit(this) + } +} + +data class EtsAliasType( + val name: String, + val originalType: EtsType, + val signature: EtsLocalSignature, +) : EtsType { + override val typeName: String + get() = name + + override fun toString(): String { + return "$name = $originalType" + } + + override fun accept(visitor: EtsType.Visitor): R { + return visitor.visit(this) + } +} + interface EtsPrimitiveType : EtsType object EtsBooleanType : EtsPrimitiveType { @@ -230,7 +277,7 @@ data class EtsLiteralType( val literalTypeName: String, ) : EtsPrimitiveType { override val typeName: String - get() = "literal" + get() = literalTypeName override fun toString(): String = typeName @@ -247,7 +294,7 @@ data class EtsClassType( ) : EtsRefType { override val typeName: String get() = if (typeParameters.isNotEmpty()) { - val generics = typeParameters.joinToString() + val generics = typeParameters.joinToString { it.typeName } "${signature.name}<$generics>" } else { signature.name @@ -260,16 +307,16 @@ data class EtsClassType( } } -data class EtsFunctionType( - val method: EtsMethodSignature, - val typeParameters: List = emptyList(), +data class EtsUnclearRefType( + val name: String, + val typeParameters: List, ) : EtsRefType { override val typeName: String get() = if (typeParameters.isNotEmpty()) { - val generics = typeParameters.joinToString() - "${method.name}<$generics>" + val generics = typeParameters.joinToString { it.typeName } + "${name}<$generics>" } else { - method.name + name } override fun toString(): String = typeName @@ -293,17 +340,11 @@ data class EtsArrayType( } } -data class EtsUnclearRefType( - val name: String, - val typeParameters: List = emptyList(), +data class EtsTupleType( + val types: List, ) : EtsRefType { override val typeName: String - get() = if (typeParameters.isNotEmpty()) { - val generics = typeParameters.joinToString() - "$name<$generics>" - } else { - name - } + get() = types.joinToString(prefix = "[", postfix = "]") { it.typeName } override fun toString(): String = typeName @@ -312,34 +353,19 @@ data class EtsUnclearRefType( } } -data class EtsGenericType( - val name: String, - val defaultType: EtsType? = null, - val constraint: EtsType? = null, +data class EtsFunctionType( + val signature: EtsMethodSignature, + val typeParameters: List = emptyList(), ) : EtsRefType { override val typeName: String - get() = name - - override fun toString(): String { - return name + (constraint?.let { " extends $it" } ?: "") + (defaultType?.let { " = $it" } ?: "") - } - - override fun accept(visitor: EtsType.Visitor): R { - return visitor.visit(this) - } -} - -data class EtsAliasType( - val name: String, - val originalType: EtsType, - val signature: EtsLocalSignature, -) : EtsType { - override val typeName: String - get() = name + get() = if (typeParameters.isNotEmpty()) { + val generics = typeParameters.joinToString { it.typeName } + "${signature.name}<$generics>" + } else { + signature.name + } - override fun toString(): String { - return "$name = $originalType" - } + override fun toString(): String = typeName override fun accept(visitor: EtsType.Visitor): R { return visitor.visit(this) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/TypeName.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/TypeName.kt new file mode 100644 index 000000000..502eb6f00 --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/TypeName.kt @@ -0,0 +1,27 @@ +/* + * 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.model + +import org.jacodb.api.common.CommonTypeName + +interface TypeName : CommonTypeName + +data class TypeNameImpl( + override val typeName: String, +) : TypeName { + override fun toString(): String = typeName +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsValue.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Value.kt similarity index 98% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsValue.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Value.kt index ae4212fdb..aebd755fb 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsValue.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Value.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonValue diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsBlockCfg.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt similarity index 51% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsBlockCfg.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt index 2a675aeeb..4c7586601 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsBlockCfg.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt @@ -14,37 +14,8 @@ * limitations under the License. */ -package org.jacodb.ets.graph - -import org.jacodb.ets.base.EtsAddExpr -import org.jacodb.ets.base.EtsAndExpr -import org.jacodb.ets.base.EtsAssignStmt -import org.jacodb.ets.base.EtsClassType -import org.jacodb.ets.base.EtsDivExpr -import org.jacodb.ets.base.EtsEntity -import org.jacodb.ets.base.EtsEqExpr -import org.jacodb.ets.base.EtsGtEqExpr -import org.jacodb.ets.base.EtsGtExpr -import org.jacodb.ets.base.EtsIfStmt -import org.jacodb.ets.base.EtsInstLocation -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.EtsNopStmt -import org.jacodb.ets.base.EtsNotEqExpr -import org.jacodb.ets.base.EtsNotExpr -import org.jacodb.ets.base.EtsNumberConstant -import org.jacodb.ets.base.EtsOrExpr -import org.jacodb.ets.base.EtsParameterRef -import org.jacodb.ets.base.EtsReturnStmt -import org.jacodb.ets.base.EtsStmt -import org.jacodb.ets.base.EtsSubExpr -import org.jacodb.ets.base.EtsThis -import org.jacodb.ets.base.EtsType -import org.jacodb.ets.base.EtsUnknownType -import org.jacodb.ets.base.EtsValue +package org.jacodb.ets.utils + import org.jacodb.ets.dsl.BinaryExpr import org.jacodb.ets.dsl.BinaryOperator import org.jacodb.ets.dsl.Block @@ -67,24 +38,39 @@ import org.jacodb.ets.dsl.local import org.jacodb.ets.dsl.param import org.jacodb.ets.dsl.program import org.jacodb.ets.dsl.toBlockCfg +import org.jacodb.ets.model.BasicBlock +import org.jacodb.ets.model.EtsAddExpr +import org.jacodb.ets.model.EtsAndExpr +import org.jacodb.ets.model.EtsAssignStmt +import org.jacodb.ets.model.EtsBlockCfg import org.jacodb.ets.model.EtsClassSignature +import org.jacodb.ets.model.EtsDivExpr +import org.jacodb.ets.model.EtsEntity +import org.jacodb.ets.model.EtsEqExpr +import org.jacodb.ets.model.EtsGtEqExpr +import org.jacodb.ets.model.EtsGtExpr +import org.jacodb.ets.model.EtsIfStmt +import org.jacodb.ets.model.EtsLocal +import org.jacodb.ets.model.EtsLtEqExpr +import org.jacodb.ets.model.EtsLtExpr import org.jacodb.ets.model.EtsMethod import org.jacodb.ets.model.EtsMethodImpl import org.jacodb.ets.model.EtsMethodParameter import org.jacodb.ets.model.EtsMethodSignature -import org.jacodb.ets.utils.toDot -import org.jacodb.ets.utils.view -import java.util.IdentityHashMap - -data class EtsBasicBlock( - val id: Int, - val statements: List, -) - -class EtsBlockCfg( - val blocks: List, - val successors: Map>, // for 'if-stmt' block, successors are (true, false) branches -) +import org.jacodb.ets.model.EtsMulExpr +import org.jacodb.ets.model.EtsNegExpr +import org.jacodb.ets.model.EtsNopStmt +import org.jacodb.ets.model.EtsNotEqExpr +import org.jacodb.ets.model.EtsNotExpr +import org.jacodb.ets.model.EtsNumberConstant +import org.jacodb.ets.model.EtsOrExpr +import org.jacodb.ets.model.EtsParameterRef +import org.jacodb.ets.model.EtsReturnStmt +import org.jacodb.ets.model.EtsStmt +import org.jacodb.ets.model.EtsStmtLocation +import org.jacodb.ets.model.EtsSubExpr +import org.jacodb.ets.model.EtsThis +import org.jacodb.ets.model.EtsUnknownType fun BlockCfg.toEtsBlockCfg(method: EtsMethod): EtsBlockCfg { return EtsBlockCfgBuilder(method).build(this) @@ -101,21 +87,20 @@ class EtsBlockCfgBuilder( } private var freeTempLocal: Int = 0 - private fun newTempLocal(type: EtsType): EtsLocal { + private fun newTempLocal(): EtsLocal { return EtsLocal( name = "_tmp${freeTempLocal++}", - type = type, ) } - private fun Block.toEtsBasicBlock(): EtsBasicBlock { + private fun Block.toEtsBasicBlock(): BasicBlock { val etsStatements: MutableList = mutableListOf() - fun ensureSingleAddress(entity: EtsEntity): EtsValue { - if (entity is EtsValue) { + fun ensureLocal(entity: EtsEntity): EtsLocal { + if (entity is EtsLocal) { return entity } - val newLocal = newTempLocal(entity.type) + val newLocal = newTempLocal() etsStatements += EtsAssignStmt( location = stub, lhv = newLocal, @@ -141,7 +126,7 @@ class EtsBlockCfgBuilder( } is BlockReturn -> { - val returnValue = ensureSingleAddress(stmt.expr.toEtsEntity()) + val returnValue = ensureLocal(stmt.expr.toEtsEntity()) etsStatements += EtsReturnStmt( location = stub, returnValue = returnValue, @@ -149,7 +134,7 @@ class EtsBlockCfgBuilder( } is BlockIf -> { - val condition = stmt.condition.toEtsEntity() + val condition = ensureLocal(stmt.condition.toEtsEntity()) etsStatements += EtsIfStmt( location = stub, condition = condition, @@ -162,13 +147,14 @@ class EtsBlockCfgBuilder( etsStatements += EtsNopStmt(location = stub) } - return EtsBasicBlock( + return BasicBlock( id = id, statements = etsStatements, ) } - private val stub = EtsInstLocation(method, -1) + private val stub + get() = EtsStmtLocation.stub(method) private fun Expr.toEtsEntity(): EtsEntity = when (this) { is Local -> EtsLocal( @@ -178,10 +164,9 @@ class EtsBlockCfgBuilder( is Parameter -> EtsParameterRef( index = index, - type = method.parameters[index].type, ) - ThisRef -> EtsThis(type = EtsClassType(EtsClassSignature.DEFAULT)) + ThisRef -> EtsThis is Constant -> EtsNumberConstant(value = value) @@ -191,7 +176,7 @@ class EtsBlockCfgBuilder( } UnaryOperator.NEG -> { - EtsNegExpr(arg = expr.toEtsEntity(), type = EtsUnknownType) + EtsNegExpr(arg = expr.toEtsEntity()) } } @@ -199,13 +184,11 @@ class EtsBlockCfgBuilder( BinaryOperator.AND -> EtsAndExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), - type = EtsUnknownType, ) BinaryOperator.OR -> EtsOrExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), - type = EtsUnknownType, ) BinaryOperator.EQ -> EtsEqExpr( @@ -241,147 +224,30 @@ class EtsBlockCfgBuilder( BinaryOperator.ADD -> EtsAddExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), - type = EtsUnknownType, ) BinaryOperator.SUB -> EtsSubExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), - type = EtsUnknownType, ) BinaryOperator.MUL -> EtsMulExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), - type = EtsUnknownType, ) BinaryOperator.DIV -> EtsDivExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), - type = EtsUnknownType, ) } } } -fun EtsBlockCfg.linearize(): EtsCfg { - val linearized: MutableList = mutableListOf() - val successorMap: MutableMap> = hashMapOf() - val stmtMap: MutableMap = IdentityHashMap() // original -> linearized (with location) - - val queue = ArrayDeque() - val visited: MutableSet = hashSetOf() - - if (blocks.isNotEmpty()) { - queue.add(blocks.first()) - } - - while (queue.isNotEmpty()) { - val block = queue.removeFirst() - if (!visited.add(block)) continue - - for (stmt in block.statements) { - val newStmt = when (stmt) { - is EtsNopStmt -> stmt.copy(location = stmt.location.copy(index = linearized.size)) - is EtsAssignStmt -> stmt.copy(location = stmt.location.copy(index = linearized.size)) - is EtsReturnStmt -> stmt.copy(location = stmt.location.copy(index = linearized.size)) - is EtsIfStmt -> stmt.copy(location = stmt.location.copy(index = linearized.size)) - else -> error("Unsupported statement type: $stmt") - } - stmtMap[stmt] = newStmt - linearized += newStmt - } - - val successors = successors[block.id] ?: error("No successors for block ${block.id}") - for (succId in successors.asReversed()) { - val succ = blocks[succId] - queue.addFirst(succ) - } - } - - for (block in blocks) { - check(block.statements.isNotEmpty()) { - "Block ${block.id} is empty" - } - - for ((stmt, next) in block.statements.zipWithNext()) { - successorMap[stmtMap.getValue(stmt)] = listOf(stmtMap.getValue(next)) - } - - val successors = successors[block.id] ?: error("No successors for block ${block.id}") - val last = stmtMap.getValue(block.statements.last()) - // Note: reverse the order of successors, because in all CFGs, except EtsCfg, - // the successors for the if-stmt are (true, false) branches, - // and only the EtsCfg (which we are building here) has the (false, true) order. - successorMap[last] = successors.asReversed().map { - stmtMap.getValue(blocks[it].statements.first()) - } - } - - return EtsCfg(linearized, successorMap) -} - -private fun EtsStmt.toDotLabel(): String = when (this) { - is EtsNopStmt -> "nop" - is EtsAssignStmt -> "$lhv := $rhv" - is EtsReturnStmt -> "return $returnValue" - is EtsIfStmt -> "if ($condition)" - else -> this.toString() -} - -private fun String.htmlEncode(): String = this - .replace("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace("\"", """) - -fun EtsBlockCfg.toDot(useHtml: Boolean = true): String { - val lines = mutableListOf() - lines += "digraph cfg {" - lines += " node [shape=${if (useHtml) "none" else "rect"} fontname=\"monospace\"]" - - // Nodes - blocks.forEach { block -> - if (useHtml) { - val s = block.statements.joinToString("") { - it.toDotLabel().htmlEncode() + "
" - } - val h = "" + - "" + - "" + - "
" + "Block #${block.id}" + "
" + s + "
" - lines += " ${block.id} [label=<${h}>]" - } else { - val s = block.statements.joinToString("") { it.toDotLabel() + "\\l" } - lines += " ${block.id} [label=\"Block #${block.id}\\n$s\"]" - } - } - - // Edges - blocks.forEach { block -> - val succs = successors[block.id] - if (succs != null) { - if (succs.isEmpty()) return@forEach - if (succs.size == 1) { - lines += " ${block.id} -> ${succs.single()}" - } else { - check(succs.size == 2) - val (trueBranch, falseBranch) = succs - lines += " ${block.id} -> $trueBranch [label=\"true\"]" - lines += " ${block.id} -> $falseBranch [label=\"false\"]" - } - } - } - - lines += "}" - return lines.joinToString("\n") -} - private fun main() { val method = EtsMethodImpl( EtsMethodSignature( - enclosingClass = EtsClassSignature.DEFAULT, + enclosingClass = EtsClassSignature.Companion.UNKNOWN, name = "foo", parameters = listOf( EtsMethodParameter( diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgToDot.kt new file mode 100644 index 000000000..49f61afef --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgToDot.kt @@ -0,0 +1,67 @@ +/* + * 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.model.EtsBlockCfg + +private fun String.htmlEncode(): String = this + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + +fun EtsBlockCfg.toDot(useHtml: Boolean = true): String { + val lines = mutableListOf() + lines += "digraph cfg {" + lines += " node [shape=${if (useHtml) "none" else "rect"} fontname=\"monospace\"]" + + // Nodes + for (block in blocks) { + if (useHtml) { + val s = block.statements.joinToString("") { + it.toDotLabel().htmlEncode() + "
" + } + val h = "" + + "" + + "" + + "
" + "Block #${block.id}" + "
" + s + "
" + lines += " ${block.id} [label=<${h}>]" + } else { + val s = block.statements.joinToString("") { it.toDotLabel() + "\\l" } + lines += " ${block.id} [label=\"Block #${block.id}\\n$s\"]" + } + } + + // Edges + for (block in blocks) { + val succs = successors[block.id] + if (succs != null) { + if (succs.isEmpty()) continue + if (succs.size == 1) { + lines += " ${block.id} -> ${succs.single()}" + } else { + check(succs.size == 2) + val (trueBranch, falseBranch) = succs + lines += " ${block.id} -> $trueBranch [label=\"true\"]" + lines += " ${block.id} -> $falseBranch [label=\"false\"]" + } + } + } + + lines += "}" + return lines.joinToString("\n") +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/CallExpr.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/CallExpr.kt index 6bfb198ba..09df5d434 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/CallExpr.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/CallExpr.kt @@ -16,8 +16,8 @@ package org.jacodb.ets.utils -import org.jacodb.ets.base.EtsCallExpr -import org.jacodb.ets.base.EtsStmt +import org.jacodb.ets.model.EtsCallExpr +import org.jacodb.ets.model.EtsStmt val EtsStmt.callExpr: EtsCallExpr? get() = getOperands().filterIsInstance().firstOrNull() 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 index 025651289..255447325 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Collector.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Collector.kt @@ -18,8 +18,8 @@ package org.jacodb.ets.utils -import org.jacodb.ets.base.EtsEntity -import org.jacodb.ets.base.EtsStmt +import org.jacodb.ets.model.EtsEntity +import org.jacodb.ets.model.EtsStmt class EntityCollector>( val result: C, diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/Constants.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Constants.kt similarity index 97% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/base/Constants.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Constants.kt index 1c6164190..1d1b56821 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/base/Constants.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Constants.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.ets.base +package org.jacodb.ets.utils const val CONSTRUCTOR_NAME = "constructor" diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Constructor.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Constructor.kt new file mode 100644 index 000000000..c8f819893 --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Constructor.kt @@ -0,0 +1,36 @@ +/* + * 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.model.EtsClassSignature +import org.jacodb.ets.model.EtsClassType +import org.jacodb.ets.model.EtsMethod +import org.jacodb.ets.model.EtsMethodImpl +import org.jacodb.ets.model.EtsMethodSignature + +fun createConstructor( + cls: EtsClassSignature, +): EtsMethod { + return EtsMethodImpl( + signature = EtsMethodSignature( + enclosingClass = cls, + name = CONSTRUCTOR_NAME, + parameters = emptyList(), + returnType = EtsClassType(cls), + ) + ) +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt index 8c6253132..f4f0a441a 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt @@ -23,7 +23,6 @@ import org.jacodb.ets.dto.IfStmtDto import org.jacodb.ets.dto.MethodDto import org.jacodb.ets.dto.NopStmtDto import org.jacodb.ets.dto.StmtDto -import org.jacodb.ets.dto.SwitchStmtDto import java.io.File import java.nio.file.Path import kotlin.io.path.createDirectories @@ -171,15 +170,6 @@ fun EtsFileDto.toDot(useLR: Boolean = false): String { } } - is SwitchStmtDto -> { - for ((j, succ) in bb.successors.withIndex()) { - val b = blockId(clazz, method, bb.id) - val bs = blockId(clazz, method, succ) - val label = if (j == 0) "default" else "case ${j - 1}" - lines += """ "${b}.${i}" -> "${bs}.0" [lhead="$b", label="$label"];""" - } - } - else -> { for (succ in bb.successors) { val b = blockId(clazz, method, bb.id) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToText.kt similarity index 55% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToText.kt index 5ecd19bcd..61d293995 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToText.kt @@ -17,7 +17,6 @@ package org.jacodb.ets.utils import org.jacodb.ets.dto.EtsFileDto -import org.jacodb.ets.model.EtsFile fun EtsFileDto.toText(): String { val lines: MutableList = mutableListOf() @@ -59,45 +58,3 @@ fun EtsFileDto.toText(): String { } return lines.joinToString("\n") } - -fun EtsFile.toText(): String { - val lines: MutableList = mutableListOf() - lines += "EtsFile '${signature}':" - classes.forEach { clazz -> - lines += "= CLASS '${clazz.signature}':" - lines += " typeParameters = ${clazz.typeParameters}" - lines += " modifiers = ${clazz.modifiers}" - lines += " decorators = ${clazz.decorators}" - lines += " superClass = '${clazz.superClass}'" - lines += " fields: ${clazz.fields.size}" - clazz.fields.forEach { field -> - lines += " - FIELD '${field.signature}'" - } - lines += " constructor = '${clazz.ctor.signature}'" - lines += " typeParameters = ${clazz.ctor.typeParameters}" - lines += " modifiers = ${clazz.ctor.modifiers}" - lines += " decorators = ${clazz.ctor.decorators}" - lines += " locals: ${clazz.ctor.locals.size}" - lines += " stmts: ${clazz.ctor.cfg.stmts.size}" - clazz.ctor.cfg.stmts.forEach { stmt -> - lines += " ${stmt.location.index}. $stmt" - val pad = " ".repeat("${stmt.location.index}".length + 2) // number + dot + space - lines += " ${pad}successors = ${clazz.ctor.cfg.successors(stmt).map { it.location.index }}" - } - lines += " methods: ${clazz.methods.size}" - clazz.methods.forEach { method -> - lines += " - METHOD '${method.signature}':" - lines += " typeParameters = ${method.typeParameters}" - lines += " modifiers = ${method.modifiers}" - lines += " decorators = ${method.decorators}" - lines += " locals: ${method.locals.size}" - lines += " stmts: ${method.cfg.stmts.size}" - method.cfg.stmts.forEach { stmt -> - lines += " ${stmt.location.index}. $stmt" - val pad = " ".repeat("${stmt.location.index}".length + 2) // number + dot + space - lines += " ${pad}successors = ${method.cfg.successors(stmt).map { it.location.index }}" - } - } - } - return lines.joinToString("\n") -} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt index f1f2b0520..30f89c5b3 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt @@ -16,10 +16,10 @@ package org.jacodb.ets.utils -import org.jacodb.ets.base.EtsStmt import org.jacodb.ets.model.EtsClass import org.jacodb.ets.model.EtsFile import org.jacodb.ets.model.EtsMethod +import org.jacodb.ets.model.EtsStmt import java.io.File import java.nio.file.Path import kotlin.io.path.createDirectories @@ -55,13 +55,11 @@ fun EtsFile.toDot(useLR: Boolean = false): String { val name = method.signature.name val params = method.signature.parameters.joinToString() val returnType = method.signature.returnType - // TODO: uncomment generics when `EtsMethod` have `typeParameters` property - // val generics = if (method.typeParameters.isNotEmpty()) { - // "<${method.typeParameters.joinToString()}>" - // } else { - // "" - // } - val generics = "" + val generics = if (method.typeParameters.isNotEmpty()) { + "<${method.typeParameters.joinToString()}>" + } else { + "" + } labelLines += " $name$generics($params): $returnType" } return labelLines.joinToString("") { "$it\\l" } @@ -91,7 +89,7 @@ fun EtsFile.toDot(useLR: Boolean = false): String { lines += """ "$c" [shape=box,label="$clabel"]""" // Methods inside class: - (clazz.methods + clazz.ctor).forEach { method -> + clazz.methods.forEach { method -> // METHOD val m = methodId(clazz, method) val mlabel = methodLabel(clazz, method) @@ -100,9 +98,9 @@ fun EtsFile.toDot(useLR: Boolean = false): String { // Link class to method: lines += """ "$c" -> "$m" [dir=none];""" - // Link method to the first statement: - method.cfg.stmts.firstOrNull()?.let { first -> - val f = stmtId(clazz, method, first) + // Link method to the entry point: + method.cfg.entries.forEach { entry -> + val f = stmtId(clazz, method, entry) lines += """ "$m" -> "$f";""" } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToText.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToText.kt new file mode 100644 index 000000000..f15b4714a --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToText.kt @@ -0,0 +1,49 @@ +/* + * 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.model.EtsFile + +fun EtsFile.toText(): String { + val lines: MutableList = mutableListOf() + lines += "EtsFile '${signature}':" + classes.forEach { clazz -> + lines += "= CLASS '${clazz.signature}':" + lines += " typeParameters = ${clazz.typeParameters}" + lines += " modifiers = ${clazz.modifiers}" + lines += " decorators = ${clazz.decorators}" + lines += " superClass = '${clazz.superClass}'" + lines += " fields: ${clazz.fields.size}" + clazz.fields.forEach { field -> + lines += " - FIELD '${field.signature}'" + } + lines += " methods: ${clazz.methods.size}" + clazz.methods.forEach { method -> + lines += " - METHOD '${method.signature}':" + lines += " typeParameters = ${method.typeParameters}" + lines += " modifiers = ${method.modifiers}" + lines += " decorators = ${method.decorators}" + lines += " stmts: ${method.cfg.stmts.size}" + method.cfg.stmts.forEach { stmt -> + lines += " ${stmt.location.index}. $stmt" + val pad = " ".repeat("${stmt.location.index}".length + 2) // number + dot + space + lines += " ${pad}successors = ${method.cfg.successors(stmt).map { it.location.index }}" + } + } + } + return lines.joinToString("\n") +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetLocals.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetLocals.kt index 584c8d3f5..cbe6a70da 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetLocals.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetLocals.kt @@ -16,11 +16,11 @@ 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.EtsAssignStmt +import org.jacodb.ets.model.EtsEntity +import org.jacodb.ets.model.EtsLocal import org.jacodb.ets.model.EtsMethod +import org.jacodb.ets.model.EtsStmt fun EtsMethod.getDeclaredLocals(): Set = cfg.stmts.mapNotNullTo(mutableSetOf()) { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetOperands.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetOperands.kt index 971d9debb..b07ba8c52 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetOperands.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetOperands.kt @@ -16,76 +16,71 @@ 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.EtsRawEntity -import org.jacodb.ets.base.EtsRawStmt -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 +import org.jacodb.ets.model.EtsAddExpr +import org.jacodb.ets.model.EtsAndExpr +import org.jacodb.ets.model.EtsArrayAccess +import org.jacodb.ets.model.EtsAssignStmt +import org.jacodb.ets.model.EtsAwaitExpr +import org.jacodb.ets.model.EtsBitAndExpr +import org.jacodb.ets.model.EtsBitNotExpr +import org.jacodb.ets.model.EtsBitOrExpr +import org.jacodb.ets.model.EtsBitXorExpr +import org.jacodb.ets.model.EtsBooleanConstant +import org.jacodb.ets.model.EtsCallStmt +import org.jacodb.ets.model.EtsCastExpr +import org.jacodb.ets.model.EtsDeleteExpr +import org.jacodb.ets.model.EtsDivExpr +import org.jacodb.ets.model.EtsEntity +import org.jacodb.ets.model.EtsEqExpr +import org.jacodb.ets.model.EtsExpExpr +import org.jacodb.ets.model.EtsGtEqExpr +import org.jacodb.ets.model.EtsGtExpr +import org.jacodb.ets.model.EtsIfStmt +import org.jacodb.ets.model.EtsInExpr +import org.jacodb.ets.model.EtsInstanceCallExpr +import org.jacodb.ets.model.EtsInstanceFieldRef +import org.jacodb.ets.model.EtsInstanceOfExpr +import org.jacodb.ets.model.EtsLeftShiftExpr +import org.jacodb.ets.model.EtsLocal +import org.jacodb.ets.model.EtsLtEqExpr +import org.jacodb.ets.model.EtsLtExpr +import org.jacodb.ets.model.EtsMulExpr +import org.jacodb.ets.model.EtsNegExpr +import org.jacodb.ets.model.EtsNewArrayExpr +import org.jacodb.ets.model.EtsNewExpr +import org.jacodb.ets.model.EtsNopStmt +import org.jacodb.ets.model.EtsNotEqExpr +import org.jacodb.ets.model.EtsNotExpr +import org.jacodb.ets.model.EtsNullConstant +import org.jacodb.ets.model.EtsNullishCoalescingExpr +import org.jacodb.ets.model.EtsNumberConstant +import org.jacodb.ets.model.EtsOrExpr +import org.jacodb.ets.model.EtsParameterRef +import org.jacodb.ets.model.EtsPostDecExpr +import org.jacodb.ets.model.EtsPostIncExpr +import org.jacodb.ets.model.EtsPreDecExpr +import org.jacodb.ets.model.EtsPreIncExpr +import org.jacodb.ets.model.EtsPtrCallExpr +import org.jacodb.ets.model.EtsRawEntity +import org.jacodb.ets.model.EtsRawStmt +import org.jacodb.ets.model.EtsRemExpr +import org.jacodb.ets.model.EtsReturnStmt +import org.jacodb.ets.model.EtsRightShiftExpr +import org.jacodb.ets.model.EtsStaticCallExpr +import org.jacodb.ets.model.EtsStaticFieldRef +import org.jacodb.ets.model.EtsStmt +import org.jacodb.ets.model.EtsStrictEqExpr +import org.jacodb.ets.model.EtsStrictNotEqExpr +import org.jacodb.ets.model.EtsStringConstant +import org.jacodb.ets.model.EtsSubExpr +import org.jacodb.ets.model.EtsThis +import org.jacodb.ets.model.EtsThrowStmt +import org.jacodb.ets.model.EtsTypeOfExpr +import org.jacodb.ets.model.EtsUnaryPlusExpr +import org.jacodb.ets.model.EtsUndefinedConstant +import org.jacodb.ets.model.EtsUnsignedRightShiftExpr +import org.jacodb.ets.model.EtsVoidExpr +import org.jacodb.ets.model.EtsYieldExpr fun EtsStmt.getOperands(): Sequence { return accept(StmtGetOperands) @@ -110,17 +105,11 @@ private object StmtGetOperands : EtsStmt.Visitor> { listOfNotNull(stmt.returnValue).asSequence() override fun visit(stmt: EtsThrowStmt): Sequence = - sequenceOf(stmt.arg) - - override fun visit(stmt: EtsGotoStmt): Sequence = - emptySequence() + sequenceOf(stmt.exception) override fun visit(stmt: EtsIfStmt): Sequence = sequenceOf(stmt.condition) - override fun visit(stmt: EtsSwitchStmt): Sequence = - sequenceOf(stmt.arg) + stmt.cases.asSequence() - override fun visit(stmt: EtsRawStmt): Sequence = emptySequence() } @@ -166,9 +155,6 @@ private object EntityGetOperands : EtsEntity.Visitor> { override fun visit(expr: EtsNewArrayExpr): Sequence = sequenceOf(expr.size) - override fun visit(expr: EtsLengthExpr): Sequence = - sequenceOf(expr.arg) - override fun visit(expr: EtsCastExpr): Sequence = sequenceOf(expr.arg) @@ -295,12 +281,6 @@ private object EntityGetOperands : EtsEntity.Visitor> { override fun visit(expr: EtsPtrCallExpr): Sequence = sequenceOf(expr.ptr) + expr.args.asSequence() - override fun visit(expr: EtsCommaExpr): Sequence = - sequenceOf(expr.left, expr.right) - - override fun visit(expr: EtsTernaryExpr): Sequence = - sequenceOf(expr.condition, expr.thenExpr, expr.elseExpr) - override fun visit(value: EtsRawEntity): Sequence = emptySequence() } 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 646e712a3..f9e4d2558 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 @@ -16,9 +16,9 @@ package org.jacodb.ets.utils -import org.jacodb.ets.base.EtsEntity -import org.jacodb.ets.base.EtsStmt -import org.jacodb.ets.base.EtsValue +import org.jacodb.ets.model.EtsEntity +import org.jacodb.ets.model.EtsStmt +import org.jacodb.ets.model.EtsValue fun EtsStmt.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 index a84d42b05..a42d01b46 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt @@ -16,85 +16,86 @@ 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 { +import org.jacodb.ets.model.EtsAddExpr +import org.jacodb.ets.model.EtsAndExpr +import org.jacodb.ets.model.EtsArrayAccess +import org.jacodb.ets.model.EtsAssignStmt +import org.jacodb.ets.model.EtsAwaitExpr +import org.jacodb.ets.model.EtsBitAndExpr +import org.jacodb.ets.model.EtsBitNotExpr +import org.jacodb.ets.model.EtsBitOrExpr +import org.jacodb.ets.model.EtsBitXorExpr +import org.jacodb.ets.model.EtsBooleanConstant +import org.jacodb.ets.model.EtsCallStmt +import org.jacodb.ets.model.EtsCastExpr +import org.jacodb.ets.model.EtsDeleteExpr +import org.jacodb.ets.model.EtsDivExpr +import org.jacodb.ets.model.EtsEntity +import org.jacodb.ets.model.EtsEqExpr +import org.jacodb.ets.model.EtsExpExpr +import org.jacodb.ets.model.EtsGtEqExpr +import org.jacodb.ets.model.EtsGtExpr +import org.jacodb.ets.model.EtsIfStmt +import org.jacodb.ets.model.EtsInExpr +import org.jacodb.ets.model.EtsInstanceCallExpr +import org.jacodb.ets.model.EtsInstanceFieldRef +import org.jacodb.ets.model.EtsInstanceOfExpr +import org.jacodb.ets.model.EtsLeftShiftExpr +import org.jacodb.ets.model.EtsLocal +import org.jacodb.ets.model.EtsLtEqExpr +import org.jacodb.ets.model.EtsLtExpr +import org.jacodb.ets.model.EtsMulExpr +import org.jacodb.ets.model.EtsNegExpr +import org.jacodb.ets.model.EtsNewArrayExpr +import org.jacodb.ets.model.EtsNewExpr +import org.jacodb.ets.model.EtsNopStmt +import org.jacodb.ets.model.EtsNotEqExpr +import org.jacodb.ets.model.EtsNotExpr +import org.jacodb.ets.model.EtsNullConstant +import org.jacodb.ets.model.EtsNullishCoalescingExpr +import org.jacodb.ets.model.EtsNumberConstant +import org.jacodb.ets.model.EtsOrExpr +import org.jacodb.ets.model.EtsParameterRef +import org.jacodb.ets.model.EtsPostDecExpr +import org.jacodb.ets.model.EtsPostIncExpr +import org.jacodb.ets.model.EtsPreDecExpr +import org.jacodb.ets.model.EtsPreIncExpr +import org.jacodb.ets.model.EtsPtrCallExpr +import org.jacodb.ets.model.EtsRawEntity +import org.jacodb.ets.model.EtsRawStmt +import org.jacodb.ets.model.EtsRemExpr +import org.jacodb.ets.model.EtsReturnStmt +import org.jacodb.ets.model.EtsRightShiftExpr +import org.jacodb.ets.model.EtsStaticCallExpr +import org.jacodb.ets.model.EtsStaticFieldRef +import org.jacodb.ets.model.EtsStmt +import org.jacodb.ets.model.EtsStrictEqExpr +import org.jacodb.ets.model.EtsStrictNotEqExpr +import org.jacodb.ets.model.EtsStringConstant +import org.jacodb.ets.model.EtsSubExpr +import org.jacodb.ets.model.EtsThis +import org.jacodb.ets.model.EtsThrowStmt +import org.jacodb.ets.model.EtsTypeOfExpr +import org.jacodb.ets.model.EtsUnaryPlusExpr +import org.jacodb.ets.model.EtsUndefinedConstant +import org.jacodb.ets.model.EtsUnsignedRightShiftExpr +import org.jacodb.ets.model.EtsVoidExpr +import org.jacodb.ets.model.EtsYieldExpr + +abstract class AbstractHandler : EtsEntity.Visitor, EtsStmt.Visitor { abstract fun handle(value: EtsEntity) abstract fun handle(stmt: EtsStmt) - final override fun defaultVisit(value: EtsEntity) { - handle(value) - } + // final override fun defaultVisit(value: EtsEntity) { + // handle(value) + // } + + // final override fun defaultVisit(stmt: EtsStmt) { + // handle(stmt) + // } - final override fun defaultVisit(stmt: EtsStmt) { + final override fun visit(stmt: EtsRawStmt) { handle(stmt) } @@ -120,11 +121,7 @@ abstract class AbstractHandler : EtsEntity.Visitor.Default, EtsStmt.Visito final override fun visit(stmt: EtsThrowStmt) { handle(stmt) - stmt.arg.accept(this) - } - - final override fun visit(stmt: EtsGotoStmt) { - handle(stmt) + stmt.exception.accept(this) } final override fun visit(stmt: EtsIfStmt) { @@ -132,8 +129,8 @@ abstract class AbstractHandler : EtsEntity.Visitor.Default, EtsStmt.Visito stmt.condition.accept(this) } - final override fun visit(stmt: EtsSwitchStmt) { - error("deprecated") + final override fun visit(value: EtsRawEntity) { + handle(value) } final override fun visit(value: EtsLocal) { @@ -192,11 +189,6 @@ abstract class AbstractHandler : EtsEntity.Visitor.Default, EtsStmt.Visito 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) @@ -432,17 +424,4 @@ abstract class AbstractHandler : EtsEntity.Visitor.Default, EtsStmt.Visito 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/main/kotlin/org/jacodb/ets/utils/IdentityHashSet.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/IdentityHashSet.kt index 1688d4ca8..4e97c4206 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/IdentityHashSet.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/IdentityHashSet.kt @@ -18,8 +18,8 @@ package org.jacodb.ets.utils import java.util.IdentityHashMap -class IdentityHashSet ( - private val map: IdentityHashMap = IdentityHashMap() +class IdentityHashSet( + private val map: IdentityHashMap = IdentityHashMap(), ) : AbstractMutableSet() { override val size: Int diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsCfgExt.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LinearCfgExt.kt similarity index 95% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsCfgExt.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LinearCfgExt.kt index d04db630c..1feecae24 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsCfgExt.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LinearCfgExt.kt @@ -22,16 +22,16 @@ import info.leadinglight.jdot.Node import info.leadinglight.jdot.enums.Color import info.leadinglight.jdot.enums.Shape import info.leadinglight.jdot.impl.Util -import org.jacodb.ets.base.EtsIfStmt -import org.jacodb.ets.base.EtsStmt -import org.jacodb.ets.graph.EtsCfg +import org.jacodb.ets.model.EtsIfStmt +import org.jacodb.ets.model.EtsLinearCfg +import org.jacodb.ets.model.EtsStmt import java.io.File import java.nio.file.Files import java.nio.file.Path private const val DEFAULT_DOT_CMD = "dot" -fun EtsCfg.view( +fun EtsLinearCfg.view( viewerCmd: String = if (System.getProperty("os.name").startsWith("Windows")) "start" else "xdg-open", dotCmd: String = DEFAULT_DOT_CMD, viewCatchConnections: Boolean = true, @@ -40,7 +40,7 @@ fun EtsCfg.view( Util.sh(arrayOf(viewerCmd, "file://$path")) } -fun EtsCfg.toFile( +fun EtsLinearCfg.toFile( file: File? = null, dotCmd: String = DEFAULT_DOT_CMD, viewCatchConnections: Boolean = true, diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsCfgToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LinearCfgToDot.kt similarity index 71% rename from jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsCfgToDot.kt rename to jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LinearCfgToDot.kt index 208b74d04..8851ffdf4 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsCfgToDot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LinearCfgToDot.kt @@ -16,43 +16,30 @@ package org.jacodb.ets.utils -import org.jacodb.ets.base.EtsAssignStmt -import org.jacodb.ets.base.EtsIfStmt -import org.jacodb.ets.base.EtsNopStmt -import org.jacodb.ets.base.EtsReturnStmt -import org.jacodb.ets.base.EtsStmt -import org.jacodb.ets.graph.EtsCfg +import org.jacodb.ets.model.EtsIfStmt +import org.jacodb.ets.model.EtsLinearCfg -private fun EtsStmt.toDotLabel(): String = when (this) { - is EtsNopStmt -> "nop" - is EtsAssignStmt -> "$lhv := $rhv" - is EtsReturnStmt -> returnValue?.let { "return $it" } ?: "return" - is EtsIfStmt -> "if ($condition)" - else -> this.toString() // TODO: support more statement types -} - -fun EtsCfg.toDot(): String { +fun EtsLinearCfg.toDot(): String { val lines = mutableListOf() lines += "digraph cfg {" lines += " node [shape=rect fontname=\"monospace\"]" // Nodes - stmts.forEach { stmt -> + for (stmt in stmts) { val id = stmt.location.index val label = stmt.toDotLabel().replace("\"", "\\\"") lines += " $id [label=\"$id: $label\"]" } // Edges - stmts.forEach { stmt -> + for (stmt in stmts) { when (stmt) { is EtsIfStmt -> { val succs = successors(stmt) check(succs.size == 2) { "Expected two successors for $stmt, but it has ${succs.size}: $succs" } - // val (thenBranch, elseBranch) = succs.toList() - val (thenBranch, elseBranch) = succs.toList().reversed() // TODO: check order of successors + val (thenBranch, elseBranch) = succs.toList() lines += " ${stmt.location.index} -> ${thenBranch.location.index} [label=\"then\"]" lines += " ${stmt.location.index} -> ${elseBranch.location.index} [label=\"else\"]" } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Linearize.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Linearize.kt new file mode 100644 index 000000000..5dbfead71 --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Linearize.kt @@ -0,0 +1,80 @@ +/* + * 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.model.BasicBlock +import org.jacodb.ets.model.EtsBlockCfg +import org.jacodb.ets.model.EtsLinearCfg +import org.jacodb.ets.model.EtsStmt + +fun EtsBlockCfg.linearize(): EtsLinearCfg { + val linearized: MutableList = mutableListOf() + + val queue: ArrayDeque = ArrayDeque() + val visited: MutableSet = hashSetOf() + + if (blocks.isNotEmpty()) { + queue.add(blocks.first()) + } + + while (queue.isNotEmpty()) { + val block = queue.removeFirst() + if (!visited.add(block.id)) continue + + for (stmt in block.statements) { + stmt.location.index = linearized.size + linearized += stmt + } + + val successors = successors[block.id] ?: error("No successors for block ${block.id}") + for (id in successors.asReversed()) { + val next = blocks[id] + queue.addFirst(next) // DFS + } + } + + val linearSuccessors = arrayOfNulls>(linearized.size) + + for (id in visited) { + val block = blocks[id] + + for ((stmt, next) in block.statements.zipWithNext()) { + linearSuccessors[stmt.location.index] = listOf(next.location.index) + } + + check(block.statements.isNotEmpty()) { + "Block ${block.id} is empty" + } + + val last = block.statements.last() + val successors = successors[block.id] ?: error("No successors for block ${block.id}") + linearSuccessors[last.location.index] = successors.map { + blocks[it].statements.first().location.index + } + } + + for (s in linearSuccessors) { + checkNotNull(s) + } + @Suppress("UNCHECKED_CAST") + linearSuccessors as Array> + + return EtsLinearCfg( + stmts = linearized, + successors = linearSuccessors.asList() + ) +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/StmtToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/StmtToDot.kt new file mode 100644 index 000000000..85d8e1f56 --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/StmtToDot.kt @@ -0,0 +1,38 @@ +/* + * 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.model.EtsAssignStmt +import org.jacodb.ets.model.EtsCallStmt +import org.jacodb.ets.model.EtsIfStmt +import org.jacodb.ets.model.EtsNopStmt +import org.jacodb.ets.model.EtsRawStmt +import org.jacodb.ets.model.EtsReturnStmt +import org.jacodb.ets.model.EtsStmt + +internal fun EtsStmt.toDotLabel(): String { + val label = when (this) { + is EtsNopStmt -> "nop" + is EtsAssignStmt -> "$lhv := $rhv" + is EtsReturnStmt -> returnValue?.let { "return $it" } ?: "return" + is EtsIfStmt -> "if ($condition)" + is EtsCallStmt -> "call $expr" + is EtsRawStmt -> "raw $kind" + else -> error("Unsupported statement: $this") + } + return label.replace("\"", "\\\"") +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ViewDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ViewDot.kt index 76e79dac2..cd099d221 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ViewDot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/ViewDot.kt @@ -42,3 +42,7 @@ fun view( println("Opening rendered file '$outputFile'...") Runtime.getRuntime().exec("$viewerCmd $outputFile").waitFor() } + +fun T.view(dot: (T) -> String) { + view(dot(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 index 8ef83f512..8f05ef1df 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/CollectorTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/CollectorTest.kt @@ -16,20 +16,15 @@ 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 io.mockk.mockk +import org.jacodb.ets.model.EtsAddExpr +import org.jacodb.ets.model.EtsAssignStmt +import org.jacodb.ets.model.EtsEntity +import org.jacodb.ets.model.EtsLocal import org.jacodb.ets.model.EtsMethod -import org.jacodb.ets.model.EtsMethodSignature -import org.jacodb.ets.model.EtsModifiers +import org.jacodb.ets.model.EtsNumberConstant +import org.jacodb.ets.model.EtsStmt +import org.jacodb.ets.model.EtsStmtLocation import org.jacodb.ets.utils.AbstractHandler import org.jacodb.ets.utils.EntityCollector import kotlin.test.Test @@ -38,28 +33,13 @@ 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 method = mockk() + val a = EtsLocal("a") + val b = EtsLocal("b") val n = EtsNumberConstant(42.0) - val rhv = EtsAddExpr(EtsUnknownType, b, n) + val rhv = EtsAddExpr(b, n) + val loc = EtsStmtLocation.stub(method) val stmt = EtsAssignStmt(loc, a, rhv) - return stmt } diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsCfgDslTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsCfgDslTest.kt index d410c083e..dea711876 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsCfgDslTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsCfgDslTest.kt @@ -16,10 +16,6 @@ package org.jacodb.ets.test -import org.jacodb.ets.base.EtsAssignStmt -import org.jacodb.ets.base.EtsLocal -import org.jacodb.ets.base.EtsNumberType -import org.jacodb.ets.base.EtsUnknownType import org.jacodb.ets.dsl.add import org.jacodb.ets.dsl.const import org.jacodb.ets.dsl.local @@ -28,15 +24,16 @@ import org.jacodb.ets.dsl.param import org.jacodb.ets.dsl.program import org.jacodb.ets.dsl.toBlockCfg import org.jacodb.ets.dsl.toDot -import org.jacodb.ets.graph.linearize -import org.jacodb.ets.graph.toDot -import org.jacodb.ets.graph.toEtsBlockCfg import org.jacodb.ets.model.EtsClassSignature import org.jacodb.ets.model.EtsFileSignature import org.jacodb.ets.model.EtsMethodImpl import org.jacodb.ets.model.EtsMethodParameter import org.jacodb.ets.model.EtsMethodSignature +import org.jacodb.ets.model.EtsNumberType +import org.jacodb.ets.model.EtsUnknownType +import org.jacodb.ets.utils.linearize import org.jacodb.ets.utils.toDot +import org.jacodb.ets.utils.toEtsBlockCfg import org.junit.jupiter.api.Test class EtsCfgDslTest { @@ -60,7 +57,6 @@ class EtsCfgDslTest { val blockCfg = prog.toBlockCfg() println("blockCfg:\n${blockCfg.toDot()}") - val locals = mutableListOf() val method = EtsMethodImpl( signature = EtsMethodSignature( enclosingClass = EtsClassSignature( @@ -76,7 +72,6 @@ class EtsCfgDslTest { ), returnType = EtsNumberType, ), - locals = locals, ) val etsBlockCfg = blockCfg.toEtsBlockCfg(method) @@ -84,16 +79,6 @@ class EtsCfgDslTest { val etsCfg = etsBlockCfg.linearize() println("etsCfg:\n${etsCfg.toDot()}") - method._cfg = etsCfg - locals += etsCfg.stmts - .filterIsInstance() - .mapNotNull { - val left = it.lhv - if (left is EtsLocal) { - left - } else { - null - } - } + method._cfg = etsBlockCfg } } diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFileTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFileTest.kt index 455537b8b..045c44336 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFileTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFileTest.kt @@ -16,17 +16,17 @@ package org.jacodb.ets.test -import org.jacodb.ets.base.EtsAssignStmt -import org.jacodb.ets.base.EtsInstanceFieldRef -import org.jacodb.ets.base.EtsLocal -import org.jacodb.ets.base.EtsNumberConstant -import org.jacodb.ets.base.EtsReturnStmt -import org.jacodb.ets.base.EtsStaticFieldRef -import org.jacodb.ets.base.EtsThis -import org.jacodb.ets.base.INSTANCE_INIT_METHOD_NAME -import org.jacodb.ets.base.STATIC_INIT_METHOD_NAME +import org.jacodb.ets.model.EtsAssignStmt import org.jacodb.ets.model.EtsFile +import org.jacodb.ets.model.EtsInstanceFieldRef +import org.jacodb.ets.model.EtsLocal +import org.jacodb.ets.model.EtsNumberConstant +import org.jacodb.ets.model.EtsReturnStmt +import org.jacodb.ets.model.EtsStaticFieldRef +import org.jacodb.ets.model.EtsThis import org.jacodb.ets.test.utils.loadEtsFileFromResource +import org.jacodb.ets.utils.INSTANCE_INIT_METHOD_NAME +import org.jacodb.ets.utils.STATIC_INIT_METHOD_NAME import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs @@ -50,10 +50,10 @@ class EtsFileTest { etsFile.classes.forEach { cls -> cls.methods.forEach { method -> logger.info { - "Method '$method', locals: ${method.locals.size}, instructions: ${method.cfg.instructions.size}" + "Method '$method', instructions: ${method.cfg.stmts.size}" } - method.cfg.instructions.forEach { inst -> - logger.info { "${inst.location.index}. $inst" } + method.cfg.stmts.forEach { inst -> + logger.info { "${inst.location.index}: $inst" } } } } @@ -66,11 +66,11 @@ class EtsFileTest { cls.methods.forEach { method -> when (method.name) { "add" -> { - assertTrue( method.cfg.instructions.size > 2) + assertTrue(method.cfg.stmts.size > 2) } "main" -> { - assertTrue(method.cfg.instructions.size > 2) + assertTrue(method.cfg.stmts.size > 2) } } } @@ -86,11 +86,11 @@ class EtsFileTest { // instance initializer run { val method = cls.methods.single { it.name == INSTANCE_INIT_METHOD_NAME } - assertEquals(3, method.cfg.instructions.size) + assertEquals(3, method.cfg.stmts.size) // this := ThisRef run { - val stmt = method.cfg.instructions[0] + val stmt = method.cfg.stmts[0] assertIs(stmt) val lhv = stmt.lhv @@ -99,12 +99,12 @@ class EtsFileTest { val rhv = stmt.rhv assertIs(rhv) - assertEquals("Foo", rhv.type.typeName) + // assertEquals("Foo", rhv.type.typeName) } // this.x := 99 run { - val stmt = method.cfg.instructions[1] + val stmt = method.cfg.stmts[1] assertIs(stmt) val lhv = stmt.lhv @@ -124,7 +124,7 @@ class EtsFileTest { // return run { - val stmt = method.cfg.instructions[2] + val stmt = method.cfg.stmts[2] assertIs(stmt) assertEquals(null, stmt.returnValue) } @@ -133,11 +133,11 @@ class EtsFileTest { // static initializer run { val method = cls.methods.single { it.name == STATIC_INIT_METHOD_NAME } - assertEquals(3, method.cfg.instructions.size) + assertEquals(3, method.cfg.stmts.size) // this := ThisRef run { - val stmt = method.cfg.instructions[0] + val stmt = method.cfg.stmts[0] assertIs(stmt) val lhv = stmt.lhv @@ -146,12 +146,12 @@ class EtsFileTest { val rhv = stmt.rhv assertIs(rhv) - assertEquals("Foo", rhv.type.typeName) + // assertEquals("Foo", rhv.type.typeName) } // this.y := 111 run { - val stmt = method.cfg.instructions[1] + val stmt = method.cfg.stmts[1] assertIs(stmt) val lhv = stmt.lhv @@ -170,7 +170,7 @@ class EtsFileTest { // return run { - val stmt = method.cfg.instructions.last() + val stmt = method.cfg.stmts.last() assertIs(stmt) assertEquals(null, stmt.returnValue) } @@ -182,7 +182,7 @@ class EtsFileTest { // this := ThisRef run { - val stmt = method.cfg.instructions[0] + val stmt = method.cfg.stmts[0] assertIs(stmt) val lhv = stmt.lhv @@ -191,12 +191,12 @@ class EtsFileTest { val rhv = stmt.rhv assertIs(rhv) - assertEquals("Foo", rhv.type.typeName) + // assertEquals("Foo", rhv.type.typeName) } // Foo.y := 222 run { - val stmt = method.cfg.instructions[1] + val stmt = method.cfg.stmts[1] assertIs(stmt) val lhv = stmt.lhv @@ -215,7 +215,7 @@ class EtsFileTest { // return run { - val stmt = method.cfg.instructions.last() + val stmt = method.cfg.stmts.last() assertIs(stmt) assertEquals(null, stmt.returnValue) } @@ -227,7 +227,7 @@ class EtsFileTest { // this := ThisRef run { - val stmt = method.cfg.instructions[0] + val stmt = method.cfg.stmts[0] assertIs(stmt) val lhv = stmt.lhv @@ -236,12 +236,12 @@ class EtsFileTest { val rhv = stmt.rhv assertIs(rhv) - assertEquals("Foo", rhv.type.typeName) + // assertEquals("Foo", rhv.type.typeName) } // this.y := 333 run { - val stmt = method.cfg.instructions[1] + val stmt = method.cfg.stmts[1] assertIs(stmt) val lhv = stmt.lhv @@ -260,7 +260,7 @@ class EtsFileTest { // return run { - val stmt = method.cfg.instructions.last() + val stmt = method.cfg.stmts.last() assertIs(stmt) assertEquals(null, stmt.returnValue) } diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt index f4a228688..093d201ae 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt @@ -19,13 +19,6 @@ package org.jacodb.ets.test import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonPrimitive import mu.KotlinLogging -import org.jacodb.ets.base.DEFAULT_ARK_CLASS_NAME -import org.jacodb.ets.base.DEFAULT_ARK_METHOD_NAME -import org.jacodb.ets.base.EtsAnyType -import org.jacodb.ets.base.EtsInstLocation -import org.jacodb.ets.base.EtsLocal -import org.jacodb.ets.base.EtsReturnStmt -import org.jacodb.ets.base.EtsUnknownType import org.jacodb.ets.dto.AnyTypeDto import org.jacodb.ets.dto.ClassSignatureDto import org.jacodb.ets.dto.DecoratorDto @@ -47,17 +40,24 @@ import org.jacodb.ets.dto.ValueDto import org.jacodb.ets.dto.dtoModule import org.jacodb.ets.dto.toEtsLocal import org.jacodb.ets.dto.toEtsMethod +import org.jacodb.ets.model.EtsAnyType import org.jacodb.ets.model.EtsClassCategory import org.jacodb.ets.model.EtsClassSignature import org.jacodb.ets.model.EtsFile import org.jacodb.ets.model.EtsFileSignature +import org.jacodb.ets.model.EtsLocal import org.jacodb.ets.model.EtsMethodSignature +import org.jacodb.ets.model.EtsReturnStmt import org.jacodb.ets.model.EtsScene +import org.jacodb.ets.model.EtsStmtLocation +import org.jacodb.ets.model.EtsUnknownType import org.jacodb.ets.test.utils.getResourcePath import org.jacodb.ets.test.utils.getResourcePathOrNull import org.jacodb.ets.test.utils.loadEtsFileFromResource import org.jacodb.ets.test.utils.loadEtsProjectFromResources import org.jacodb.ets.test.utils.testFactory +import org.jacodb.ets.utils.DEFAULT_ARK_CLASS_NAME +import org.jacodb.ets.utils.DEFAULT_ARK_METHOD_NAME import org.jacodb.ets.utils.loadEtsFileAutoConvert import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.Test @@ -169,7 +169,7 @@ class EtsFromJsonTest { for (path in availableFiles) { test("load $path") { val file = loadEtsFileFromResource("$prefix/etsir/ast/$path.json") - printFile(file) + printFile(file, showStmts = true) } } } @@ -202,7 +202,7 @@ class EtsFromJsonTest { test("load $path") { val p = getResourcePath("$prefix/$path") val file = loadEtsFileAutoConvert(p) - printFile(file) + printFile(file, showStmts = true) } } } @@ -388,11 +388,10 @@ class EtsFromJsonTest { ), method.signature ) - assertEquals(0, method.locals.size) assertEquals(1, method.cfg.stmts.size) assertEquals( listOf( - EtsReturnStmt(EtsInstLocation(method, 0), null), + EtsReturnStmt(EtsStmtLocation(method, 0), null), ), method.cfg.stmts ) diff --git a/jacodb-ets/src/test/resources/.gitignore b/jacodb-ets/src/test/resources/.gitignore index 1fe1a8b3b..cc22931a1 100644 --- a/jacodb-ets/src/test/resources/.gitignore +++ b/jacodb-ets/src/test/resources/.gitignore @@ -1,4 +1,5 @@ /samples -!/samples/source +!/samples/ +!/samples/source/ /projects /repos From 9ac136fa87761edade8937a0a0244fff10436efb Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Wed, 16 Apr 2025 17:14:10 +0300 Subject: [PATCH 2/8] Restore types in the ETS code model --- .../main/kotlin/org/jacodb/ets/dto/Convert.kt | 71 ++++++++++++------- .../kotlin/org/jacodb/ets/model/Constant.kt | 17 +++++ .../kotlin/org/jacodb/ets/model/Entity.kt | 9 ++- .../main/kotlin/org/jacodb/ets/model/Expr.kt | 58 +++++++++++++-- .../main/kotlin/org/jacodb/ets/model/Local.kt | 3 +- .../main/kotlin/org/jacodb/ets/model/Ref.kt | 15 ++-- .../main/kotlin/org/jacodb/ets/model/Value.kt | 2 + .../org/jacodb/ets/utils/BlockCfgBuilder.kt | 26 ++++--- .../org/jacodb/ets/utils/GetOperands.kt | 4 ++ .../kotlin/org/jacodb/ets/utils/Handler.kt | 5 ++ .../org/jacodb/ets/test/CollectorTest.kt | 3 +- 11 files changed, 165 insertions(+), 48 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt index 2eb5923df..50e6beed4 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt @@ -278,10 +278,12 @@ class EtsMethodBuilder( is AwaitExprDto -> EtsAwaitExpr( arg = arg.toEtsEntity(), + type = type.toEtsType(), ) is YieldExprDto -> EtsYieldExpr( arg = arg.toEtsEntity(), + type = type.toEtsType(), ) is TypeOfExprDto -> EtsTypeOfExpr( @@ -300,14 +302,14 @@ class EtsMethodBuilder( is UnaryOperationDto -> { val arg = arg.toEtsEntity() - // Note: `type` is ignored here! + val type = type.toEtsType() when (op) { Ops.Unary.NOT -> EtsNotExpr(arg) - Ops.Unary.BIT_NOT -> EtsBitNotExpr(arg) - Ops.Unary.MINUS -> EtsNegExpr(arg) - Ops.Unary.PLUS -> EtsUnaryPlusExpr(arg) - Ops.Unary.INC -> EtsPreIncExpr(arg) - Ops.Unary.DEC -> EtsPreDecExpr(arg) + Ops.Unary.BIT_NOT -> EtsBitNotExpr(arg, type) + Ops.Unary.MINUS -> EtsNegExpr(arg, type) + Ops.Unary.PLUS -> EtsUnaryPlusExpr(arg, type) + Ops.Unary.INC -> EtsPreIncExpr(arg, type) + Ops.Unary.DEC -> EtsPreDecExpr(arg, type) else -> error("Unknown unop: '$op'") } } @@ -315,23 +317,23 @@ class EtsMethodBuilder( is BinaryOperationDto -> { val left = left.toEtsEntity() val right = right.toEtsEntity() - // val type = type.toEtsType() + val type = type.toEtsType() when (op) { - Ops.Binary.ADD -> EtsAddExpr(left, right) - Ops.Binary.SUB -> EtsSubExpr(left, right) - Ops.Binary.MUL -> EtsMulExpr(left, right) - Ops.Binary.DIV -> EtsDivExpr(left, right) - Ops.Binary.MOD -> EtsRemExpr(left, right) - Ops.Binary.EXP -> EtsExpExpr(left, right) - Ops.Binary.BIT_AND -> EtsBitAndExpr(left, right) - Ops.Binary.BIT_OR -> EtsBitOrExpr(left, right) - Ops.Binary.BIT_XOR -> EtsBitXorExpr(left, right) - Ops.Binary.LSH -> EtsLeftShiftExpr(left, right) - Ops.Binary.RSH -> EtsRightShiftExpr(left, right) - Ops.Binary.URSH -> EtsUnsignedRightShiftExpr(left, right) - Ops.Binary.AND -> EtsAndExpr(left, right) - Ops.Binary.OR -> EtsOrExpr(left, right) - Ops.Binary.NULLISH -> EtsNullishCoalescingExpr(left, right) + Ops.Binary.ADD -> EtsAddExpr(left, right, type) + Ops.Binary.SUB -> EtsSubExpr(left, right, type) + Ops.Binary.MUL -> EtsMulExpr(left, right, type) + Ops.Binary.DIV -> EtsDivExpr(left, right, type) + Ops.Binary.MOD -> EtsRemExpr(left, right, type) + Ops.Binary.EXP -> EtsExpExpr(left, right, type) + Ops.Binary.BIT_AND -> EtsBitAndExpr(left, right, type) + Ops.Binary.BIT_OR -> EtsBitOrExpr(left, right, type) + Ops.Binary.BIT_XOR -> EtsBitXorExpr(left, right, type) + Ops.Binary.LSH -> EtsLeftShiftExpr(left, right, type) + Ops.Binary.RSH -> EtsRightShiftExpr(left, right, type) + Ops.Binary.URSH -> EtsUnsignedRightShiftExpr(left, right, type) + Ops.Binary.AND -> EtsAndExpr(left, right, type) + Ops.Binary.OR -> EtsOrExpr(left, right, type) + Ops.Binary.NULLISH -> EtsNullishCoalescingExpr(left, right, type) else -> error("Unknown binop: $op") } } @@ -358,23 +360,29 @@ class EtsMethodBuilder( instance = (instance as LocalDto).toEtsLocal(), // safe cast callee = method.toEtsMethodSignature(), args = args.map { ensureLocal(it.toEtsEntity()) }, + type = type.toEtsType(), ) is StaticCallExprDto -> EtsStaticCallExpr( callee = method.toEtsMethodSignature(), args = args.map { ensureLocal(it.toEtsEntity()) }, + type = type.toEtsType(), ) is PtrCallExprDto -> EtsPtrCallExpr( ptr = ensureLocal(ptr.toEtsEntity() as EtsValue), // safe cast callee = method.toEtsMethodSignature(), args = args.map { ensureLocal(it.toEtsEntity()) }, + type = type.toEtsType(), ) - is ThisRefDto -> EtsThis + is ThisRefDto -> EtsThis( + type = type.toEtsType(), + ) is ParameterRefDto -> EtsParameterRef( index = index, + type = type.toEtsType(), ) is ArrayRefDto -> EtsArrayAccess( @@ -544,7 +552,7 @@ fun ClassTypeDto.toEtsClassType(): EtsClassType { } fun ConstantDto.toEtsConstant(): EtsConstant { - return when (type.toEtsType()) { + return when (val type = type.toEtsType()) { EtsStringType -> EtsStringConstant(value = this.value) EtsBooleanType -> EtsBooleanConstant(value = value.toBoolean()) @@ -555,8 +563,19 @@ fun ConstantDto.toEtsConstant(): EtsConstant { EtsUndefinedType -> EtsUndefinedConstant - // Note: we simply use StringConstant for all other types - else -> EtsStringConstant(value = this.value) + else -> object : EtsConstant { + val value: String = this@toEtsConstant.value + + override val type: EtsType = type + + override fun toString(): String { + return value + } + + override fun accept(visitor: EtsValue.Visitor): R { + return visitor.visit(this) + } + } } } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt index 3a7c7f4b2..ca5ba5705 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("OVERRIDE_DEPRECATION") + package org.jacodb.ets.model interface EtsConstant : EtsImmediate @@ -21,6 +23,9 @@ interface EtsConstant : EtsImmediate data class EtsStringConstant( val value: String, ) : EtsConstant { + override val type: EtsType + get() = EtsStringType + override fun toString(): String { return "\"$value\"" } @@ -33,6 +38,9 @@ data class EtsStringConstant( data class EtsBooleanConstant( val value: Boolean, ) : EtsConstant { + override val type: EtsType + get() = EtsBooleanType + override fun toString(): String { return if (value) "true" else "false" } @@ -50,6 +58,9 @@ data class EtsBooleanConstant( data class EtsNumberConstant( val value: Double, ) : EtsConstant { + override val type: EtsType + get() = EtsNumberType + override fun toString(): String { return value.toString() } @@ -60,6 +71,9 @@ data class EtsNumberConstant( } object EtsNullConstant : EtsConstant { + override val type: EtsType + get() = EtsNullType + override fun toString(): String = "null" override fun accept(visitor: EtsValue.Visitor): R { @@ -68,6 +82,9 @@ object EtsNullConstant : EtsConstant { } object EtsUndefinedConstant : EtsConstant { + override val type: EtsType + get() = EtsUndefinedType + override fun toString(): String = "undefined" override fun accept(visitor: EtsValue.Visitor): R { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt index cbb8c07c8..7d263621d 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt @@ -20,8 +20,11 @@ import org.jacodb.api.common.cfg.CommonExpr interface EtsEntity : CommonExpr { + @Deprecated("Do not rely on it!") + val type: EtsType + override val typeName: String - get() = error("Not supported") + get() = type.typeName interface Visitor : EtsValue.Visitor, @@ -52,8 +55,8 @@ data class EtsRawEntity( val kind: String, val extra: Map = emptyMap(), ) : EtsEntity { - // override val type: EtsType - // get() = EtsUnknownType + override val type: EtsType + get() = EtsUnknownType override fun toString(): String { return "$kind $extra" diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt index 637799e07..be3206a80 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("OVERRIDE_DEPRECATION") + package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonCallExpr @@ -142,7 +144,7 @@ interface EtsExpr : EtsEntity { } data class EtsNewExpr( - val type: EtsType, + override val type: EtsType, ) : EtsExpr { override fun toString(): String { return "new $type" @@ -157,6 +159,9 @@ data class EtsNewArrayExpr( val elementType: EtsType, val size: EtsEntity, ) : EtsExpr { + override val type: EtsType + get() = EtsArrayType(elementType, 1) + override fun toString(): String { return "new Array<$elementType>($size)" } @@ -168,7 +173,7 @@ data class EtsNewArrayExpr( data class EtsCastExpr( val arg: EtsEntity, - val type: EtsType, + override val type: EtsType, ) : EtsExpr { override fun toString(): String { return "$arg as $type" @@ -183,6 +188,9 @@ data class EtsInstanceOfExpr( val arg: EtsEntity, val checkType: EtsType, ) : EtsExpr { + override val type: EtsType + get() = EtsBooleanType + override fun toString(): String { return "$arg instanceof $checkType" } @@ -199,6 +207,9 @@ interface EtsUnaryExpr : EtsExpr { data class EtsDeleteExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { + override val type: EtsType + get() = EtsBooleanType + override fun toString(): String { return "delete $arg" } @@ -210,6 +221,7 @@ data class EtsDeleteExpr( data class EtsAwaitExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "await $arg" @@ -222,6 +234,7 @@ data class EtsAwaitExpr( data class EtsYieldExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "yield $arg" @@ -235,6 +248,9 @@ data class EtsYieldExpr( data class EtsTypeOfExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { + override val type: EtsType + get() = EtsStringType + override fun toString(): String { return "typeof $arg" } @@ -247,6 +263,9 @@ data class EtsTypeOfExpr( data class EtsVoidExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { + override val type: EtsType + get() = EtsUndefinedType + override fun toString(): String { return "void $arg" } @@ -259,6 +278,9 @@ data class EtsVoidExpr( data class EtsNotExpr( override val arg: EtsEntity, ) : EtsUnaryExpr { + override val type: EtsType + get() = EtsBooleanType + override fun toString(): String { return "!$arg" } @@ -270,6 +292,7 @@ data class EtsNotExpr( data class EtsBitNotExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "~$arg" @@ -282,6 +305,7 @@ data class EtsBitNotExpr( data class EtsNegExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "-$arg" @@ -294,6 +318,7 @@ data class EtsNegExpr( data class EtsUnaryPlusExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "+$arg" @@ -306,6 +331,7 @@ data class EtsUnaryPlusExpr( data class EtsPreIncExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "++$arg" @@ -318,6 +344,7 @@ data class EtsPreIncExpr( data class EtsPreDecExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "--$arg" @@ -330,6 +357,7 @@ data class EtsPreDecExpr( data class EtsPostIncExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "$arg++" @@ -342,6 +370,7 @@ data class EtsPostIncExpr( data class EtsPostDecExpr( override val arg: EtsEntity, + override val type: EtsType, ) : EtsUnaryExpr { override fun toString(): String { return "$arg--" @@ -357,7 +386,10 @@ interface EtsBinaryExpr : EtsExpr { val right: EtsEntity } -interface EtsRelationExpr : EtsBinaryExpr +interface EtsRelationExpr : EtsBinaryExpr { + override val type: EtsType + get() = EtsBooleanType +} data class EtsEqExpr( override val left: EtsEntity, @@ -481,6 +513,7 @@ interface EtsArithmeticExpr : EtsBinaryExpr data class EtsAddExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsArithmeticExpr { override fun toString(): String { return "$left + $right" @@ -494,6 +527,7 @@ data class EtsAddExpr( data class EtsSubExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsArithmeticExpr { override fun toString(): String { return "$left - $right" @@ -507,6 +541,7 @@ data class EtsSubExpr( data class EtsMulExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsArithmeticExpr { override fun toString(): String { return "$left * $right" @@ -520,6 +555,7 @@ data class EtsMulExpr( data class EtsDivExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsArithmeticExpr { override fun toString(): String { return "$left / $right" @@ -533,6 +569,7 @@ data class EtsDivExpr( data class EtsRemExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsArithmeticExpr { override fun toString(): String { return "$left % $right" @@ -546,6 +583,7 @@ data class EtsRemExpr( data class EtsExpExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsArithmeticExpr { override fun toString(): String { return "$left ** $right" @@ -561,6 +599,7 @@ interface EtsBitwiseExpr : EtsBinaryExpr data class EtsBitAndExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsBitwiseExpr { override fun toString(): String { return "$left & $right" @@ -574,6 +613,7 @@ data class EtsBitAndExpr( data class EtsBitOrExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsBitwiseExpr { override fun toString(): String { return "$left | $right" @@ -587,6 +627,7 @@ data class EtsBitOrExpr( data class EtsBitXorExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsBitwiseExpr { override fun toString(): String { return "$left ^ $right" @@ -600,6 +641,7 @@ data class EtsBitXorExpr( data class EtsLeftShiftExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsBitwiseExpr { override fun toString(): String { return "$left << $right" @@ -614,6 +656,7 @@ data class EtsLeftShiftExpr( data class EtsRightShiftExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsBitwiseExpr { override fun toString(): String { return "$left >> $right" @@ -628,6 +671,7 @@ data class EtsRightShiftExpr( data class EtsUnsignedRightShiftExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsBitwiseExpr { override fun toString(): String { return "$left >>> $right" @@ -643,6 +687,7 @@ interface EtsLogicalExpr : EtsBinaryExpr data class EtsAndExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsLogicalExpr { override fun toString(): String { return "$left && $right" @@ -656,6 +701,7 @@ data class EtsAndExpr( data class EtsOrExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsLogicalExpr { override fun toString(): String { return "$left || $right" @@ -669,6 +715,7 @@ data class EtsOrExpr( data class EtsNullishCoalescingExpr( override val left: EtsEntity, override val right: EtsEntity, + override val type: EtsType, ) : EtsLogicalExpr { override fun toString(): String { return "$left ?? $right" @@ -688,6 +735,7 @@ data class EtsInstanceCallExpr( override val instance: EtsLocal, override val callee: EtsMethodSignature, override val args: List, + override val type: EtsType, ) : EtsCallExpr, CommonInstanceCallExpr { override fun toString(): String { return "$instance.${callee.name}(${args.joinToString()})" @@ -701,6 +749,7 @@ data class EtsInstanceCallExpr( data class EtsStaticCallExpr( override val callee: EtsMethodSignature, override val args: List, + override val type: EtsType, ) : EtsCallExpr { override fun toString(): String { return "${callee.enclosingClass.name}.${callee.name}(${args.joinToString()})" @@ -712,9 +761,10 @@ data class EtsStaticCallExpr( } data class EtsPtrCallExpr( - val ptr: EtsValue, // Local or FieldRef + val ptr: EtsLocal, override val callee: EtsMethodSignature, override val args: List, + override val type: EtsType, ) : EtsCallExpr { override fun toString(): String { return "${ptr}.${callee.name}(${args.joinToString()})" diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt index c580522d4..69a345f7c 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt @@ -16,9 +16,10 @@ package org.jacodb.ets.model +@Suppress("OVERRIDE_DEPRECATION") data class EtsLocal( val name: String, - var type: EtsType = EtsUnknownType, + override val type: EtsType = EtsUnknownType, ) : EtsImmediate, EtsLValue { override fun toString(): String { return name diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt index c990d26ff..89d10445c 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("OVERRIDE_DEPRECATION") + package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonArgument @@ -23,7 +25,9 @@ import org.jacodb.api.common.cfg.CommonThis interface EtsRef : EtsValue -data object EtsThis : EtsRef, EtsImmediate, CommonThis { +data class EtsThis( + override val type: EtsType, +) : EtsRef, EtsImmediate, CommonThis { override fun toString(): String = "this" override fun accept(visitor: EtsValue.Visitor): R { @@ -33,6 +37,7 @@ data object EtsThis : EtsRef, EtsImmediate, CommonThis { data class EtsParameterRef( val index: Int, + override val type: EtsType, ) : EtsRef, CommonArgument { override fun toString(): String { return "arg$index" @@ -46,7 +51,7 @@ data class EtsParameterRef( data class EtsArrayAccess( override val array: EtsLocal, override val index: EtsValue, - val type: EtsType, // = EtsUnknownType, + override val type: EtsType, ) : EtsRef, EtsLValue, CommonArrayAccess { override fun toString(): String { return "$array[$index]" @@ -60,13 +65,13 @@ data class EtsArrayAccess( interface EtsFieldRef : EtsRef, EtsLValue, CommonFieldRef { override val instance: EtsLocal? val field: EtsFieldSignature - val type: EtsType + override val type: EtsType } data class EtsInstanceFieldRef( override val instance: EtsLocal, override val field: EtsFieldSignature, - override val type: EtsType, // = EtsUnknownType, + override val type: EtsType, ) : EtsFieldRef { override fun toString(): String { return "${instance}.${field.name}" @@ -79,7 +84,7 @@ data class EtsInstanceFieldRef( data class EtsStaticFieldRef( override val field: EtsFieldSignature, - override val type: EtsType, // = EtsUnknownType, + override val type: EtsType, ) : EtsFieldRef { override val instance get() = null diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Value.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Value.kt index aebd755fb..50355c9c6 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Value.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Value.kt @@ -23,6 +23,7 @@ interface EtsValue : EtsEntity, CommonValue { fun visit(value: EtsLocal): R // Constant + fun visit(value: EtsConstant): R fun visit(value: EtsStringConstant): R fun visit(value: EtsBooleanConstant): R fun visit(value: EtsNumberConstant): R @@ -39,6 +40,7 @@ interface EtsValue : EtsEntity, CommonValue { interface Default : Visitor { override fun visit(value: EtsLocal): R = defaultVisit(value) + override fun visit(value: EtsConstant): R = defaultVisit(value) override fun visit(value: EtsStringConstant): R = defaultVisit(value) override fun visit(value: EtsBooleanConstant): R = defaultVisit(value) override fun visit(value: EtsNumberConstant): R = defaultVisit(value) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt index 4c7586601..6cd537736 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt @@ -159,36 +159,42 @@ class EtsBlockCfgBuilder( private fun Expr.toEtsEntity(): EtsEntity = when (this) { is Local -> EtsLocal( name = name, - type = EtsUnknownType, + type = EtsUnknownType, // TODO ) is Parameter -> EtsParameterRef( index = index, + type = EtsUnknownType, // TODO ) - ThisRef -> EtsThis + ThisRef -> EtsThis( + type = EtsUnknownType, // TODO + ) is Constant -> EtsNumberConstant(value = value) is UnaryExpr -> when (operator) { - UnaryOperator.NOT -> { - EtsNotExpr(arg = expr.toEtsEntity()) - } + UnaryOperator.NOT -> EtsNotExpr( + arg = expr.toEtsEntity(), + ) - UnaryOperator.NEG -> { - EtsNegExpr(arg = expr.toEtsEntity()) - } + UnaryOperator.NEG -> EtsNegExpr( + arg = expr.toEtsEntity(), + type = EtsUnknownType, // TODO + ) } is BinaryExpr -> when (operator) { BinaryOperator.AND -> EtsAndExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), + type = EtsUnknownType, // TODO ) BinaryOperator.OR -> EtsOrExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), + type = EtsUnknownType, // TODO ) BinaryOperator.EQ -> EtsEqExpr( @@ -224,21 +230,25 @@ class EtsBlockCfgBuilder( BinaryOperator.ADD -> EtsAddExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), + type = EtsUnknownType, // TODO ) BinaryOperator.SUB -> EtsSubExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), + type = EtsUnknownType, // TODO ) BinaryOperator.MUL -> EtsMulExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), + type = EtsUnknownType, // TODO ) BinaryOperator.DIV -> EtsDivExpr( left = left.toEtsEntity(), right = right.toEtsEntity(), + type = EtsUnknownType, // TODO ) } } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetOperands.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetOperands.kt index b07ba8c52..d0731e987 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetOperands.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/GetOperands.kt @@ -28,6 +28,7 @@ import org.jacodb.ets.model.EtsBitXorExpr import org.jacodb.ets.model.EtsBooleanConstant import org.jacodb.ets.model.EtsCallStmt import org.jacodb.ets.model.EtsCastExpr +import org.jacodb.ets.model.EtsConstant import org.jacodb.ets.model.EtsDeleteExpr import org.jacodb.ets.model.EtsDivExpr import org.jacodb.ets.model.EtsEntity @@ -119,6 +120,9 @@ private object EntityGetOperands : EtsEntity.Visitor> { override fun visit(value: EtsLocal): Sequence = emptySequence() + override fun visit(value: EtsConstant): Sequence = + emptySequence() + override fun visit(value: EtsStringConstant): Sequence = emptySequence() 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 index a42d01b46..68bc8f8a0 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt @@ -28,6 +28,7 @@ import org.jacodb.ets.model.EtsBitXorExpr import org.jacodb.ets.model.EtsBooleanConstant import org.jacodb.ets.model.EtsCallStmt import org.jacodb.ets.model.EtsCastExpr +import org.jacodb.ets.model.EtsConstant import org.jacodb.ets.model.EtsDeleteExpr import org.jacodb.ets.model.EtsDivExpr import org.jacodb.ets.model.EtsEntity @@ -137,6 +138,10 @@ abstract class AbstractHandler : EtsEntity.Visitor, EtsStmt.Visitor handle(value) } + final override fun visit(value: EtsConstant) { + handle(value) + } + final override fun visit(value: EtsStringConstant) { handle(value) } 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 index 8f05ef1df..8fd8f464a 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/CollectorTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/CollectorTest.kt @@ -25,6 +25,7 @@ import org.jacodb.ets.model.EtsMethod import org.jacodb.ets.model.EtsNumberConstant import org.jacodb.ets.model.EtsStmt import org.jacodb.ets.model.EtsStmtLocation +import org.jacodb.ets.model.EtsUnknownType import org.jacodb.ets.utils.AbstractHandler import org.jacodb.ets.utils.EntityCollector import kotlin.test.Test @@ -37,7 +38,7 @@ class CollectorTest { val a = EtsLocal("a") val b = EtsLocal("b") val n = EtsNumberConstant(42.0) - val rhv = EtsAddExpr(b, n) + val rhv = EtsAddExpr(b, n, type = EtsUnknownType) val loc = EtsStmtLocation.stub(method) val stmt = EtsAssignStmt(loc, a, rhv) return stmt From 70a0f5eff55337b82c880ea323174db66990abf4 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Thu, 17 Apr 2025 12:57:41 +0300 Subject: [PATCH 3/8] Suppress --- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt index 7d263621d..a3a8a724a 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("OVERRIDE_DEPRECATION") + package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonExpr @@ -23,6 +25,7 @@ interface EtsEntity : CommonExpr { @Deprecated("Do not rely on it!") val type: EtsType + @Suppress("DEPRECATION") override val typeName: String get() = type.typeName From e11a0e3032d1022d0252c23dfdf984d74d5e1fa9 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Thu, 17 Apr 2025 12:59:49 +0300 Subject: [PATCH 4/8] Remove unnecessary commented code --- .../kotlin/org/jacodb/ets/model/CfgBlock.kt | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgBlock.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgBlock.kt index 094c425f2..4f8bda97d 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgBlock.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/CfgBlock.kt @@ -65,28 +65,6 @@ class EtsBlockCfg( override fun throwers(node: EtsStmt): Set = linear.throwers(node) override fun catchers(node: EtsStmt): Set = linear.catchers(node) - // val stmts: List by lazy { - // val queue = ArrayDeque() - // val visited: MutableSet = hashSetOf() - // val result = mutableListOf() - // - // if (blocks.isNotEmpty()) { - // queue += blocks.first() - // } - // - // while (queue.isNotEmpty()) { - // val block = queue.removeFirst() - // if (visited.add(block)) { - // result += block.statements - // for (s in successors[block.id].orEmpty()) { - // queue += blocks[s] - // } - // } - // } - // - // result - // } - companion object { val EMPTY: EtsBlockCfg by lazy { EtsBlockCfg(emptyList(), emptyMap()) From a923dadd96dee5ee84e00cbabee2babd2d122708 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Thu, 17 Apr 2025 13:01:42 +0300 Subject: [PATCH 5/8] Remove custom equals for EtsStmtLocation --- .../org/jacodb/ets/model/StmtLocation.kt | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/StmtLocation.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/StmtLocation.kt index d9ec452eb..759afa466 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/StmtLocation.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/StmtLocation.kt @@ -22,25 +22,6 @@ data class EtsStmtLocation( override val method: EtsMethod, var index: Int, ) : CommonInstLocation { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as EtsStmtLocation - - if (method != other.method) return false - // if (index == -1 || other.index == -1) return false - if (index != other.index) return false - - return true - } - - override fun hashCode(): Int { - var result = method.hashCode() - result = 31 * result + index.hashCode() - return result - } - companion object { fun stub(method: EtsMethod): EtsStmtLocation { return EtsStmtLocation(method, -1) From ebe7621465a962b2630abf7953c30b63bb51f712 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Thu, 17 Apr 2025 13:02:19 +0300 Subject: [PATCH 6/8] Remove unnecessary code --- .../org/jacodb/ets/utils/BlockCfgBuilder.kt | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt index 6cd537736..d039a0c80 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/BlockCfgBuilder.kt @@ -253,40 +253,3 @@ class EtsBlockCfgBuilder( } } } - -private fun main() { - val method = EtsMethodImpl( - EtsMethodSignature( - enclosingClass = EtsClassSignature.Companion.UNKNOWN, - name = "foo", - parameters = listOf( - EtsMethodParameter( - index = 0, - name = "x", - type = EtsUnknownType, - ), - EtsMethodParameter( - index = 1, - name = "y", - type = EtsUnknownType, - ), - ), - returnType = EtsUnknownType, - ) - ) - val p = program { - assign(local("x"), param(0)) - assign(local("y"), param(1)) - ifStmt(and(local("x"), local("y"))) { - ret(add(local("x"), local("y"))) - } - ret(const(0.0)) - } - val blockCfg = p.toBlockCfg() - val etsBlockCfg = blockCfg.toEtsBlockCfg(method) - println(etsBlockCfg.toDot()) - view(etsBlockCfg.toDot(), name = "etsBlockCfg") - val etsCfg = etsBlockCfg.linearize() - println(etsCfg.toDot()) - view(etsCfg.toDot(), name = "etsCfg") -} From 8e94521475c0c538d20c70808e7614baa6831459 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Thu, 17 Apr 2025 13:07:02 +0300 Subject: [PATCH 7/8] Remove commented code --- .../src/main/kotlin/org/jacodb/ets/utils/Handler.kt | 8 -------- 1 file changed, 8 deletions(-) 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 index 68bc8f8a0..668823f36 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Handler.kt @@ -88,14 +88,6 @@ abstract class AbstractHandler : EtsEntity.Visitor, EtsStmt.Visitor 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: EtsRawStmt) { handle(stmt) } From fdbbbfa23c73e4aa78d1b312a54416d457b2c3b9 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Thu, 17 Apr 2025 14:36:36 +0300 Subject: [PATCH 8/8] Remove deprecate --- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt | 2 -- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt | 4 ---- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt | 2 -- jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt | 1 - jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt | 2 -- 5 files changed, 11 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt index ca5ba5705..c9db2733b 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Constant.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:Suppress("OVERRIDE_DEPRECATION") - package org.jacodb.ets.model interface EtsConstant : EtsImmediate diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt index a3a8a724a..c4c63193e 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Entity.kt @@ -14,18 +14,14 @@ * limitations under the License. */ -@file:Suppress("OVERRIDE_DEPRECATION") - package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonExpr interface EtsEntity : CommonExpr { - @Deprecated("Do not rely on it!") val type: EtsType - @Suppress("DEPRECATION") override val typeName: String get() = type.typeName diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt index be3206a80..3fa7f405d 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Expr.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:Suppress("OVERRIDE_DEPRECATION") - package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonCallExpr diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt index 69a345f7c..491ec8bf5 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Local.kt @@ -16,7 +16,6 @@ package org.jacodb.ets.model -@Suppress("OVERRIDE_DEPRECATION") data class EtsLocal( val name: String, override val type: EtsType = EtsUnknownType, diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt index 89d10445c..bb647bd57 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/model/Ref.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:Suppress("OVERRIDE_DEPRECATION") - package org.jacodb.ets.model import org.jacodb.api.common.cfg.CommonArgument