From a3f641fb08e93330822f3b2b9186a8b42fcf64c2 Mon Sep 17 00:00:00 2001 From: MchKosticyn Date: Fri, 18 Jul 2025 15:29:00 +0300 Subject: [PATCH 1/5] [feat] supported versions for approximations --- .../jacodb/approximation/Approximations.kt | 107 +++++++++++++++- .../InstSubstitutorForApproximations.kt | 121 +++++++++--------- .../approximation/TransformerIntoVirtual.kt | 12 +- .../kotlin/org/jacodb/approximation/Util.kt | 7 +- .../annotation/ApproximationAnnotations.kt | 9 +- .../approximations/ApproximationsTest.kt | 18 +-- .../impl/storage/ers/ErsPersistenceImpl.kt | 2 +- 7 files changed, 189 insertions(+), 87 deletions(-) diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt index eb2280752..ebcc76d92 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt @@ -31,8 +31,8 @@ import org.jacodb.api.jvm.cfg.JcInstList import org.jacodb.api.jvm.cfg.JcRawInst import org.jacodb.api.storage.StorageContext import org.jacodb.api.storage.ers.compressed -import org.jacodb.approximation.TransformerIntoVirtual.transformMethodIntoVirtual import org.jacodb.approximation.annotation.Approximate +import org.jacodb.approximation.annotation.Version import org.jacodb.impl.cfg.JcInstListImpl import org.jacodb.impl.fs.className import org.jacodb.impl.storage.dslContext @@ -59,7 +59,12 @@ import java.util.concurrent.ConcurrentMap * otherwise you might get incomplete mapping. * See [JcDatabase.awaitBackgroundJobs]. */ -object Approximations : JcFeature, JcClassExtFeature, JcInstExtFeature { +class Approximations( + private val versions: List +) : JcFeature, JcClassExtFeature, JcInstExtFeature { + + private val instSubstitutorForApproximations = InstSubstitutorForApproximations(this) + private val transformerIntoVirtual = TransformerIntoVirtual(this) private val originalToApproximation: ConcurrentMap = ConcurrentHashMap() private val approximationToOriginal: ConcurrentMap = ConcurrentHashMap() @@ -95,11 +100,40 @@ object Approximations : JcFeature, JcClassExtFeature, JcInstExtFeatu }, noSqlAction = { val valueId = persistence.findSymbolId("value") + val versionsId = persistence.findSymbolId("versions") + val versionSymbol = persistence.findSymbolId(versionAnnotationClassName) + val targetId = persistence.findSymbolId("target") + val fromVersionId = persistence.findSymbolId("fromVersion") + val toVersionId = persistence.findSymbolId("toVersion") context.txn.find("Annotation", "nameId", approxSymbol.compressed) .filter { it.getCompressedBlob("refKind") == RefKind.CLASS.ordinal } - .flatMap { annotation -> + .mapNotNull { annotation -> + val values = annotation.getLinks("values") + val versionsValue = values.filterTo(mutableListOf()) { versionsId == it["nameId"] } + if (versionsValue.isEmpty()) + return@mapNotNull annotation to values + + val versionMatches = versionsValue.any { versionValue -> + val versionAnnotation = versionValue.getLink("refAnnotation") + check(versionSymbol == versionAnnotation["nameId"]) + val versionValues = versionAnnotation.getLinks("values") + val target = persistence.findSymbolName( + versionValues.find { targetId == it["nameId"] }!!["primitiveValue"]!! + ) + val fromVersion = persistence.findSymbolName( + versionValues.find { fromVersionId == it["nameId"] }!!["primitiveValue"]!! + ) + val toVersion = persistence.findSymbolName( + versionValues.find { toVersionId == it["nameId"] }!!["primitiveValue"]!! + ) + versions.any { VersionsIntervalInfo(target, fromVersion, toVersion).matches(it) } + } + if (versionMatches) + annotation to values + else null + }.flatMap { (annotation, values) -> annotation.getLink("ref").let { clazz -> - annotation.getLinks("values").map { clazz to it } + values.map { clazz to it } } }.filter { (_, annotationValue) -> valueId == annotationValue["nameId"] @@ -125,13 +159,13 @@ object Approximations : JcFeature, JcClassExtFeature, JcInstExtFeatu val approximationName = findApproximationByOriginOrNull(clazz.name.toOriginalName()) ?: return null val approximationClass = clazz.classpath.findClassOrNull(approximationName) ?: return null - return approximationClass.declaredFields.map { TransformerIntoVirtual.transformIntoVirtualField(clazz, it) } + return approximationClass.declaredFields.map { transformerIntoVirtual.transformIntoVirtualField(clazz, it) } } /** * Returns a list of [JcEnrichedVirtualMethod] if there is an approximation for [clazz] and null otherwise. */ - override fun methodsOf(clazz: JcClassOrInterface): List? { + override fun methodsOf(clazz: JcClassOrInterface): List? = with(transformerIntoVirtual) { val approximationName = findApproximationByOriginOrNull(clazz.name.toOriginalName()) ?: return null val approximationClass = clazz.classpath.findClassOrNull(approximationName) ?: return null @@ -141,7 +175,7 @@ object Approximations : JcFeature, JcClassExtFeature, JcInstExtFeatu } override fun transformRawInstList(method: JcMethod, list: JcInstList): JcInstList { - return JcInstListImpl(list.map { it.accept(InstSubstitutorForApproximations) }) + return JcInstListImpl(list.map { it.accept(instSubstitutorForApproximations) }) } /** @@ -159,6 +193,64 @@ object Approximations : JcFeature, JcClassExtFeature, JcInstExtFeatu ): String? = approximationToOriginal[className]?.className } +data class VersionInfo( + val target: String, + val version: String, +) { + init { + check(version.isVersion) + } +} + +private data class VersionsIntervalInfo( + val target: String, + val fromVersion: String, + val toVersion: String, +) { + init { + check(fromVersion.isVersion) + check(toVersion.isVersion) + } +} + +/** + * Checks if the string is a version in the form of 3 numbers separated by dots, e.g., "1.2.3". + */ +private val String.isVersion: Boolean get() = + Regex("^(\\d+)\\.(\\d+)\\.(\\d+)$").matches(this) + +private val String.toNumbers: IntArray get() { + val numbers = this.split('.') + check(numbers.size == 3) + return IntArray(numbers.size) { i -> numbers[i].toInt() } +} + +/** + * Checks if a version (array of 3 numbers) is within the inclusive range defined by fromVersion and toVersion (also arrays of 3 numbers). + */ +private fun IntArray.isVersionInRange(fromVersion: IntArray, toVersion: IntArray): Boolean { + require(this.size == 3 && fromVersion.size == 3 && toVersion.size == 3) { "All version arrays must have size 3" } + for (i in 0..2) { + if (this[i] < fromVersion[i]) return false + if (this[i] > toVersion[i]) return false + } + + return true +} + +private fun VersionsIntervalInfo.matches(versionInfo: VersionInfo): Boolean { + check(fromVersion.isVersion && toVersion.isVersion && versionInfo.version.isVersion) + + if (versionInfo.target != this.target) + return false + + val fromVersionNumbers = fromVersion.toNumbers + val toVersionNumbers = toVersion.toNumbers + val versionNumbers = versionInfo.version.toNumbers + + return versionNumbers.isVersionInRange(fromVersionNumbers, toVersionNumbers) +} + private class ApproximationIndexer( private val originalToApproximation: ConcurrentMap, private val approximationToOriginal: ConcurrentMap @@ -201,6 +293,7 @@ private class ApproximationIndexer( } private val approximationAnnotationClassName = Approximate::class.qualifiedName!! +private val versionAnnotationClassName = Version::class.qualifiedName!! @JvmInline value class ApproximationClassName(val className: String) { diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/InstSubstitutorForApproximations.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/InstSubstitutorForApproximations.kt index e331551c2..7b34d9e1e 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/InstSubstitutorForApproximations.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/InstSubstitutorForApproximations.kt @@ -95,13 +95,14 @@ import org.jacodb.api.jvm.cfg.JcRawUshrExpr import org.jacodb.api.jvm.cfg.JcRawValue import org.jacodb.api.jvm.cfg.JcRawVirtualCallExpr import org.jacodb.api.jvm.cfg.JcRawXorExpr -import org.jacodb.approximation.Approximations.findOriginalByApproximationOrNull import org.jacodb.impl.types.TypeNameImpl /** * Removes all occurrences of approximations with their targets in [JcRawInst]s and [JcRawExpr]s. */ -object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExprVisitor { +class InstSubstitutorForApproximations( + private val approximations: Approximations +) : JcRawInstVisitor, JcRawExprVisitor { override fun visitJcRawAssignInst(inst: JcRawAssignInst): JcRawInst { val newLhv = inst.lhv.accept(this) as JcRawValue val newRhv = inst.rhv.accept(this) @@ -144,7 +145,7 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr override fun visitJcRawCatchInst(inst: JcRawCatchInst): JcRawInst { val newThrowable = inst.throwable.accept(this) as JcRawValue val entries = inst.entries.map { - it.copy(acceptedThrowable = it.acceptedThrowable.eliminateApproximation()) + it.copy(acceptedThrowable = it.acceptedThrowable.eliminateApproximation(approximations)) } return JcRawCatchInst(inst.owner, newThrowable, inst.handler, entries) @@ -172,7 +173,7 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr val newLhv = expr.lhv.accept(this) as JcRawValue val newRhv = expr.rhv.accept(this) as JcRawValue - return constructor(newLhv.typeName.eliminateApproximation(), newLhv, newRhv) + return constructor(newLhv.typeName.eliminateApproximation(approximations), newLhv, newRhv) } override fun visitJcRawAddExpr(expr: JcRawAddExpr): JcRawExpr = binaryHandler(expr) { type, lhv, rhv -> @@ -204,27 +205,27 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr } override fun visitJcRawEqExpr(expr: JcRawEqExpr) = binaryHandler(expr) { _, lhv, rhv -> - JcRawEqExpr(expr.typeName.eliminateApproximation(), lhv, rhv) + JcRawEqExpr(expr.typeName.eliminateApproximation(approximations), lhv, rhv) } override fun visitJcRawNeqExpr(expr: JcRawNeqExpr) = binaryHandler(expr) { _, lhv, rhv -> - JcRawNeqExpr(expr.typeName.eliminateApproximation(), lhv, rhv) + JcRawNeqExpr(expr.typeName.eliminateApproximation(approximations), lhv, rhv) } override fun visitJcRawGeExpr(expr: JcRawGeExpr) = binaryHandler(expr) { _, lhv, rhv -> - JcRawGeExpr(expr.typeName.eliminateApproximation(), lhv, rhv) + JcRawGeExpr(expr.typeName.eliminateApproximation(approximations), lhv, rhv) } override fun visitJcRawGtExpr(expr: JcRawGtExpr) = binaryHandler(expr) { _, lhv, rhv -> - JcRawGtExpr(expr.typeName.eliminateApproximation(), lhv, rhv) + JcRawGtExpr(expr.typeName.eliminateApproximation(approximations), lhv, rhv) } override fun visitJcRawLeExpr(expr: JcRawLeExpr) = binaryHandler(expr) { _, lhv, rhv -> - JcRawLeExpr(expr.typeName.eliminateApproximation(), lhv, rhv) + JcRawLeExpr(expr.typeName.eliminateApproximation(approximations), lhv, rhv) } override fun visitJcRawLtExpr(expr: JcRawLtExpr) = binaryHandler(expr) { _, lhv, rhv -> - JcRawLtExpr(expr.typeName.eliminateApproximation(), lhv, rhv) + JcRawLtExpr(expr.typeName.eliminateApproximation(approximations), lhv, rhv) } override fun visitJcRawOrExpr(expr: JcRawOrExpr) = binaryHandler(expr) { type, lhv, rhv -> @@ -257,17 +258,17 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr override fun visitJcRawLengthExpr(expr: JcRawLengthExpr): JcRawExpr { val newArray = expr.array.accept(this) as JcRawValue - return JcRawLengthExpr(expr.typeName.eliminateApproximation(), newArray) + return JcRawLengthExpr(expr.typeName.eliminateApproximation(approximations), newArray) } override fun visitJcRawNegExpr(expr: JcRawNegExpr): JcRawExpr { val newOperand = expr.operand.accept(this) as JcRawValue - return JcRawNegExpr(newOperand.typeName.eliminateApproximation(), newOperand) + return JcRawNegExpr(newOperand.typeName.eliminateApproximation(approximations), newOperand) } override fun visitJcRawCastExpr(expr: JcRawCastExpr): JcRawExpr { val newOperand = expr.operand.accept(this) as JcRawValue - return JcRawCastExpr(expr.typeName.eliminateApproximation(), newOperand) + return JcRawCastExpr(expr.typeName.eliminateApproximation(approximations), newOperand) } override fun visitJcRawNewExpr(expr: JcRawNewExpr): JcRawExpr { @@ -276,22 +277,22 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr override fun visitJcRawNewArrayExpr(expr: JcRawNewArrayExpr): JcRawExpr { val newDimensions = expr.dimensions.map { it.accept(this) as JcRawValue } - return JcRawNewArrayExpr(expr.typeName.eliminateApproximation(), newDimensions) + return JcRawNewArrayExpr(expr.typeName.eliminateApproximation(approximations), newDimensions) } override fun visitJcRawInstanceOfExpr(expr: JcRawInstanceOfExpr): JcRawExpr { val newOperand = expr.operand.accept(this) as JcRawValue return JcRawInstanceOfExpr( - expr.typeName.eliminateApproximation(), + expr.typeName.eliminateApproximation(approximations), newOperand, - expr.targetType.eliminateApproximation() + expr.targetType.eliminateApproximation(approximations) ) } private fun BsmHandle.eliminateApproximations(): BsmHandle = copy( - declaringClass = declaringClass.eliminateApproximation(), - argTypes = argTypes.map { it.eliminateApproximation() }, - returnType = returnType.eliminateApproximation() + declaringClass = declaringClass.eliminateApproximation(approximations), + argTypes = argTypes.map { it.eliminateApproximation(approximations) }, + returnType = returnType.eliminateApproximation(approximations) ) override fun visitJcRawDynamicCallExpr(expr: JcRawDynamicCallExpr): JcRawExpr { @@ -308,17 +309,17 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr is BsmIntArg -> arg is BsmLongArg -> arg is BsmMethodTypeArg -> arg.copy( - arg.argumentTypes.map { it.eliminateApproximation() }, - arg.returnType.eliminateApproximation() + arg.argumentTypes.map { it.eliminateApproximation(approximations) }, + arg.returnType.eliminateApproximation(approximations) ) is BsmStringArg -> arg - is BsmTypeArg -> arg.copy(arg.typeName.eliminateApproximation()) + is BsmTypeArg -> arg.copy(arg.typeName.eliminateApproximation(approximations)) } }, callSiteMethodName, - callSiteArgTypes.map { it.eliminateApproximation() }, - callSiteReturnType.eliminateApproximation(), + callSiteArgTypes.map { it.eliminateApproximation(approximations) }, + callSiteReturnType.eliminateApproximation(approximations), newArgs ) } @@ -330,10 +331,10 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr return with(expr) { JcRawVirtualCallExpr( - declaringClass.eliminateApproximation(), + declaringClass.eliminateApproximation(approximations), methodName, - argumentTypes.map { it.eliminateApproximation() }, - returnType.eliminateApproximation(), + argumentTypes.map { it.eliminateApproximation(approximations) }, + returnType.eliminateApproximation(approximations), newInstance, newArgs ) @@ -346,10 +347,10 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr return with(expr) { JcRawInterfaceCallExpr( - declaringClass.eliminateApproximation(), + declaringClass.eliminateApproximation(approximations), methodName, - argumentTypes.map { it.eliminateApproximation() }, - returnType.eliminateApproximation(), + argumentTypes.map { it.eliminateApproximation(approximations) }, + returnType.eliminateApproximation(approximations), newInstance, newArgs ) @@ -361,10 +362,10 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr return with(expr) { JcRawStaticCallExpr( - declaringClass.eliminateApproximation(), + declaringClass.eliminateApproximation(approximations), methodName, - argumentTypes.map { it.eliminateApproximation() }, - returnType.eliminateApproximation(), + argumentTypes.map { it.eliminateApproximation(approximations) }, + returnType.eliminateApproximation(approximations), newArgs, isInterfaceMethodCall ) @@ -377,10 +378,10 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr return with(expr) { JcRawSpecialCallExpr( - declaringClass.eliminateApproximation(), + declaringClass.eliminateApproximation(approximations), methodName, - argumentTypes.map { it.eliminateApproximation() }, - returnType.eliminateApproximation(), + argumentTypes.map { it.eliminateApproximation(approximations) }, + returnType.eliminateApproximation(approximations), newInstance, newArgs ) @@ -388,7 +389,7 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr } override fun visitJcRawThis(value: JcRawThis): JcRawExpr { - return value.copy(value.typeName.eliminateApproximation()) + return value.copy(value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawArgument(value: JcRawArgument): JcRawExpr { @@ -397,21 +398,21 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr private fun T.eliminateApproximations(typeName: TypeName, constructor: (TypeName) -> T): T { val className = typeName.typeName.toApproximationName() - val originalClassName = findOriginalByApproximationOrNull(className) ?: return this + val originalClassName = approximations.findOriginalByApproximationOrNull(className) ?: return this return constructor(TypeNameImpl.fromTypeName(originalClassName)) } override fun visitJcRawLocalVar(value: JcRawLocalVar): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawFieldRef(value: JcRawFieldRef): JcRawExpr { val newInstance = value.instance?.accept(this) as? JcRawValue return JcRawFieldRef( newInstance, - value.declaringClass.eliminateApproximation(), + value.declaringClass.eliminateApproximation(approximations), value.fieldName, - value.typeName.eliminateApproximation() + value.typeName.eliminateApproximation(approximations) ) } @@ -419,43 +420,43 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr val newArray = value.array.accept(this) as JcRawValue val newIndex = value.index.accept(this) as JcRawValue - return JcRawArrayAccess(newArray, newIndex, value.typeName.eliminateApproximation()) + return JcRawArrayAccess(newArray, newIndex, value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawBool(value: JcRawBool): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawByte(value: JcRawByte): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawChar(value: JcRawChar): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawShort(value: JcRawShort): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawInt(value: JcRawInt): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawLong(value: JcRawLong): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawFloat(value: JcRawFloat): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawDouble(value: JcRawDouble): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawNullConstant(value: JcRawNullConstant): JcRawExpr { - return value.copy(typeName = value.typeName.eliminateApproximation()) + return value.copy(typeName = value.typeName.eliminateApproximation(approximations)) } override fun visitJcRawStringConstant(value: JcRawStringConstant): JcRawExpr { @@ -464,19 +465,19 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr override fun visitJcRawClassConstant(value: JcRawClassConstant): JcRawExpr { return JcRawClassConstant( - value.className.eliminateApproximation(), - value.typeName.eliminateApproximation() + value.className.eliminateApproximation(approximations), + value.typeName.eliminateApproximation(approximations) ) } override fun visitJcRawMethodConstant(value: JcRawMethodConstant): JcRawExpr { return with(value) { JcRawMethodConstant( - declaringClass.eliminateApproximation(), + declaringClass.eliminateApproximation(approximations), name, - argumentTypes.map { it.eliminateApproximation() }, - returnType.eliminateApproximation(), - typeName.eliminateApproximation() + argumentTypes.map { it.eliminateApproximation(approximations) }, + returnType.eliminateApproximation(approximations), + typeName.eliminateApproximation(approximations) ) } } @@ -484,9 +485,9 @@ object InstSubstitutorForApproximations : JcRawInstVisitor, JcRawExpr override fun visitJcRawMethodType(value: JcRawMethodType): JcRawExpr { return with(value) { JcRawMethodType( - argumentTypes.map { it.eliminateApproximation() }, - returnType.eliminateApproximation(), - typeName.eliminateApproximation() + argumentTypes.map { it.eliminateApproximation(approximations) }, + returnType.eliminateApproximation(approximations), + typeName.eliminateApproximation(approximations) ) } } diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/TransformerIntoVirtual.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/TransformerIntoVirtual.kt index f3ea27c99..91773f2f5 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/TransformerIntoVirtual.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/TransformerIntoVirtual.kt @@ -22,7 +22,9 @@ import org.jacodb.api.jvm.JcField import org.jacodb.api.jvm.JcMethod import org.jacodb.impl.features.JcFeaturesChain -object TransformerIntoVirtual { +class TransformerIntoVirtual( + private val approximations: Approximations +) { fun JcClasspath.transformMethodIntoVirtual( to: JcClassOrInterface, method: JcMethod @@ -30,20 +32,20 @@ object TransformerIntoVirtual { val parameters = parameters.map { param -> // TODO process annotations somehow to eliminate approximations with(param) { - JcEnrichedVirtualParameter(index, type.eliminateApproximation(), name, annotations, access) + JcEnrichedVirtualParameter(index, type.eliminateApproximation(approximations), name, annotations, access) } } val featuresChain = features?.let { JcFeaturesChain(it) } ?: JcFeaturesChain(emptyList()) - val exceptions = exceptions.map { it.eliminateApproximation() } + val exceptions = exceptions.map { it.eliminateApproximation(approximations) } val methodNode = withAsmNode { it } // Safe since used under synchronization in JcEnrichedVirtualMethod (EnrichedVirtualMethodBuilder() .name(name) .access(access) - .returnType(returnType.eliminateApproximation().typeName) as EnrichedVirtualMethodBuilder) + .returnType(returnType.eliminateApproximation(approximations).typeName) as EnrichedVirtualMethodBuilder) .enrichedParameters(parameters) .featuresChain(featuresChain) .exceptions(exceptions) @@ -59,7 +61,7 @@ object TransformerIntoVirtual { ): JcEnrichedVirtualField = with(field) { (EnrichedVirtualFieldBuilder() .name(name) - .type(type.eliminateApproximation().typeName) + .type(type.eliminateApproximation(approximations).typeName) .access(access) as EnrichedVirtualFieldBuilder) .annotations(annotations) .build() diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Util.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Util.kt index 4a0d53b9f..73d5aa467 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Util.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Util.kt @@ -17,7 +17,6 @@ package org.jacodb.approximation import org.jacodb.api.jvm.TypeName -import org.jacodb.approximation.Approximations.findOriginalByApproximationOrNull import org.jacodb.impl.cfg.util.asArray import org.jacodb.impl.cfg.util.baseElementType import org.jacodb.impl.cfg.util.isArray @@ -26,12 +25,12 @@ import org.jacodb.impl.types.TypeNameImpl fun String.toApproximationName() = ApproximationClassName(this) fun String.toOriginalName() = OriginalClassName(this) -fun TypeName.eliminateApproximation(): TypeName { +fun TypeName.eliminateApproximation(approximations: Approximations): TypeName { if (this.isArray) { val (elemType, dim) = this.baseElementType() - val resultElemType = elemType.eliminateApproximation() + val resultElemType = elemType.eliminateApproximation(approximations) return resultElemType.asArray(dim) } - val originalClassName = findOriginalByApproximationOrNull(typeName.toApproximationName()) ?: return this + val originalClassName = approximations.findOriginalByApproximationOrNull(typeName.toApproximationName()) ?: return this return TypeNameImpl.fromTypeName(originalClassName) } diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/annotation/ApproximationAnnotations.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/annotation/ApproximationAnnotations.kt index e5b369a70..bcf6677ea 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/annotation/ApproximationAnnotations.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/annotation/ApproximationAnnotations.kt @@ -18,6 +18,13 @@ package org.jacodb.approximation.annotation import kotlin.reflect.KClass +@Suppress("unused") +@Target(allowedTargets = []) +annotation class Version(val target: String, val fromVersion: String, val toVersion: String) + @Suppress("unused") @Target(AnnotationTarget.CLASS) -annotation class Approximate(val value: KClass<*>) +annotation class Approximate( + val value: KClass<*>, + val versions: Array = [], +) diff --git a/jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt b/jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt index ed20ad288..3c3fdd6b9 100644 --- a/jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt +++ b/jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt @@ -26,8 +26,6 @@ import org.jacodb.api.jvm.cfg.JcRawFieldRef import org.jacodb.api.jvm.ext.findClass import org.jacodb.api.jvm.ext.findDeclaredFieldOrNull import org.jacodb.approximation.Approximations -import org.jacodb.approximation.Approximations.findApproximationByOriginOrNull -import org.jacodb.approximation.Approximations.findOriginalByApproximationOrNull import org.jacodb.approximation.JcEnrichedVirtualField import org.jacodb.approximation.JcEnrichedVirtualMethod import org.jacodb.approximation.toApproximationName @@ -45,21 +43,23 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import java.io.File +private val approximations = Approximations(emptyList()) + open class ApproximationsTest : BaseTest() { // ApproximationsTest designed to work only with applied ApproximationIndexer // So, if WithDbImmutable is used then indexing would be skipped and tests would fail - companion object : WithDb(Approximations) + companion object : WithDb(approximations) @Test fun `kotlin approximation`() { val classes = cp.findClass() val originalClassName = KotlinClass::class.qualifiedName!!.toOriginalName() - val approximation = findApproximationByOriginOrNull(originalClassName) + val approximation = approximations.findApproximationByOriginOrNull(originalClassName) assertNotNull(approximation) - assertEquals(classes.name, findOriginalByApproximationOrNull(approximation!!.toApproximationName())) + assertEquals(classes.name, approximations.findOriginalByApproximationOrNull(approximation!!.toApproximationName())) } @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") @@ -68,10 +68,10 @@ open class ApproximationsTest : BaseTest() { val classec = cp.findClass() val originalClassName = "java.lang.Integer".toOriginalName() - val approximation = findApproximationByOriginOrNull(originalClassName) + val approximation = approximations.findApproximationByOriginOrNull(originalClassName) assertNotNull(approximation) - assertEquals(classec.name, findOriginalByApproximationOrNull(approximation!!.toApproximationName())) + assertEquals(classec.name, approximations.findOriginalByApproximationOrNull(approximation!!.toApproximationName())) } @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") @@ -209,7 +209,7 @@ open class ApproximationsTest : BaseTest() { } } - assertTrue(types.none { findOriginalByApproximationOrNull(it.toApproximationName()) != null }) + assertTrue(types.none { approximations.findOriginalByApproximationOrNull(it.toApproximationName()) != null }) } @Test @@ -237,5 +237,5 @@ open class ApproximationsTest : BaseTest() { class ApproximationsSQLiteTest : ApproximationsTest() { - companion object : WithSQLiteDb(Approximations) + companion object : WithSQLiteDb(approximations) } diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsPersistenceImpl.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsPersistenceImpl.kt index ae18f0f05..ff6266dfd 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsPersistenceImpl.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/storage/ers/ErsPersistenceImpl.kt @@ -299,4 +299,4 @@ class ErsPersistenceImpl( } } } -} \ No newline at end of file +} From 0db590304e5754b92d8aa63939bb3916c2ed8622 Mon Sep 17 00:00:00 2001 From: MchKosticyn Date: Sun, 20 Jul 2025 18:27:44 +0300 Subject: [PATCH 2/5] wip --- .../jacodb/approximation/Approximations.kt | 79 +++++++++++++++---- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt index ebcc76d92..bd48a4291 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt @@ -42,6 +42,7 @@ import org.jacodb.impl.storage.jooq.tables.references.ANNOTATIONVALUES import org.jacodb.impl.storage.jooq.tables.references.CLASSES import org.jacodb.impl.storage.txn import org.jacodb.impl.types.RefKind +import org.objectweb.asm.tree.AnnotationNode import org.objectweb.asm.tree.ClassNode import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentMap @@ -69,6 +70,8 @@ class Approximations( private val originalToApproximation: ConcurrentMap = ConcurrentHashMap() private val approximationToOriginal: ConcurrentMap = ConcurrentHashMap() + private val versionMap: VersionMap = versions.associate { it.target to it.version } + override suspend fun query(classpath: JcClasspath, req: Any?): Sequence { // returns an empty sequence for now, all requests are made using // findApproximationOrNull and findOriginalByApproximation functions @@ -78,7 +81,7 @@ class Approximations( override fun newIndexer( jcdb: JcDatabase, location: RegisteredLocation - ): ByteCodeIndexer = ApproximationIndexer(originalToApproximation, approximationToOriginal) + ): ByteCodeIndexer = ApproximationIndexer(originalToApproximation, approximationToOriginal, versionMap) override fun onSignal(signal: JcSignal) { if (signal is JcSignal.BeforeIndexing) { @@ -126,7 +129,7 @@ class Approximations( val toVersion = persistence.findSymbolName( versionValues.find { toVersionId == it["nameId"] }!!["primitiveValue"]!! ) - versions.any { VersionsIntervalInfo(target, fromVersion, toVersion).matches(it) } + VersionsIntervalInfo(target, fromVersion, toVersion).matches(versionMap) } if (versionMatches) annotation to values @@ -202,6 +205,8 @@ data class VersionInfo( } } +private typealias VersionMap = Map + private data class VersionsIntervalInfo( val target: String, val fromVersion: String, @@ -211,6 +216,18 @@ private data class VersionsIntervalInfo( check(fromVersion.isVersion) check(toVersion.isVersion) } + + fun matches(versionMap: VersionMap): Boolean { + check(fromVersion.isVersion && toVersion.isVersion) + + val version = versionMap[this.target] ?: return false + + val fromVersionNumbers = fromVersion.toNumbers + val toVersionNumbers = toVersion.toNumbers + val versionNumbers = version.toNumbers + + return versionNumbers.isVersionInRange(fromVersionNumbers, toVersionNumbers) + } } /** @@ -238,23 +255,50 @@ private fun IntArray.isVersionInRange(fromVersion: IntArray, toVersion: IntArray return true } -private fun VersionsIntervalInfo.matches(versionInfo: VersionInfo): Boolean { - check(fromVersion.isVersion && toVersion.isVersion && versionInfo.version.isVersion) - - if (versionInfo.target != this.target) - return false - - val fromVersionNumbers = fromVersion.toNumbers - val toVersionNumbers = toVersion.toNumbers - val versionNumbers = versionInfo.version.toNumbers - - return versionNumbers.isVersionInRange(fromVersionNumbers, toVersionNumbers) -} - private class ApproximationIndexer( private val originalToApproximation: ConcurrentMap, - private val approximationToOriginal: ConcurrentMap + private val approximationToOriginal: ConcurrentMap, + private val versionMap: VersionMap ) : ByteCodeIndexer { + + private fun checkVersion(approximationAnnotation: AnnotationNode): Boolean { + val values = approximationAnnotation.values + val versionsNameIdx = values.indexOf("versions") + val versionsIdx = versionsNameIdx + 1 + if (versionsNameIdx == -1 || versionsIdx >= values.size) + // When `Approximate` annotation does not contain `Version` annotation, it matches any version + return true + + val versions = values[versionsIdx] as List<*> + if (versions.isEmpty()) + return true + + for (version in versions) { + version as AnnotationNode + val versionValues = version.values + val targetNameIdx = versionValues.indexOf("target") + check(targetNameIdx != -1) + val targetIdx = targetNameIdx + 1 + check(targetIdx < versionValues.size) + val target = versionValues[targetIdx] as String + val fromNameIdx = versionValues.indexOf("fromVersion") + check(fromNameIdx != -1) + val fromIdx = fromNameIdx + 1 + check(fromIdx < versionValues.size) + val from = versionValues[fromIdx] as String + val toNameIdx = versionValues.indexOf("toVersion") + check(toNameIdx != -1) + val toIdx = toNameIdx + 1 + check(toIdx < versionValues.size) + val to = versionValues[toIdx] as String + val versionIntervalInfo = VersionsIntervalInfo(target, from, to) + if (versionIntervalInfo.matches(versionMap)) + return true + } + + return false + } + override fun index(classNode: ClassNode) { val annotations = classNode.visibleAnnotations ?: return @@ -263,6 +307,9 @@ private class ApproximationIndexer( approximationAnnotationClassName in it.desc.className } ?: return + if (!checkVersion(approximationAnnotation)) + return + // Extract a name of the target class for this approximation val target = approximationAnnotation.values.filterIsInstance().single() From 59051fe5fe2ba29f403a2b78b85c0a102fd4d12f Mon Sep 17 00:00:00 2001 From: MchKosticyn Date: Sun, 20 Jul 2025 18:37:41 +0300 Subject: [PATCH 3/5] wip --- .../main/kotlin/org/jacodb/approximation/Approximations.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt index bd48a4291..ee619b802 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt @@ -90,6 +90,7 @@ class Approximations( persistence.read { context -> context.execute( sqlAction = { + // TODO: support versions context.dslContext.select(CLASSES.NAME, ANNOTATIONVALUES.CLASS_SYMBOL) .from(ANNOTATIONS) .join(CLASSES).on(ANNOTATIONS.CLASS_ID.eq(CLASSES.ID)) @@ -244,14 +245,18 @@ private val String.toNumbers: IntArray get() { /** * Checks if a version (array of 3 numbers) is within the inclusive range defined by fromVersion and toVersion (also arrays of 3 numbers). + * Example: 1.1.1 <= 1.2.0 <= 2.0.0 */ private fun IntArray.isVersionInRange(fromVersion: IntArray, toVersion: IntArray): Boolean { require(this.size == 3 && fromVersion.size == 3 && toVersion.size == 3) { "All version arrays must have size 3" } for (i in 0..2) { if (this[i] < fromVersion[i]) return false + if (this[i] > fromVersion[i]) break + } + for (i in 0..2) { if (this[i] > toVersion[i]) return false + if (this[i] < toVersion[i]) break } - return true } From a50e88c35542611db21fb14bc3414202d94b42e8 Mon Sep 17 00:00:00 2001 From: MchKosticyn Date: Tue, 22 Jul 2025 17:20:01 +0300 Subject: [PATCH 4/5] fixes --- .../jacodb/approximation/Approximations.kt | 127 +++++++++++------- 1 file changed, 76 insertions(+), 51 deletions(-) diff --git a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt index ee619b802..4bd06d5a8 100644 --- a/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt +++ b/jacodb-approximations/src/main/kotlin/org/jacodb/approximation/Approximations.kt @@ -21,6 +21,7 @@ import org.jacodb.api.jvm.JcClassExtFeature import org.jacodb.api.jvm.JcClassOrInterface import org.jacodb.api.jvm.JcClasspath import org.jacodb.api.jvm.JcDatabase +import org.jacodb.api.jvm.JcDatabasePersistence import org.jacodb.api.jvm.JcFeature import org.jacodb.api.jvm.JcField import org.jacodb.api.jvm.JcInstExtFeature @@ -30,16 +31,14 @@ import org.jacodb.api.jvm.RegisteredLocation import org.jacodb.api.jvm.cfg.JcInstList import org.jacodb.api.jvm.cfg.JcRawInst import org.jacodb.api.storage.StorageContext +import org.jacodb.api.storage.ers.Entity +import org.jacodb.api.storage.ers.EntityIterable import org.jacodb.api.storage.ers.compressed import org.jacodb.approximation.annotation.Approximate import org.jacodb.approximation.annotation.Version import org.jacodb.impl.cfg.JcInstListImpl import org.jacodb.impl.fs.className -import org.jacodb.impl.storage.dslContext import org.jacodb.impl.storage.execute -import org.jacodb.impl.storage.jooq.tables.references.ANNOTATIONS -import org.jacodb.impl.storage.jooq.tables.references.ANNOTATIONVALUES -import org.jacodb.impl.storage.jooq.tables.references.CLASSES import org.jacodb.impl.storage.txn import org.jacodb.impl.types.RefKind import org.objectweb.asm.tree.AnnotationNode @@ -61,7 +60,7 @@ import java.util.concurrent.ConcurrentMap * See [JcDatabase.awaitBackgroundJobs]. */ class Approximations( - private val versions: List + versions: List ) : JcFeature, JcClassExtFeature, JcInstExtFeature { private val instSubstitutorForApproximations = InstSubstitutorForApproximations(this) @@ -83,6 +82,28 @@ class Approximations( location: RegisteredLocation ): ByteCodeIndexer = ApproximationIndexer(originalToApproximation, approximationToOriginal, versionMap) + private val Entity.annotationNameId: Long? + get() = getCompressed("nameId") + + private val Entity.annotationValueNameId: Long? + get() = getCompressedBlob("nameId") + + private fun EntityIterable.annotationPrimitiveValueByName(valueNameId: Long): Long? { + val value = find { valueNameId == it.getCompressedBlob("nameId") } + return value?.getCompressedBlob("primitiveValue") + } + + private fun EntityIterable.annotationStringValueByName( + persistence: JcDatabasePersistence, + valueNameId: Long + ): String? { + val valueId = annotationPrimitiveValueByName(valueNameId) + if (valueId != null) + return persistence.findSymbolName(valueId) + + return null + } + override fun onSignal(signal: JcSignal) { if (signal is JcSignal.BeforeIndexing) { val persistence = signal.jcdb.persistence @@ -90,17 +111,17 @@ class Approximations( persistence.read { context -> context.execute( sqlAction = { - // TODO: support versions - context.dslContext.select(CLASSES.NAME, ANNOTATIONVALUES.CLASS_SYMBOL) - .from(ANNOTATIONS) - .join(CLASSES).on(ANNOTATIONS.CLASS_ID.eq(CLASSES.ID)) - .join(ANNOTATIONVALUES).on(ANNOTATIONVALUES.ANNOTATION_ID.eq(ANNOTATIONS.ID)) - .where( - ANNOTATIONS.ANNOTATION_NAME.eq(approxSymbol).and( - ANNOTATIONVALUES.NAME.eq("value") - ) - ) - .fetch().asSequence().map { record -> record.value1() to record.value2() } + TODO("support versions for SQL persistence") +// context.dslContext.select(CLASSES.NAME, ANNOTATIONVALUES.CLASS_SYMBOL) +// .from(ANNOTATIONS) +// .join(CLASSES).on(ANNOTATIONS.CLASS_ID.eq(CLASSES.ID)) +// .join(ANNOTATIONVALUES).on(ANNOTATIONVALUES.ANNOTATION_ID.eq(ANNOTATIONS.ID)) +// .where( +// ANNOTATIONS.ANNOTATION_NAME.eq(approxSymbol).and( +// ANNOTATIONVALUES.NAME.eq("value") +// ) +// ) +// .fetch().asSequence().map { record -> record.value1() to record.value2() } }, noSqlAction = { val valueId = persistence.findSymbolId("value") @@ -113,23 +134,22 @@ class Approximations( .filter { it.getCompressedBlob("refKind") == RefKind.CLASS.ordinal } .mapNotNull { annotation -> val values = annotation.getLinks("values") - val versionsValue = values.filterTo(mutableListOf()) { versionsId == it["nameId"] } + val versionsValue = values.filterTo(mutableListOf()) { + versionsId == it.annotationValueNameId + } if (versionsValue.isEmpty()) return@mapNotNull annotation to values val versionMatches = versionsValue.any { versionValue -> val versionAnnotation = versionValue.getLink("refAnnotation") - check(versionSymbol == versionAnnotation["nameId"]) + check(versionSymbol == versionAnnotation.annotationNameId) val versionValues = versionAnnotation.getLinks("values") - val target = persistence.findSymbolName( - versionValues.find { targetId == it["nameId"] }!!["primitiveValue"]!! - ) - val fromVersion = persistence.findSymbolName( - versionValues.find { fromVersionId == it["nameId"] }!!["primitiveValue"]!! - ) - val toVersion = persistence.findSymbolName( - versionValues.find { toVersionId == it["nameId"] }!!["primitiveValue"]!! - ) + val target = versionValues.annotationStringValueByName(persistence, targetId) + ?: error("unable to find `target` value in `Version` annotation") + val fromVersion = versionValues.annotationStringValueByName(persistence, fromVersionId) + ?: error("unable to find `fromVersion` value in `Version` annotation") + val toVersion = versionValues.annotationStringValueByName(persistence, toVersionId) + ?: error("unable to find `toVersion` value in `Version` annotation") VersionsIntervalInfo(target, fromVersion, toVersion).matches(versionMap) } if (versionMatches) @@ -266,38 +286,43 @@ private class ApproximationIndexer( private val versionMap: VersionMap ) : ByteCodeIndexer { + private fun annotationValueByName(versionValues: List, name: String): Any? { + val valueNameIdx = versionValues.indexOf(name) + if (valueNameIdx == -1) + return null + + val valueIdx = valueNameIdx + 1 + if (valueIdx >= versionValues.size) + return null + + return versionValues[valueIdx] + } + + private fun annotationStringValueByName(versionValues: List, name: String): String? { + return annotationValueByName(versionValues, name) as? String + } + + private val AnnotationNode.versionIntervalInfo: VersionsIntervalInfo get() { + val target = annotationStringValueByName(values, "target") + ?: error("unable to find `target` value in `Version` annotation") + val from = annotationStringValueByName(values, "fromVersion") + ?: error("unable to find `target` value in `Version` annotation") + val to = annotationStringValueByName(values, "toVersion") + ?: error("unable to find `target` value in `Version` annotation") + return VersionsIntervalInfo(target, from, to) + } + private fun checkVersion(approximationAnnotation: AnnotationNode): Boolean { - val values = approximationAnnotation.values - val versionsNameIdx = values.indexOf("versions") - val versionsIdx = versionsNameIdx + 1 - if (versionsNameIdx == -1 || versionsIdx >= values.size) + val versions = annotationValueByName(approximationAnnotation.values, "versions") as? List<*> // When `Approximate` annotation does not contain `Version` annotation, it matches any version - return true + ?: return true - val versions = values[versionsIdx] as List<*> if (versions.isEmpty()) return true for (version in versions) { version as AnnotationNode - val versionValues = version.values - val targetNameIdx = versionValues.indexOf("target") - check(targetNameIdx != -1) - val targetIdx = targetNameIdx + 1 - check(targetIdx < versionValues.size) - val target = versionValues[targetIdx] as String - val fromNameIdx = versionValues.indexOf("fromVersion") - check(fromNameIdx != -1) - val fromIdx = fromNameIdx + 1 - check(fromIdx < versionValues.size) - val from = versionValues[fromIdx] as String - val toNameIdx = versionValues.indexOf("toVersion") - check(toNameIdx != -1) - val toIdx = toNameIdx + 1 - check(toIdx < versionValues.size) - val to = versionValues[toIdx] as String - val versionIntervalInfo = VersionsIntervalInfo(target, from, to) - if (versionIntervalInfo.matches(versionMap)) + if (version.versionIntervalInfo.matches(versionMap)) return true } From 8ddfd11226d40d50bc44d6ae0ce2ea04e9574154 Mon Sep 17 00:00:00 2001 From: MchKosticyn Date: Wed, 23 Jul 2025 16:37:17 +0300 Subject: [PATCH 5/5] [test] disabled sql approximations test --- .../test/kotlin/org/jacodb/approximations/ApproximationsTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt b/jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt index 3c3fdd6b9..74d7461ed 100644 --- a/jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt +++ b/jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt @@ -40,6 +40,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import java.io.File @@ -235,6 +236,7 @@ open class ApproximationsTest : BaseTest() { } } +@Disabled("support approximation versions for SQL persistence") class ApproximationsSQLiteTest : ApproximationsTest() { companion object : WithSQLiteDb(approximations)