From 56c217033b37d39d5aaab617722e06dbe2545c3c Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Wed, 19 Oct 2022 19:42:06 +0800 Subject: [PATCH 1/4] Add: dynamic invoke processing for string concatenation --- build.gradle.kts | 4 +- utbot-framework-test/build.gradle | 2 +- .../utbot/examples/strings/StringConcat.java | 79 ++++++++++++ .../examples/strings/StringConcatTest.kt | 114 +++++++++++++++++ .../overrides/strings/UtStringBuffer.java | 10 +- .../overrides/strings/UtStringBuilder.java | 10 +- .../utbot/engine/ConstructedSootMethods.kt | 121 ++++++++++++++++++ .../org/utbot/engine/DynamicInvokeResolver.kt | 114 +++++++++++++++++ .../kotlin/org/utbot/engine/JimpleCreation.kt | 19 ++- .../main/kotlin/org/utbot/engine/Traverser.kt | 22 +++- 10 files changed, 471 insertions(+), 24 deletions(-) create mode 100644 utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java create mode 100644 utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringConcatTest.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/DynamicInvokeResolver.kt diff --git a/build.gradle.kts b/build.gradle.kts index 88722424cb..0f9882be61 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,14 +41,14 @@ allprojects { withType { kotlinOptions { jvmTarget = "1.8" - freeCompilerArgs = freeCompilerArgs + listOf("-Xallow-result-return-type", "-Xsam-conversions=class") + freeCompilerArgs = freeCompilerArgs + listOf("-Xallow-result-return-type", "-Xsam-conversions=class", "-Xcontext-receivers") allWarningsAsErrors = false } } compileTestKotlin { kotlinOptions { jvmTarget = "1.8" - freeCompilerArgs = freeCompilerArgs + listOf("-Xallow-result-return-type", "-Xsam-conversions=class") + freeCompilerArgs = freeCompilerArgs + listOf("-Xallow-result-return-type", "-Xsam-conversions=class", "-Xcontext-receivers") allWarningsAsErrors = false } } diff --git a/utbot-framework-test/build.gradle b/utbot-framework-test/build.gradle index 23676e0c38..320f7fe2d9 100644 --- a/utbot-framework-test/build.gradle +++ b/utbot-framework-test/build.gradle @@ -6,7 +6,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { } tasks.withType(JavaCompile) { - sourceCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } diff --git a/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java b/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java new file mode 100644 index 0000000000..ea05847978 --- /dev/null +++ b/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java @@ -0,0 +1,79 @@ +package org.utbot.examples.strings; + +import org.utbot.api.mock.UtMock; + +class Test { + int x; + + @Override + public String toString() { + if (x == 42) { + throw new IllegalArgumentException(); + } + return "x = " + x; + } +} + +public class StringConcat { + String str; + public String concatArguments(String a, String b, String c) { + return a + b + c; + } + + public int concatWithConstants(String a) { + String res = '<' + a + '>'; + + if (res.equals("")) { + return 1; + } + + if (res.equals("")) { + return 2; + } + + if (a == null) { + return 3; + } + + return 4; + } + + public String concatWithPrimitives(String a) { + return a + '#' + 42 + 53.0; + } + + public String exceptionInToString(Test t) { + return "Test: " + t + "!"; + } + + public String concatWithField(String a) { + return a + str + '#'; + } + + public int concatWithPrimitiveWrappers(Integer b, char c) { + String res = "" + b + c; + + if (res.endsWith("42")) { + return 1; + } + return 2; + } + + public int sameConcat(String a, String b) { + UtMock.assume(a != null && b != null); + + String res1 = '!' + a + '#'; + String res2 = '!' + b + '#'; + + if (res1.equals(res2)) { + return 0; + } else { + return 1; + } + } + + public String concatStrangeSymbols() { + return "\u0000" + '#' + '\u0001' + "!\u0002" + "@\u0012\t"; + } +} + diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringConcatTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringConcatTest.kt new file mode 100644 index 0000000000..232ba02c99 --- /dev/null +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringConcatTest.kt @@ -0,0 +1,114 @@ +package org.utbot.examples.strings + +import org.junit.jupiter.api.Test +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.testcheckers.eq +import org.utbot.testcheckers.withoutConcrete +import org.utbot.tests.infrastructure.* + +class StringConcatTest : UtValueTestCaseChecker( + testClass = StringConcat::class, + testCodeGeneration = true, + pipelines = listOf( + TestLastStage(CodegenLanguage.JAVA), + TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) + ) +) { + @Test + fun testConcatArguments() { + withoutConcrete { + check( + StringConcat::concatArguments, + eq(1), + { a, b, c, r -> "$a$b$c" == r } + ) + } + } + + @Test + fun testConcatWithConstants() { + withoutConcrete { + check( + StringConcat::concatWithConstants, + eq(4), + { a, r -> a == "head" && r == 1 }, + { a, r -> a == "body" && r == 2 }, + { a, r -> a == null && r == 3 }, + { a, r -> a != "head" && a != "body" && a != null && r == 4 }, + ) + } + } + + @Test + fun testConcatWithPrimitives() { + withoutConcrete { + check( + StringConcat::concatWithPrimitives, + eq(1), + { a, r -> "$a#4253.0" == r} + ) + } + } + + @Test + fun testExceptionInToString() { + withoutConcrete { + checkWithException( + StringConcat::exceptionInToString, + ignoreExecutionsNumber, + { t, r -> t.x == 42 && r.isException() }, + { t, r -> t.x != 42 && r.getOrThrow() == "Test: x = ${t.x}!" }, + coverage = DoNotCalculate + ) + } + } + + @Test + fun testConcatWithField() { + withoutConcrete { + checkWithThis( + StringConcat::concatWithField, + eq(1), + { o, a, r -> "$a${o.str}#" == r } + ) + } + } + + @Test + fun testConcatWithPrimitiveWrappers() { + withoutConcrete { + check( + StringConcat::concatWithPrimitiveWrappers, + ignoreExecutionsNumber, + { b, c, r -> b.toString().endsWith("4") && c == '2' && r == 1 }, + { _, c, r -> !c.toString().endsWith("42") && r == 2 }, + coverage = DoNotCalculate + ) + } + } + + @Test + fun testSameConcat() { + withoutConcrete { + check( + StringConcat::sameConcat, + ignoreExecutionsNumber, + { a, b, r -> a == b && r == 0 }, + { a, b, r -> a != b && r == 1 }, + coverage = DoNotCalculate + ) + } + } + + @Test + fun testConcatStrangeSymbols() { + withoutConcrete { + check( + StringConcat::concatStrangeSymbols, + eq(1), + { r -> r == "\u0000#\u0001!\u0002@\u0012\t" } + ) + } + } + +} \ No newline at end of file diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/strings/UtStringBuffer.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/strings/UtStringBuffer.java index 7931fc166c..d778a49ce3 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/strings/UtStringBuffer.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/strings/UtStringBuffer.java @@ -450,7 +450,7 @@ public StringBuffer insert(int dstOffset, CharSequence s, int start, int end) { } public StringBuffer insert(int offset, boolean b) { - return insert(offset, String.valueOf(b)); + return insert(offset, Boolean.toString(b)); } public StringBuffer insert(int offset, char c) { @@ -463,19 +463,19 @@ public StringBuffer insert(int offset, char c) { } public StringBuffer insert(int offset, int i) { - return insert(offset, String.valueOf(i)); + return insert(offset, Integer.toString(i)); } public StringBuffer insert(int offset, long l) { - return insert(offset, String.valueOf(l)); + return insert(offset, Long.toString(l)); } public StringBuffer insert(int offset, float f) { - return insert(offset, String.valueOf(f)); + return insert(offset, Float.toString(f)); } public StringBuffer insert(int offset, double d) { - return insert(offset, String.valueOf(d)); + return insert(offset, Double.toString(d)); } public int indexOf(String str) { diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/strings/UtStringBuilder.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/strings/UtStringBuilder.java index 4e06aaf24d..b627fa2aa1 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/strings/UtStringBuilder.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/strings/UtStringBuilder.java @@ -451,7 +451,7 @@ public StringBuilder insert(int dstOffset, CharSequence s, int start, int end) { } public StringBuilder insert(int offset, boolean b) { - return insert(offset, String.valueOf(b)); + return insert(offset, Boolean.toString(b)); } public StringBuilder insert(int offset, char c) { @@ -464,19 +464,19 @@ public StringBuilder insert(int offset, char c) { } public StringBuilder insert(int offset, int i) { - return insert(offset, String.valueOf(i)); + return insert(offset, Integer.toString(i)); } public StringBuilder insert(int offset, long l) { - return insert(offset, String.valueOf(l)); + return insert(offset, Long.toString(l)); } public StringBuilder insert(int offset, float f) { - return insert(offset, String.valueOf(f)); + return insert(offset, Float.toString(f)); } public StringBuilder insert(int offset, double d) { - return insert(offset, String.valueOf(d)); + return insert(offset, Double.toString(d)); } public int indexOf(String str) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstructedSootMethods.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstructedSootMethods.kt index 1bc3f9aa5e..79a7e90c38 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstructedSootMethods.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstructedSootMethods.kt @@ -4,10 +4,13 @@ import soot.ArrayType import soot.IntType import soot.PrimType import soot.RefType +import soot.Scene +import soot.SootClass import soot.SootMethod import soot.Type import soot.Unit import soot.VoidType +import soot.jimple.StringConstant import soot.jimple.internal.JArrayRef import soot.jimple.internal.JAssignStmt import soot.jimple.internal.JNewMultiArrayExpr @@ -169,4 +172,122 @@ fun unfoldMultiArrayExpr(assignStmt: JAssignStmt): ExceptionalUnitGraph { createSootMethod(methodName, jimpleLocalsForSizes.map { it.type }, multiArray.type, sootClass, graphBody) return ExceptionalUnitGraph(graphBody) +} + + +fun makeSootConcat(declaringClass: SootClass, recipe: String, paramTypes: List, constants: List): SootMethod { + val paramsHashcode = paramTypes.hashCode() + val constantsHashcode = constants.hashCode() + val name = "utbot\$concatenateStringWithRecipe<$recipe>AndParams<$paramsHashcode>AndConstants<$constantsHashcode>" + + + // check if it is already defined + val cachedMethod = declaringClass.getMethodByNameUnsafe(name) + if (cachedMethod != null) { + return cachedMethod + } + + var objectCounter = 0 + + val units = mutableListOf() + val locals = mutableSetOf() + + + // initialize parameter locals + val parameterLocals = paramTypes.map { + JimpleLocal("r${objectCounter++}", it) + } + locals += parameterLocals + + + val parameterRefs = paramTypes.mapIndexed { idx, type -> parameterRef(type, idx) } + val identityStmts = parameterLocals.zip(parameterRefs) { local, ref -> identityStmt(local, ref) } + units += identityStmts + + + // initialize string builder + val sbSootClass = Scene.v().getSootClass("java.lang.StringBuilder") + + + val sb = JimpleLocal("\$r${objectCounter++}", sbSootClass.type) + locals += sb + + val newSbExpr = newNewExpr(sbSootClass.type) + units += assignStmt(sb, newSbExpr) + val initSootMethod = sbSootClass.getMethod("", emptyList()) + val initSbExpr = initSootMethod.toSpecialInvokeExpr(sb) + units += initSbExpr.toInvokeStmt() + + + var paramPointer = 0 // pointer to dynamic parameter + var constantPointer = 0 // pointer to constant list + var delayedString = "" // string which is build of sequent values from [constants] + + // helper function for appending constants to delayedString + fun appendDelayedStringIfNotEmpty() { + if (delayedString.isEmpty()) { + return + } + + val type = STRING_TYPE + + val appendSootMethod = sbSootClass.getMethod("append", listOf(type), sbSootClass.type) + + val invokeStringBuilderAppendStmt = appendSootMethod.toVirtualInvokeExpr(sb, StringConstant.v(delayedString)) + units += invokeStringBuilderAppendStmt.toInvokeStmt() + + delayedString = "" + } + + // recipe parsing and building the result string + for (c in recipe) { + when (c) { + '\u0001' -> { + appendDelayedStringIfNotEmpty() + + val local = parameterLocals[paramPointer++] + val type = local.type + + val appendSootMethod = sbSootClass.getMethodUnsafe("append", listOf(type), sbSootClass.type) + ?: sbSootClass.getMethod("append", listOf(OBJECT_TYPE), sbSootClass.type) + + val invokeStringBuilderAppendStmt = appendSootMethod.toVirtualInvokeExpr(sb, local) + units += invokeStringBuilderAppendStmt.toInvokeStmt() + } + '\u0002' -> { + appendDelayedStringIfNotEmpty() + + val const = constants[constantPointer++] + val type = STRING_TYPE + + val appendSootMethod = sbSootClass.getMethod("append", listOf(type), sbSootClass.type) + + val stringBuilderAppendExpr = appendSootMethod.toVirtualInvokeExpr(sb, StringConstant.v(const)) + units += stringBuilderAppendExpr.toInvokeStmt() + } + else -> { + delayedString += c + } + } + } + appendDelayedStringIfNotEmpty() + + // receiving result + val toStringSootMethod = sbSootClass.getMethodByName("toString") + + val result = JimpleLocal("\$r${objectCounter++}", STRING_TYPE) + locals += result + + val stringBuilderToStringExpr = toStringSootMethod.toVirtualInvokeExpr(sb) + val assignStmt = assignStmt(result, stringBuilderToStringExpr) + units += assignStmt + + val returnStmt = returnStatement(result) + units += returnStmt + + + val body = units.toGraphBody() + body.locals.addAll(locals) + + return createSootMethod(name, paramTypes, STRING_TYPE, declaringClass, body) } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/DynamicInvokeResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/DynamicInvokeResolver.kt new file mode 100644 index 0000000000..0cf1fd6a21 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/DynamicInvokeResolver.kt @@ -0,0 +1,114 @@ +package org.utbot.engine + +import soot.SootClass +import soot.Type +import soot.jimple.StringConstant +import soot.jimple.internal.JDynamicInvokeExpr + + +/** + * Map of supported boostrap function names to their resolvers + */ +private val defaultProcessors = mapOf( + // referencing by a name instead of a signature, because we believe, that all bootstrap methods have unique names + StringMakeConcatResolver.MAKE_CONCAT_METHOD_NAME to StringMakeConcatResolver, + StringMakeConcatResolver.MAKE_CONCAT_WITH_CONSTANTS_NAME to StringMakeConcatResolver +) + +interface DynamicInvokeResolver { + /** + * @return a successfully resolved [Invocation] or `null`, if it can't be resolved with this [DynamicInvokeResolver] + */ + context(Traverser) + fun TraversalContext.resolveDynamicInvoke(invokeExpr: JDynamicInvokeExpr): Invocation? +} + +/** + * Composes several [DynamicInvokeResolver]s into a single [DynamicInvokeResolver] based on bootstrap method names. + */ +class DelegatingDynamicInvokeResolver( + private val bootstrapMethodToProcessor: Map = defaultProcessors +) : DynamicInvokeResolver { + context(Traverser) + override fun TraversalContext.resolveDynamicInvoke(invokeExpr: JDynamicInvokeExpr): Invocation? { + val processor = bootstrapMethodToProcessor[invokeExpr.bootstrapMethod.name] ?: return null + return with(processor) { resolveDynamicInvoke(invokeExpr) } + } + +} + +/** + * Implements the logic of [java.lang.invoke.StringConcatFactory]. + * + * This is useful when analyzing only Java 9+ bytecode. + */ +object StringMakeConcatResolver : DynamicInvokeResolver { + const val STRING_CONCAT_LIBRARY_NAME = "java.lang.invoke.StringConcatFactory" + const val MAKE_CONCAT_METHOD_NAME = "makeConcat" + const val MAKE_CONCAT_WITH_CONSTANTS_NAME = "makeConcatWithConstants" + + /** + * Implements the logic of [java.lang.invoke.StringConcatFactory.makeConcat] and + * [java.lang.invoke.StringConcatFactory.makeConcatWithConstants] in a symbolic way. + * + * - Generates [soot.SootMethod] performing string concatenation if it has not generated yet + * - Links it + * + * Check out [java.lang.invoke.StringConcatFactory] documentation for more details. + * + * @return [Invocation] with a single generated [soot.SootMethod], which represents a concatenating function. + */ + context(Traverser) + override fun TraversalContext.resolveDynamicInvoke(invokeExpr: JDynamicInvokeExpr): Invocation { + val bootstrapMethod = invokeExpr.bootstrapMethod + require(bootstrapMethod.declaringClass.name == STRING_CONCAT_LIBRARY_NAME) + + val bootstrapArguments = invokeExpr.bootstrapArgs.map { arg -> + requireNotNull((arg as? StringConstant)?.value) { "StringConstant expected, but got ${arg::class.java}"} + } + + val (recipe, constants) = when (bootstrapMethod.name) { + MAKE_CONCAT_METHOD_NAME -> { + "\u0001".repeat(invokeExpr.args.size) to emptyList() + } + MAKE_CONCAT_WITH_CONSTANTS_NAME -> { + val recipe = requireNotNull(bootstrapArguments.firstOrNull()) { "At least one bootstrap argument expected" } + recipe to bootstrapArguments.drop(1) + } + else -> error("Unknown bootstrap method for string concatenation!") + } + + val declaringClass = environment.method.declaringClass + val dynamicParameterTypes = invokeExpr.methodRef.parameterTypes + + val parameters = resolveParameters(invokeExpr.args, dynamicParameterTypes) + return makeInvocation(declaringClass, recipe, dynamicParameterTypes, constants, parameters) + } + + private fun makeInvocation( + declaringClass: SootClass, + recipe: String, + dynamicParameterTypes: MutableList, + constants: List, + parameters: List + ): Invocation { + val sootMethod = makeSootConcat( + declaringClass, + recipe, + dynamicParameterTypes, + constants + ) + + val invocationTarget = InvocationTarget( + null, + sootMethod + ) + + return Invocation( + null, + sootMethod, + parameters, + invocationTarget + ) + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt index 4c7887eab9..ba80310f8d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt @@ -1,5 +1,8 @@ package org.utbot.engine +import soot.Local +import soot.Modifier +import soot.RefType import soot.SootClass import soot.SootMethod import soot.Type @@ -17,13 +20,20 @@ import soot.jimple.InvokeStmt import soot.jimple.Jimple import soot.jimple.JimpleBody import soot.jimple.NewArrayExpr +import soot.jimple.NewExpr import soot.jimple.ParameterRef import soot.jimple.ReturnStmt import soot.jimple.ReturnVoidStmt +import soot.jimple.SpecialInvokeExpr import soot.jimple.StaticInvokeExpr +import soot.jimple.VirtualInvokeExpr fun SootMethod.toStaticInvokeExpr(): StaticInvokeExpr = Jimple.v().newStaticInvokeExpr(this.makeRef()) +fun SootMethod.toVirtualInvokeExpr(local: Local, vararg values: Value): VirtualInvokeExpr = Jimple.v().newVirtualInvokeExpr(local, this.makeRef(), *values) + +fun SootMethod.toSpecialInvokeExpr(local: Local, vararg values: Value): SpecialInvokeExpr = Jimple.v().newSpecialInvokeExpr(local, this.makeRef(), *values) + fun InvokeExpr.toInvokeStmt(): InvokeStmt = Jimple.v().newInvokeStmt(this) fun returnVoidStatement(): ReturnVoidStmt = Jimple.v().newReturnVoidStmt() @@ -34,6 +44,8 @@ fun parameterRef(type: Type, number: Int): ParameterRef = Jimple.v().newParamete fun identityStmt(local: Value, identityRef: Value): IdentityStmt = Jimple.v().newIdentityStmt(local, identityRef) +fun newNewExpr(type: RefType): NewExpr = Jimple.v().newNewExpr(type) + fun newArrayExpr(type: Type, size: Value): NewArrayExpr = Jimple.v().newNewArrayExpr(type, size) fun assignStmt(variable: Value, rValue: Value): AssignStmt = Jimple.v().newAssignStmt(variable, rValue) @@ -60,11 +72,10 @@ fun createSootMethod( argsTypes: List, returnType: Type, declaringClass: SootClass, - graphBody: JimpleBody -) = SootMethod(name, argsTypes, returnType) + graphBody: JimpleBody, + isStatic: Boolean = true +) = SootMethod(name, argsTypes, returnType, (if (isStatic) Modifier.STATIC else 0)) .also { - it.declaringClass = declaringClass declaringClass.addMethod(it) - graphBody.method = it it.activeBody = graphBody } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index 28d83e6ce4..57667b508d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -250,6 +250,8 @@ class Traverser( } internal fun findNewAddr() = findNewAddr(environment.state.isInsideStaticInitializer).also { touchAddress(it) } + private val dynamicInvokeResolver: DynamicInvokeResolver = DelegatingDynamicInvokeResolver() + // Counter used for a creation symbolic results of "hashcode" and "equals" methods. private var equalsCounter = 0 private var hashcodeCounter = 0 @@ -1009,7 +1011,6 @@ class Traverser( private fun updateGenericTypeInfo(identityRef: IdentityRef, value: ReferenceValue) { val callable = methodUnderTest.executable val kCallable = ::updateGenericTypeInfo - val test = kCallable.instanceParameter?.type?.javaType val type = if (identityRef is ThisRef) { // TODO: for ThisRef both methods don't return parameterized type if (methodUnderTest.isConstructor) { @@ -1888,7 +1889,7 @@ class Traverser( return created } - private fun TraversalContext.resolveParameters(parameters: List, types: List) = + fun TraversalContext.resolveParameters(parameters: List, types: List) = parameters.zip(types).map { (value, type) -> resolve(value, type) } private fun applyPreferredConstraints(value: SymbolicValue) { @@ -2503,12 +2504,19 @@ class Traverser( } private fun TraversalContext.dynamicInvoke(invokeExpr: JDynamicInvokeExpr): List { - workaround(HACK) { - // The engine does not yet support JDynamicInvokeExpr, so switch to concrete execution if we encounter it - offerState(environment.state.withLabel(StateLabel.CONCRETE)) - queuedSymbolicStateUpdates += UtFalse.asHardConstraint() - return emptyList() + val invocation = with(dynamicInvokeResolver) { resolveDynamicInvoke(invokeExpr) } + + if (invocation == null) { + workaround(HACK) { + logger.warn { "Marking state as a concrete, because of an unknown dynamic invoke instruction: $invokeExpr" } + // The engine does not yet support JDynamicInvokeExpr, so switch to concrete execution if we encounter it + offerState(environment.state.withLabel(StateLabel.CONCRETE)) + queuedSymbolicStateUpdates += UtFalse.asHardConstraint() + return emptyList() + } } + + return commonInvokePart(invocation) } /** From e414253041147a5ae002999a021ca099f3acfb79 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Fri, 21 Oct 2022 17:09:58 +0800 Subject: [PATCH 2/4] Fix: CE --- .../utbot/examples/strings/StringConcat.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java b/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java index ea05847978..727411663a 100644 --- a/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java +++ b/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java @@ -2,19 +2,20 @@ import org.utbot.api.mock.UtMock; -class Test { - int x; - @Override - public String toString() { - if (x == 42) { - throw new IllegalArgumentException(); +public class StringConcat { + static class Test { + public int x; + + @Override + public String toString() { + if (x == 42) { + throw new IllegalArgumentException(); + } + return "x = " + x; } - return "x = " + x; } -} -public class StringConcat { String str; public String concatArguments(String a, String b, String c) { return a + b + c; From d390c58f2b2d5c7fc92a0dcae9ec225b52793d44 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Fri, 21 Oct 2022 17:14:57 +0800 Subject: [PATCH 3/4] Fix: review comments --- .../org/utbot/examples/strings/StringConcat.java | 2 +- .../org/utbot/engine/ConstructedSootMethods.kt | 13 ++++--------- .../org/utbot/engine/DynamicInvokeResolver.kt | 4 ++-- .../main/kotlin/org/utbot/engine/JimpleCreation.kt | 2 +- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java b/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java index 727411663a..771340c2ae 100644 --- a/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java +++ b/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java @@ -4,7 +4,7 @@ public class StringConcat { - static class Test { + public static class Test { public int x; @Override diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstructedSootMethods.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstructedSootMethods.kt index 79a7e90c38..5cd7db37d3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstructedSootMethods.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstructedSootMethods.kt @@ -223,17 +223,15 @@ fun makeSootConcat(declaringClass: SootClass, recipe: String, paramTypes: List { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/DynamicInvokeResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/DynamicInvokeResolver.kt index 0cf1fd6a21..4c07e8d485 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/DynamicInvokeResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/DynamicInvokeResolver.kt @@ -100,12 +100,12 @@ object StringMakeConcatResolver : DynamicInvokeResolver { ) val invocationTarget = InvocationTarget( - null, + instance = null, sootMethod ) return Invocation( - null, + instance = null, sootMethod, parameters, invocationTarget diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt index ba80310f8d..791d78f55d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt @@ -74,7 +74,7 @@ fun createSootMethod( declaringClass: SootClass, graphBody: JimpleBody, isStatic: Boolean = true -) = SootMethod(name, argsTypes, returnType, (if (isStatic) Modifier.STATIC else 0)) +) = SootMethod(name, argsTypes, returnType, if (isStatic) Modifier.STATIC else 0) .also { declaringClass.addMethod(it) it.activeBody = graphBody From f0c07428e82f535f050f31a8f4a63fb9e7add42e Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Thu, 27 Oct 2022 11:48:50 +0800 Subject: [PATCH 4/4] Refactor: modifier utils for ClassId, ExecutableId, FieldId; Remove: field strategies --- .github/workflows/framework-tests-matrix.json | 2 +- .../org/utbot/framework/plugin/api/Api.kt | 153 ++++++++---------- .../plugin/api/impl/FieldIdStrategies.kt | 90 ----------- .../utbot/framework/plugin/api/util/IdUtil.kt | 20 --- .../framework/plugin/api/util/ModifierUtil.kt | 98 +++++++++++ .../{strings => strings11}/StringConcat.java | 2 +- .../StringConcatTest.kt | 2 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 1 + .../assemble/AssembleModelGenerator.kt | 5 +- .../framework/codegen/model/CodeGenerator.kt | 1 + .../tree/CgCallableAccessManager.kt | 11 +- .../constructor/tree/CgMethodConstructor.kt | 1 + .../codegen/model/util/ClassIdUtil.kt | 4 + .../codegen/model/util/FieldIdUtil.kt | 5 + .../codegen/model/visitor/CgJavaRenderer.kt | 4 + .../codegen/model/visitor/CgKotlinRenderer.kt | 4 + .../concrete/UtExecutionInstrumentation.kt | 5 - .../TestCodeGeneratorPipeline.kt | 36 +++-- ...CollectionWithModificationModelProvider.kt | 1 + .../fuzzer/providers/ObjectModelProvider.kt | 2 + .../instrumentation/InvokeInstrumentation.kt | 1 + 21 files changed, 218 insertions(+), 230 deletions(-) delete mode 100644 utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/impl/FieldIdStrategies.kt create mode 100644 utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/ModifierUtil.kt rename utbot-framework-test/src/main/java/org/utbot/examples/{strings => strings11}/StringConcat.java (97%) rename utbot-framework-test/src/test/kotlin/org/utbot/examples/{strings => strings11}/StringConcatTest.kt (98%) diff --git a/.github/workflows/framework-tests-matrix.json b/.github/workflows/framework-tests-matrix.json index 67448bca1c..fadcf414ad 100644 --- a/.github/workflows/framework-tests-matrix.json +++ b/.github/workflows/framework-tests-matrix.json @@ -26,7 +26,7 @@ }, { "PART_NAME": "examples-part3", - "TESTS_TO_RUN": "--tests \"org.utbot.examples.primitives.*\" --tests \"org.utbot.examples.recursion.*\" --tests \"org.utbot.examples.statics.substitution.*\" --tests \"org.utbot.examples.stdlib.*\" --tests \"org.utbot.examples.strings.*\" --tests \"org.utbot.examples.structures.*\" --tests \"org.utbot.examples.thirdparty.numbers.*\" --tests \"org.utbot.examples.types.*\" --tests \"org.utbot.examples.unsafe.*\" --tests \"org.utbot.examples.wrappers.*\"" + "TESTS_TO_RUN": "--tests \"org.utbot.examples.primitives.*\" --tests \"org.utbot.examples.recursion.*\" --tests \"org.utbot.examples.statics.substitution.*\" --tests \"org.utbot.examples.stdlib.*\" --tests \"org.utbot.examples.strings11.*\" --tests \"org.utbot.examples.strings.*\" --tests \"org.utbot.examples.structures.*\" --tests \"org.utbot.examples.thirdparty.numbers.*\" --tests \"org.utbot.examples.types.*\" --tests \"org.utbot.examples.unsafe.*\" --tests \"org.utbot.examples.wrappers.*\"" }, { "PART_NAME": "examples-lists", diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 0c0f5efe08..af70771bcc 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -8,11 +8,11 @@ package org.utbot.framework.plugin.api +import org.utbot.common.FileUtil import org.utbot.common.isDefaultValue import org.utbot.common.withToStringThreadLocalReentrancyGuard import org.utbot.framework.UtSettings -import org.utbot.framework.plugin.api.impl.FieldIdReflectionStrategy -import org.utbot.framework.plugin.api.impl.FieldIdSootStrategy +import org.utbot.framework.plugin.api.util.ModifierFactory import org.utbot.framework.plugin.api.util.booleanClassId import org.utbot.framework.plugin.api.util.byteClassId import org.utbot.framework.plugin.api.util.charClassId @@ -24,7 +24,9 @@ import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isPrimitive +import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.jClass +import org.utbot.framework.plugin.api.util.jField import org.utbot.framework.plugin.api.util.longClassId import org.utbot.framework.plugin.api.util.method import org.utbot.framework.plugin.api.util.primitiveTypeJvmNameOrNull @@ -33,14 +35,24 @@ import org.utbot.framework.plugin.api.util.shortClassId import org.utbot.framework.plugin.api.util.supertypeOfAnonymousClass import org.utbot.framework.plugin.api.util.toReferenceTypeBytecodeSignature import org.utbot.framework.plugin.api.util.voidClassId -import soot.* +import soot.ArrayType +import soot.BooleanType +import soot.ByteType +import soot.CharType +import soot.DoubleType +import soot.FloatType +import soot.IntType +import soot.LongType +import soot.RefType +import soot.ShortType +import soot.SootClass +import soot.Type +import soot.VoidType import soot.jimple.JimpleBody import soot.jimple.Stmt import java.io.File -import java.lang.reflect.Modifier import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract -import org.utbot.common.FileUtil const val SYMBOLIC_NULL_ADDR: Int = 0 @@ -730,6 +742,8 @@ open class ClassId @JvmOverloads constructor( // Treat simple class ids as non-nullable open val isNullable: Boolean = false ) { + open val modifiers: Int + get() = jClass.modifiers open val canonicalName: String get() = jClass.canonicalName ?: error("ClassId $name does not have canonical name") @@ -764,27 +778,6 @@ open class ClassId @JvmOverloads constructor( open val isInDefaultPackage: Boolean get() = packageName.isEmpty() - open val isPublic: Boolean - get() = Modifier.isPublic(jClass.modifiers) - - open val isProtected: Boolean - get() = Modifier.isProtected(jClass.modifiers) - - open val isPrivate: Boolean - get() = Modifier.isPrivate(jClass.modifiers) - - val isPackagePrivate: Boolean - get() = !(isPublic || isProtected || isPrivate) - - open val isFinal: Boolean - get() = Modifier.isFinal(jClass.modifiers) - - open val isStatic: Boolean - get() = Modifier.isStatic(jClass.modifiers) - - open val isAbstract: Boolean - get() = Modifier.isAbstract(jClass.modifiers) - open val isAnonymous: Boolean get() = jClass.isAnonymousClass @@ -883,12 +876,12 @@ class BuiltinClassId( // by default, we assume that the class is not a member class override val simpleNameWithEnclosings: String = simpleName, override val isNullable: Boolean = false, - override val isPublic: Boolean = true, - override val isProtected: Boolean = false, - override val isPrivate: Boolean = false, - override val isFinal: Boolean = false, - override val isStatic: Boolean = false, - override val isAbstract: Boolean = false, + isPublic: Boolean = true, + isProtected: Boolean = false, + isPrivate: Boolean = false, + isFinal: Boolean = false, + isStatic: Boolean = false, + isAbstract: Boolean = false, override val isAnonymous: Boolean = false, override val isLocal: Boolean = false, override val isInner: Boolean = false, @@ -912,6 +905,15 @@ class BuiltinClassId( elementClassId = elementClassId, isNullable = isNullable, ) { + override val modifiers: Int = ModifierFactory { + public = isPublic + protected = isProtected + private = isPrivate + final = isFinal + static = isStatic + abstract = isAbstract + } + init { BUILTIN_CLASSES_BY_NAMES[name] = this } @@ -929,10 +931,6 @@ class BuiltinClassId( fun getBuiltinClassByNameOrNull(name: String): BuiltinClassId? = BUILTIN_CLASSES_BY_NAMES[name] } } -enum class FieldIdStrategyValues { - Reflection, - Soot -} /** * Field id. Contains field name. @@ -940,38 +938,14 @@ enum class FieldIdStrategyValues { * Created to avoid usage String objects as a key. */ open class FieldId(val declaringClass: ClassId, val name: String) { - - object Strategy { - var value: FieldIdStrategyValues = FieldIdStrategyValues.Soot - } - - private val strategy - get() = if (Strategy.value == FieldIdStrategyValues.Soot) - FieldIdSootStrategy(declaringClass, this) else FieldIdReflectionStrategy(this) - - open val isPublic: Boolean - get() = strategy.isPublic - - open val isProtected: Boolean - get() = strategy.isProtected - - open val isPrivate: Boolean - get() = strategy.isPrivate - - open val isPackagePrivate: Boolean - get() = strategy.isPackagePrivate - - open val isFinal: Boolean - get() = strategy.isFinal - - open val isStatic: Boolean - get() = strategy.isStatic + open val modifiers: Int + get() = jField.modifiers open val isSynthetic: Boolean - get() = strategy.isSynthetic + get() = jField.isSynthetic open val type: ClassId - get() = strategy.type + get() = jField.type.id override fun equals(other: Any?): Boolean { if (this === other) return true @@ -994,16 +968,6 @@ open class FieldId(val declaringClass: ClassId, val name: String) { override fun toString() = safeJField?.toString() ?: "[unresolved] $declaringClass.$name" } -inline fun withReflection(block: () -> T): T { - val prevStrategy = FieldId.Strategy.value - try { - FieldId.Strategy.value = FieldIdStrategyValues.Reflection - return block() - } finally { - FieldId.Strategy.value = prevStrategy - } -} - /** * The same as [FieldId], except it represents the fields * of classes that may not be present on the classpath. @@ -1017,11 +981,18 @@ class BuiltinFieldId( name: String, override val type: ClassId, // by default we assume that the builtin field is public and non-final - override val isPublic: Boolean = true, - override val isPrivate: Boolean = false, - override val isFinal: Boolean = false, + isPublic: Boolean = true, + isPrivate: Boolean = false, + isFinal: Boolean = false, override val isSynthetic: Boolean = false, -) : FieldId(declaringClass, name) +) : FieldId(declaringClass, name) { + override val modifiers = ModifierFactory { + public = isPublic + private = isPrivate + final = isFinal + } + +} sealed class StatementId { abstract val classId: ClassId @@ -1113,11 +1084,12 @@ class BuiltinMethodId( isProtected: Boolean = false, isPrivate: Boolean = false ) : MethodId(classId, name, returnType, parameters) { - override val modifiers: Int = - (if (isStatic) Modifier.STATIC else 0) or - (if (isPublic) Modifier.PUBLIC else 0) or - (if (isProtected) Modifier.PROTECTED else 0) or - (if (isPrivate) Modifier.PRIVATE else 0) + override val modifiers: Int = ModifierFactory { + static = isStatic + public = isPublic + private = isPrivate + protected = isProtected + } } class BuiltinConstructorId( @@ -1128,10 +1100,11 @@ class BuiltinConstructorId( isProtected: Boolean = false, isPrivate: Boolean = false ) : ConstructorId(classId, parameters) { - override val modifiers: Int = - (if (isPublic) Modifier.PUBLIC else 0) or - (if (isProtected) Modifier.PROTECTED else 0) or - (if (isPrivate) Modifier.PRIVATE else 0) + override val modifiers: Int = ModifierFactory { + public = isPublic + private = isPrivate + protected = isProtected + } } open class TypeParameters(val parameters: List = emptyList()) @@ -1139,7 +1112,7 @@ open class TypeParameters(val parameters: List = emptyList()) class WildcardTypeParameter : TypeParameters(emptyList()) interface CodeGenerationSettingItem { - val id : String + val id: String val displayName: String val description: String } @@ -1152,7 +1125,7 @@ interface CodeGenerationSettingBox { } enum class MockStrategyApi( - override val id : String, + override val id: String, override val displayName: String, override val description: String ) : CodeGenerationSettingItem { @@ -1179,7 +1152,7 @@ enum class MockStrategyApi( } enum class TreatOverflowAsError( - override val id : String, + override val id: String, override val displayName: String, override val description: String, ) : CodeGenerationSettingItem { diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/impl/FieldIdStrategies.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/impl/FieldIdStrategies.kt deleted file mode 100644 index 21e23f0b26..0000000000 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/impl/FieldIdStrategies.kt +++ /dev/null @@ -1,90 +0,0 @@ -package org.utbot.framework.plugin.api.impl - -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.FieldId -import org.utbot.framework.plugin.api.classId -import org.utbot.framework.plugin.api.util.jField -import org.utbot.framework.plugin.api.util.id -import java.lang.reflect.Modifier -import soot.Scene -import soot.SootClass - -interface FieldIdStrategy { - - val isPublic: Boolean - - val isProtected: Boolean - - val isPrivate: Boolean - - val isPackagePrivate: Boolean - get() = !(isPublic || isProtected || isPrivate) - - val isFinal: Boolean - - val isStatic: Boolean - - val isSynthetic: Boolean - - val type: ClassId -} - -class FieldIdReflectionStrategy(val fieldId: FieldId) : FieldIdStrategy { - - override val isPublic: Boolean - get() = Modifier.isPublic(fieldId.jField.modifiers) - - override val isProtected: Boolean - get() = Modifier.isProtected(fieldId.jField.modifiers) - - override val isPrivate: Boolean - get() = Modifier.isPrivate(fieldId.jField.modifiers) - - override val isFinal: Boolean - get() = Modifier.isFinal(fieldId.jField.modifiers) - - override val isStatic: Boolean - get() = Modifier.isStatic(fieldId.jField.modifiers) - - override val isSynthetic: Boolean - get() = fieldId.jField.isSynthetic - - override val type: ClassId - get() = fieldId.jField.type.id -} - -class FieldIdSootStrategy(val declaringClass: ClassId, val fieldId: FieldId) : FieldIdStrategy { - - private val declaringSootClass: SootClass - get() = Scene.v().getSootClass(declaringClass.name) - - /** - * For hidden field (fields with the same names but different types in one class) produces RuntimeException. - * [SAT-315](JIRA:315) - */ - private val modifiers: Int - get() = declaringSootClass.getFieldByName(fieldId.name).modifiers - - - override val isPublic: Boolean - get() = soot.Modifier.isPublic(modifiers) - - override val isProtected: Boolean - get() = soot.Modifier.isProtected(modifiers) - - override val isPrivate: Boolean - get() = soot.Modifier.isPrivate(modifiers) - - override val isFinal: Boolean - get() = soot.Modifier.isFinal(modifiers) - - override val isStatic: Boolean - get() = soot.Modifier.isStatic(modifiers) - - override val isSynthetic: Boolean - get() = soot.Modifier.isSynthetic(modifiers) - - override val type: ClassId - get() = declaringSootClass.getFieldByName(fieldId.name).type.classId - -} diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt index 62e077cfdc..57dbb004c0 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt @@ -15,7 +15,6 @@ import java.lang.reflect.Constructor import java.lang.reflect.Executable import java.lang.reflect.Field import java.lang.reflect.Method -import java.lang.reflect.Modifier import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.util.concurrent.atomic.AtomicInteger @@ -497,25 +496,6 @@ val ExecutableId.isMethod: Boolean val ExecutableId.isConstructor: Boolean get() = this is ConstructorId -val ExecutableId.isPublic: Boolean - get() = Modifier.isPublic(modifiers) - -val ExecutableId.isProtected: Boolean - get() = Modifier.isProtected(modifiers) - -val ExecutableId.isPrivate: Boolean - get() = Modifier.isPrivate(modifiers) - -val ExecutableId.isStatic: Boolean - get() = Modifier.isStatic(modifiers) - -val ExecutableId.isPackagePrivate: Boolean - get() = !(isPublic || isProtected || isPrivate) - -val ExecutableId.isAbstract: Boolean - get() = Modifier.isAbstract(modifiers) - - /** * Construct MethodId */ diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/ModifierUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/ModifierUtil.kt new file mode 100644 index 0000000000..9a71ca82ac --- /dev/null +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/ModifierUtil.kt @@ -0,0 +1,98 @@ +package org.utbot.framework.plugin.api.util + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.FieldId +import java.lang.reflect.Modifier + +class ModifierFactory private constructor( + configure: ModifierFactory.() -> Unit = {} +) { + var public: Boolean = true + var protected: Boolean = false + var private: Boolean = false + var final: Boolean = false + var static: Boolean = false + var abstract: Boolean = false + + init { + this.configure() + } + + private val modifiers: Int = + (Modifier.PUBLIC.takeIf { public } ?: 0) or + (Modifier.PRIVATE.takeIf { private } ?: 0) or + (Modifier.PROTECTED.takeIf { protected } ?: 0) or + (Modifier.FINAL.takeIf { final } ?: 0) or + (Modifier.STATIC.takeIf { static } ?: 0) or + (Modifier.ABSTRACT.takeIf { abstract } ?: 0) + + companion object { + operator fun invoke(configure: ModifierFactory.() -> Unit): Int { + return ModifierFactory(configure).modifiers + } + } +} + +// ClassIds + +val ClassId.isAbstract: Boolean + get() = Modifier.isAbstract(modifiers) + +val ClassId.isPrivate: Boolean + get() = Modifier.isPrivate(modifiers) + +val ClassId.isPackagePrivate: Boolean + get() = !(isPublic || isProtected || isPrivate) + +val ClassId.isStatic: Boolean + get() = Modifier.isStatic(modifiers) + +val ClassId.isFinal: Boolean + get() = Modifier.isFinal(modifiers) + +val ClassId.isProtected: Boolean + get() = Modifier.isProtected(modifiers) + +val ClassId.isPublic: Boolean + get() = Modifier.isPublic(modifiers) + +// ExecutableIds + +val ExecutableId.isPublic: Boolean + get() = Modifier.isPublic(modifiers) + +val ExecutableId.isProtected: Boolean + get() = Modifier.isProtected(modifiers) + +val ExecutableId.isPrivate: Boolean + get() = Modifier.isPrivate(modifiers) + +val ExecutableId.isStatic: Boolean + get() = Modifier.isStatic(modifiers) + +val ExecutableId.isPackagePrivate: Boolean + get() = !(isPublic || isProtected || isPrivate) + +val ExecutableId.isAbstract: Boolean + get() = Modifier.isAbstract(modifiers) + +// FieldIds + +val FieldId.isStatic: Boolean + get() = Modifier.isStatic(modifiers) + +val FieldId.isFinal: Boolean + get() = Modifier.isFinal(modifiers) + +val FieldId.isPackagePrivate: Boolean + get() = !(isPublic || isProtected || isPrivate) + +val FieldId.isProtected: Boolean + get() = Modifier.isProtected(modifiers) + +val FieldId.isPrivate + get() = Modifier.isPrivate(modifiers) + +val FieldId.isPublic: Boolean + get() = Modifier.isPublic(modifiers) \ No newline at end of file diff --git a/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java b/utbot-framework-test/src/main/java/org/utbot/examples/strings11/StringConcat.java similarity index 97% rename from utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java rename to utbot-framework-test/src/main/java/org/utbot/examples/strings11/StringConcat.java index 771340c2ae..78552f701d 100644 --- a/utbot-framework-test/src/main/java/org/utbot/examples/strings/StringConcat.java +++ b/utbot-framework-test/src/main/java/org/utbot/examples/strings11/StringConcat.java @@ -1,4 +1,4 @@ -package org.utbot.examples.strings; +package org.utbot.examples.strings11; import org.utbot.api.mock.UtMock; diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringConcatTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings11/StringConcatTest.kt similarity index 98% rename from utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringConcatTest.kt rename to utbot-framework-test/src/test/kotlin/org/utbot/examples/strings11/StringConcatTest.kt index 232ba02c99..10359f3b04 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings/StringConcatTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/strings11/StringConcatTest.kt @@ -1,4 +1,4 @@ -package org.utbot.examples.strings +package org.utbot.examples.strings11 import org.junit.jupiter.api.Test import org.utbot.framework.plugin.api.CodegenLanguage diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index d1a45454be..4c5388d30b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -105,6 +105,7 @@ import org.utbot.framework.plugin.api.UtExecutionSuccess import org.utbot.framework.plugin.api.UtLambdaModel import org.utbot.framework.plugin.api.UtSandboxFailure import org.utbot.framework.plugin.api.util.executable +import org.utbot.framework.plugin.api.util.isAbstract import org.utbot.fuzzer.toFuzzerType val logger = KotlinLogging.logger {} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt index bd454e3e83..cedaec3e8a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt @@ -36,7 +36,10 @@ import org.utbot.framework.plugin.api.hasDefaultValue import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.plugin.api.util.executableId -import org.utbot.framework.plugin.api.util.isSubtypeOf +import org.utbot.framework.plugin.api.util.isFinal +import org.utbot.framework.plugin.api.util.isPrivate +import org.utbot.framework.plugin.api.util.isPublic +import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.util.nextModelName import java.lang.reflect.Constructor diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt index a8e632976c..865c43dff0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt @@ -42,6 +42,7 @@ class CodeGenerator( enableTestsTimeout: Boolean = true, testClassPackageName: String = classUnderTest.packageName, ) { + private var context: CgContext = CgContext( classUnderTest = classUnderTest, generateUtilClassFile = generateUtilClassFile, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt index 2c73f18281..8a985e0c39 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt @@ -12,7 +12,9 @@ import org.utbot.framework.codegen.model.constructor.builtin.newInstance import org.utbot.framework.codegen.model.constructor.builtin.setAccessible import org.utbot.framework.codegen.model.constructor.context.CgContext import org.utbot.framework.codegen.model.constructor.context.CgContextOwner -import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManagerImpl.FieldAccessorSuitability.* +import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManagerImpl.FieldAccessorSuitability.ReflectionOnly +import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManagerImpl.FieldAccessorSuitability.RequiresTypeCast +import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManagerImpl.FieldAccessorSuitability.Suitable import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getStatementConstructorBy import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getVariableConstructorBy import org.utbot.framework.codegen.model.constructor.util.getAmbiguousOverloadsOf @@ -33,12 +35,12 @@ import org.utbot.framework.codegen.model.tree.CgThisInstance import org.utbot.framework.codegen.model.tree.CgValue import org.utbot.framework.codegen.model.tree.CgVariable import org.utbot.framework.codegen.model.util.at -import org.utbot.framework.codegen.model.util.isAccessibleFrom import org.utbot.framework.codegen.model.util.canBeReadFrom +import org.utbot.framework.codegen.model.util.isAccessibleFrom import org.utbot.framework.codegen.model.util.nullLiteral import org.utbot.framework.codegen.model.util.resolve -import org.utbot.framework.plugin.api.BuiltinConstructorId import org.utbot.framework.plugin.api.BuiltinClassId +import org.utbot.framework.plugin.api.BuiltinConstructorId import org.utbot.framework.plugin.api.BuiltinMethodId import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage @@ -47,10 +49,11 @@ import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.UtExplicitlyThrownException -import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.exceptions import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.isAbstract import org.utbot.framework.plugin.api.util.isArray +import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.isSubtypeOf import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.method diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index 92aeaf3b66..9187140721 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -146,6 +146,7 @@ import java.lang.reflect.InvocationTargetException import java.security.AccessControlException import java.lang.reflect.ParameterizedType import org.utbot.framework.UtSettings +import org.utbot.framework.plugin.api.util.isStatic private const val DEEP_EQUALS_MAX_DEPTH = 5 // TODO move it to plugin settings? diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/ClassIdUtil.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/ClassIdUtil.kt index a215965853..783037eb7e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/ClassIdUtil.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/ClassIdUtil.kt @@ -3,6 +3,10 @@ package org.utbot.framework.codegen.model.util import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.util.isPackagePrivate +import org.utbot.framework.plugin.api.util.isProtected +import org.utbot.framework.plugin.api.util.isPublic +import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.allDeclaredFieldIds import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.isArray diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt index 8df76d3d39..d90a4c9151 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt @@ -4,6 +4,11 @@ import org.utbot.framework.codegen.model.constructor.context.CgContext import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.util.isFinal +import org.utbot.framework.plugin.api.util.isPackagePrivate +import org.utbot.framework.plugin.api.util.isProtected +import org.utbot.framework.plugin.api.util.isPublic +import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.voidClassId /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt index 88bbba9ffa..49a8efea03 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt @@ -43,6 +43,10 @@ import org.utbot.framework.codegen.model.util.nullLiteral import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.TypeParameters +import org.utbot.framework.plugin.api.util.isFinal +import org.utbot.framework.plugin.api.util.isPrivate +import org.utbot.framework.plugin.api.util.isProtected +import org.utbot.framework.plugin.api.util.isPublic import org.utbot.framework.plugin.api.util.wrapperByPrimitive internal class CgJavaRenderer(context: CgRendererContext, printer: CgPrinter = CgPrinterImpl()) : diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt index 09459b5f8b..fdf2fab716 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt @@ -51,6 +51,10 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.TypeParameters import org.utbot.framework.plugin.api.WildcardTypeParameter +import org.utbot.framework.plugin.api.util.isFinal +import org.utbot.framework.plugin.api.util.isPrivate +import org.utbot.framework.plugin.api.util.isProtected +import org.utbot.framework.plugin.api.util.isPublic import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isPrimitive diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt index ab0b69e8b2..16eed51791 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt @@ -8,13 +8,11 @@ import org.utbot.framework.UtSettings import org.utbot.framework.assemble.AssembleModelGenerator import org.utbot.framework.plugin.api.Coverage import org.utbot.framework.plugin.api.EnvironmentModels -import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.Instruction import org.utbot.framework.plugin.api.MissingState import org.utbot.framework.plugin.api.TimeoutException import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtConcreteExecutionFailure import org.utbot.framework.plugin.api.UtExecutionFailure import org.utbot.framework.plugin.api.UtExecutionResult import org.utbot.framework.plugin.api.UtExecutionSuccess @@ -32,7 +30,6 @@ import org.utbot.framework.plugin.api.util.jField import org.utbot.framework.plugin.api.util.singleExecutableId import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.plugin.api.util.withUtContext -import org.utbot.framework.plugin.api.withReflection import org.utbot.framework.util.isInaccessibleViaReflection import org.utbot.instrumentation.instrumentation.ArgumentList import org.utbot.instrumentation.instrumentation.Instrumentation @@ -139,7 +136,6 @@ object UtExecutionInstrumentation : Instrumentation { arguments: ArgumentList, parameters: Any? ): UtConcreteExecutionResult { - withReflection { if (parameters !is UtConcreteExecutionData) { throw IllegalArgumentException("Argument parameters must be of type UtConcreteExecutionData, but was: ${parameters?.javaClass}") } @@ -232,7 +228,6 @@ object UtExecutionInstrumentation : Instrumentation { concreteExecutionResult } - } } override fun getStaticField(fieldId: FieldId): Result = diff --git a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestCodeGeneratorPipeline.kt b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestCodeGeneratorPipeline.kt index 5cab79ad50..7fca8e52f3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestCodeGeneratorPipeline.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestCodeGeneratorPipeline.kt @@ -260,24 +260,26 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram ): CodeGeneratorResult { val params = mutableMapOf>() - val codeGenerator = with(testFrameworkConfiguration) { - CodeGenerator( - classUnderTest.id, - generateUtilClassFile = generateUtilClassFile, - paramNames = params, - testFramework = testFramework, - staticsMocking = staticsMocking, - forceStaticMocking = forceStaticMocking, - generateWarningsForStaticMocking = false, - codegenLanguage = codegenLanguage, - parameterizedTestSource = parametrizedTestSource, - runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, - enableTestsTimeout = enableTestsTimeout - ) - } - val testClassCustomName = "${classUnderTest.java.simpleName}GeneratedTest" + withUtContext(UtContext(classUnderTest.java.classLoader)) { + val codeGenerator = with(testFrameworkConfiguration) { + CodeGenerator( + classUnderTest.id, + generateUtilClassFile = generateUtilClassFile, + paramNames = params, + testFramework = testFramework, + staticsMocking = staticsMocking, + forceStaticMocking = forceStaticMocking, + generateWarningsForStaticMocking = false, + codegenLanguage = codegenLanguage, + parameterizedTestSource = parametrizedTestSource, + runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, + enableTestsTimeout = enableTestsTimeout + ) + } + val testClassCustomName = "${classUnderTest.java.simpleName}GeneratedTest" - return codeGenerator.generateAsStringWithTestReport(testSets, testClassCustomName) + return codeGenerator.generateAsStringWithTestReport(testSets, testClassCustomName) + } } private fun checkPipelinesResults(classesPipelines: List) { diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/CollectionWithModificationModelProvider.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/CollectionWithModificationModelProvider.kt index 434c3056dd..48a60d8a42 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/CollectionWithModificationModelProvider.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/CollectionWithModificationModelProvider.kt @@ -1,6 +1,7 @@ package org.utbot.fuzzer.providers import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.util.isAbstract import org.utbot.framework.plugin.api.util.booleanClassId import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt index 10b329d8f1..6bdbf2891f 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt @@ -16,6 +16,8 @@ import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtExecutableCallModel +import org.utbot.framework.plugin.api.util.isAbstract +import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.dateClassId import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.isEnum diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeInstrumentation.kt index 7e0e454df7..730f73fd5b 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeInstrumentation.kt @@ -9,6 +9,7 @@ import java.lang.reflect.Modifier import java.security.ProtectionDomain import org.utbot.common.withAccessibility import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.jField typealias ArgumentList = List