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 d57f6c2da8..bb1050d068 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 @@ -234,6 +234,17 @@ object UtExecutionInstrumentation : Instrumentation { } } + override fun getStaticField(fieldId: FieldId): Result = + delegateInstrumentation.getStaticField(fieldId).map { value -> + val cache = IdentityHashMap() + val strategy = ConstructOnlyUserClassesOrCachedObjectsStrategy( + pathsToUserClasses, cache + ) + UtModelConstructor(cache, strategy).run { + construct(value, fieldId.type) + } + } + private fun sortOutException(exception: Throwable): UtExecutionFailure { if (exception is TimeoutException) { return UtTimeoutException(exception) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt index 41cb7fdc02..49564092a4 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt @@ -5,30 +5,32 @@ import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.lifetime.LifetimeDefinition import com.jetbrains.rd.util.lifetime.isAlive import com.jetbrains.rd.util.lifetime.throwIfNotAlive +import java.io.Closeable +import java.util.concurrent.atomic.AtomicLong +import kotlin.concurrent.thread +import kotlin.reflect.KCallable +import kotlin.reflect.KFunction +import kotlin.reflect.KProperty +import kotlin.reflect.jvm.javaConstructor +import kotlin.reflect.jvm.javaGetter +import kotlin.reflect.jvm.javaMethod +import kotlin.streams.toList import kotlinx.coroutines.CancellationException import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import mu.KotlinLogging import org.utbot.framework.plugin.api.ConcreteExecutionFailureException +import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.process.ChildProcessRunner import org.utbot.instrumentation.rd.UtInstrumentationProcess -import org.utbot.rd.UtRdKLoggerFactory +import org.utbot.instrumentation.rd.generated.ComputeStaticFieldParams import org.utbot.instrumentation.rd.generated.InvokeMethodCommandParams import org.utbot.instrumentation.util.ChildProcessError -import java.io.Closeable -import java.util.concurrent.atomic.AtomicLong -import kotlin.concurrent.thread -import kotlin.reflect.KCallable -import kotlin.reflect.KFunction -import kotlin.reflect.KProperty -import kotlin.reflect.jvm.javaConstructor -import kotlin.reflect.jvm.javaGetter -import kotlin.reflect.jvm.javaMethod -import kotlin.streams.toList +import org.utbot.rd.UtRdKLoggerFactory private val logger = KotlinLogging.logger {} @@ -298,4 +300,18 @@ fun ConcreteExecutor<*,*>.warmup() = runBlocking { withProcess { protocolModel.warmup.start(lifetime, Unit) } -} \ No newline at end of file +} + +/** + * Extension function for the [ConcreteExecutor], which allows to collect static field value of [fieldId]. + */ +fun ConcreteExecutor<*, *>.computeStaticField(fieldId: FieldId): Result = runBlocking { + withProcess { + val fieldIdSerialized = kryoHelper.writeObject(fieldId) + val params = ComputeStaticFieldParams(fieldIdSerialized) + + val result = protocolModel.computeStaticField.startSuspending(lifetime, params) + + kryoHelper.readObject(result.result) + } +} diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt index 1a904a8923..362989dbd5 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt @@ -1,6 +1,7 @@ package org.utbot.instrumentation.instrumentation import java.lang.instrument.ClassFileTransformer +import org.utbot.framework.plugin.api.FieldId /** * Abstract class for the instrumentation. @@ -24,6 +25,8 @@ interface Instrumentation : ClassFileTransformer parameters: Any? = null ): TInvocationInstrumentation + fun getStaticField(fieldId: FieldId): Result<*> + /** * Will be called in the very beginning in the child process. */ 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 1e145c0ea6..7e0e454df7 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 @@ -7,6 +7,9 @@ import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method 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.jField typealias ArgumentList = List @@ -89,6 +92,19 @@ class InvokeInstrumentation : Instrumentation> { } } + /** + * Get field by reflection and return raw value. + */ + override fun getStaticField(fieldId: FieldId): Result = + if (!fieldId.isStatic) { + Result.failure(IllegalArgumentException("Field must be static!")) + } else { + val field = fieldId.jField + val value = field.withAccessibility { + field.get(null) + } + Result.success(value) + } /** * Does not change bytecode. diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeWithStaticsInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeWithStaticsInstrumentation.kt index b4ea56bab0..4b91be37fa 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeWithStaticsInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeWithStaticsInstrumentation.kt @@ -6,6 +6,7 @@ import org.utbot.instrumentation.util.StaticEnvironment import java.lang.reflect.Field import java.lang.reflect.Modifier import java.security.ProtectionDomain +import org.utbot.framework.plugin.api.FieldId /** * This instrumentation allows supplying [StaticEnvironment] and saving static fields. This makes call pure. @@ -43,6 +44,9 @@ class InvokeWithStaticsInstrumentation : Instrumentation> { return invokeResult } + override fun getStaticField(fieldId: FieldId): Result<*> = + invokeInstrumentation.getStaticField(fieldId) + private fun setStaticFields(staticEnvironment: StaticEnvironment?) { staticEnvironment?.run { listOfFields.forEach { (fieldId, value) -> diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt index cc1977296f..1617cd4e21 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt @@ -13,6 +13,8 @@ import org.utbot.instrumentation.util.CastProbesArrayException import org.utbot.instrumentation.util.ChildProcessError import org.utbot.instrumentation.util.NoProbesArrayException import java.security.ProtectionDomain +import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.util.jField data class CoverageInfo( val methodToInstrRange: Map, @@ -47,6 +49,8 @@ object CoverageInstrumentation : Instrumentation> { } } + override fun getStaticField(fieldId: FieldId): Result<*> = + invokeWithStatics.getStaticField(fieldId) /** * Collects coverage from the given [clazz] via reflection. diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/ExecutionTraceInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/ExecutionTraceInstrumentation.kt index f53831d2f2..c10e90e73b 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/ExecutionTraceInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/ExecutionTraceInstrumentation.kt @@ -5,6 +5,7 @@ import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.instrumentation.InvokeWithStaticsInstrumentation import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter import java.security.ProtectionDomain +import org.utbot.framework.plugin.api.FieldId /** * This instrumentation allows to get execution trace during each call. @@ -33,6 +34,10 @@ class ExecutionTraceInstrumentation : Instrumentation { return trace } + + override fun getStaticField(fieldId: FieldId): Result<*> = + invokeWithStatics.getStaticField(fieldId) + /** * Transforms bytecode such way that it becomes possible to get an execution trace during a call. * diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt index 70fb867205..7350e406d5 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt @@ -38,6 +38,8 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.concurrent.TimeUnit import kotlin.system.measureTimeMillis +import org.utbot.framework.plugin.api.FieldId +import org.utbot.instrumentation.rd.generated.ComputeStaticFieldResult /** * We use this ClassLoader to separate user's classes and our dependency classes. @@ -199,7 +201,7 @@ private fun RdCall.measureExecutionForTermination(block: (T) -> R) } } -private suspend fun ProtocolModel.setup(kryoHelper: KryoHelper, onStop: () -> Unit) { +private fun ProtocolModel.setup(kryoHelper: KryoHelper, onStop: () -> Unit) { warmup.measureExecutionForTermination { logDebug { "received warmup request" } val time = measureTimeMillis { @@ -250,6 +252,11 @@ private suspend fun ProtocolModel.setup(kryoHelper: KryoHelper, onStop: () -> Un val result = (instrumentation as CoverageInstrumentation).collectCoverageInfo(anyClass) CollectCoverageResult(kryoHelper.writeObject(result)) } + computeStaticField.measureExecutionForTermination { params -> + val fieldId = kryoHelper.readObject(params.fieldId) + val result = instrumentation.getStaticField(fieldId) + ComputeStaticFieldResult(kryoHelper.writeObject(result)) + } } private suspend fun initiate(lifetime: Lifetime, port: Int, pid: Int) { diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolModel.Generated.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolModel.Generated.kt index 7a68147b71..fa70bab72b 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolModel.Generated.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/ProtocolModel.Generated.kt @@ -23,7 +23,8 @@ class ProtocolModel private constructor( private val _setInstrumentation: RdCall, private val _invokeMethodCommand: RdCall, private val _stopProcess: RdCall, - private val _collectCoverage: RdCall + private val _collectCoverage: RdCall, + private val _computeStaticField: RdCall ) : RdExtBase() { //companion @@ -36,6 +37,8 @@ class ProtocolModel private constructor( serializers.register(InvokeMethodCommandResult) serializers.register(CollectCoverageParams) serializers.register(CollectCoverageResult) + serializers.register(ComputeStaticFieldParams) + serializers.register(ComputeStaticFieldResult) } @@ -59,7 +62,7 @@ class ProtocolModel private constructor( } - const val serializationHash = -983308496809975144L + const val serializationHash = -3299689793276292923L } override val serializersOwner: ISerializersOwner get() = ProtocolModel @@ -99,6 +102,12 @@ class ProtocolModel private constructor( [clazz] */ val collectCoverage: RdCall get() = _collectCoverage + + /** + * This command is sent to the child process from the [ConcreteExecutor] if user wants to get value of static field + [fieldId] + */ + val computeStaticField: RdCall get() = _computeStaticField //methods //initializer init { @@ -108,6 +117,7 @@ class ProtocolModel private constructor( _invokeMethodCommand.async = true _stopProcess.async = true _collectCoverage.async = true + _computeStaticField.async = true } init { @@ -117,6 +127,7 @@ class ProtocolModel private constructor( bindableChildren.add("invokeMethodCommand" to _invokeMethodCommand) bindableChildren.add("stopProcess" to _stopProcess) bindableChildren.add("collectCoverage" to _collectCoverage) + bindableChildren.add("computeStaticField" to _computeStaticField) } //secondary constructor @@ -127,7 +138,8 @@ class ProtocolModel private constructor( RdCall(SetInstrumentationParams, FrameworkMarshallers.Void), RdCall(InvokeMethodCommandParams, InvokeMethodCommandResult), RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Void), - RdCall(CollectCoverageParams, CollectCoverageResult) + RdCall(CollectCoverageParams, CollectCoverageResult), + RdCall(ComputeStaticFieldParams, ComputeStaticFieldResult) ) //equals trait @@ -142,6 +154,7 @@ class ProtocolModel private constructor( print("invokeMethodCommand = "); _invokeMethodCommand.print(printer); println() print("stopProcess = "); _stopProcess.print(printer); println() print("collectCoverage = "); _collectCoverage.print(printer); println() + print("computeStaticField = "); _computeStaticField.print(printer); println() } printer.print(")") } @@ -153,7 +166,8 @@ class ProtocolModel private constructor( _setInstrumentation.deepClonePolymorphic(), _invokeMethodCommand.deepClonePolymorphic(), _stopProcess.deepClonePolymorphic(), - _collectCoverage.deepClonePolymorphic() + _collectCoverage.deepClonePolymorphic(), + _computeStaticField.deepClonePolymorphic() ) } //contexts @@ -339,6 +353,120 @@ data class CollectCoverageResult ( } +/** + * #### Generated from [ProtocolRoot.kt:36] + */ +data class ComputeStaticFieldParams ( + val fieldId: ByteArray +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = ComputeStaticFieldParams::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): ComputeStaticFieldParams { + val fieldId = buffer.readByteArray() + return ComputeStaticFieldParams(fieldId) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: ComputeStaticFieldParams) { + buffer.writeByteArray(value.fieldId) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as ComputeStaticFieldParams + + if (!(fieldId contentEquals other.fieldId)) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + fieldId.contentHashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("ComputeStaticFieldParams (") + printer.indent { + print("fieldId = "); fieldId.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [ProtocolRoot.kt:40] + */ +data class ComputeStaticFieldResult ( + val result: ByteArray +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = ComputeStaticFieldResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): ComputeStaticFieldResult { + val result = buffer.readByteArray() + return ComputeStaticFieldResult(result) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: ComputeStaticFieldResult) { + buffer.writeByteArray(value.result) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as ComputeStaticFieldResult + + if (!(result contentEquals other.result)) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + result.contentHashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("ComputeStaticFieldResult (") + printer.indent { + print("result = "); result.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + /** * #### Generated from [ProtocolRoot.kt:17] */ diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/ProtocolRoot.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/ProtocolRoot.kt index ad49e762a0..b79243d943 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/ProtocolRoot.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/ProtocolRoot.kt @@ -33,6 +33,14 @@ object ProtocolModel : Ext(ProtocolRoot) { field("coverageInfo", array(PredefinedType.byte)) } + val ComputeStaticFieldParams = structdef { + field("fieldId", array(PredefinedType.byte)) + } + + val ComputeStaticFieldResult = structdef { + field("result", array(PredefinedType.byte)) + } + init { call("AddPaths", AddPathsParams, PredefinedType.void).apply { async @@ -67,5 +75,11 @@ object ProtocolModel : Ext(ProtocolRoot) { "This command is sent to the child process from the [ConcreteExecutor] if user wants to collect coverage for the\n" + "[clazz]" } + call("ComputeStaticField", ComputeStaticFieldParams, ComputeStaticFieldResult).apply { + async + documentation = + "This command is sent to the child process from the [ConcreteExecutor] if user wants to get value of static field\n" + + "[fieldId]" + } } } \ No newline at end of file