Skip to content

Get static field concretly #904

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,17 @@ object UtExecutionInstrumentation : Instrumentation<UtConcreteExecutionResult> {
}
}

override fun getStaticField(fieldId: FieldId): Result<UtModel> =
delegateInstrumentation.getStaticField(fieldId).map { value ->
val cache = IdentityHashMap<Any, UtModel>()
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}

Expand Down Expand Up @@ -298,4 +300,18 @@ fun ConcreteExecutor<*,*>.warmup() = runBlocking {
withProcess {
protocolModel.warmup.start(lifetime, Unit)
}
}
}

/**
* Extension function for the [ConcreteExecutor], which allows to collect static field value of [fieldId].
*/
fun <T> ConcreteExecutor<*, *>.computeStaticField(fieldId: FieldId): Result<T> = runBlocking {
withProcess {
val fieldIdSerialized = kryoHelper.writeObject(fieldId)
val params = ComputeStaticFieldParams(fieldIdSerialized)

val result = protocolModel.computeStaticField.startSuspending(lifetime, params)

kryoHelper.readObject(result.result)
}
}
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -24,6 +25,8 @@ interface Instrumentation<out TInvocationInstrumentation> : ClassFileTransformer
parameters: Any? = null
): TInvocationInstrumentation

fun getStaticField(fieldId: FieldId): Result<*>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need it in a base class?

Copy link
Member

@sergeypospelov sergeypospelov Sep 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed. We will take a look at it later.


/**
* Will be called in the very beginning in the child process.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Any?>

Expand Down Expand Up @@ -89,6 +92,19 @@ class InvokeInstrumentation : Instrumentation<Result<*>> {
}
}

/**
* Get field by reflection and return raw value.
*/
override fun getStaticField(fieldId: FieldId): Result<Any?> =
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -43,6 +44,9 @@ class InvokeWithStaticsInstrumentation : Instrumentation<Result<*>> {
return invokeResult
}

override fun getStaticField(fieldId: FieldId): Result<*> =
invokeInstrumentation.getStaticField(fieldId)

private fun setStaticFields(staticEnvironment: StaticEnvironment?) {
staticEnvironment?.run {
listOfFields.forEach { (fieldId, value) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, IntRange>,
Expand Down Expand Up @@ -47,6 +49,8 @@ object CoverageInstrumentation : Instrumentation<Result<*>> {
}
}

override fun getStaticField(fieldId: FieldId): Result<*> =
invokeWithStatics.getStaticField(fieldId)

/**
* Collects coverage from the given [clazz] via reflection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -33,6 +34,10 @@ class ExecutionTraceInstrumentation : Instrumentation<Trace> {

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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -199,7 +201,7 @@ private fun <T, R> RdCall<T, R>.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 {
Expand Down Expand Up @@ -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<FieldId>(params.fieldId)
val result = instrumentation.getStaticField(fieldId)
ComputeStaticFieldResult(kryoHelper.writeObject(result))
}
}

private suspend fun initiate(lifetime: Lifetime, port: Int, pid: Int) {
Expand Down
Loading