Skip to content

Commit

Permalink
Concrete executor phases refactoring (#1446)
Browse files Browse the repository at this point in the history
  • Loading branch information
SBOne-Kenobi authored Dec 8, 2022
1 parent 66c3443 commit b5f40fc
Show file tree
Hide file tree
Showing 24 changed files with 509 additions and 214 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.utbot.framework.concrete.constructors

import org.utbot.engine.ValueConstructor
import org.utbot.framework.concrete.UtModelConstructor
import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.util.UtContext
import org.utbot.framework.plugin.api.util.id
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,30 @@
package org.utbot.framework.concrete

import org.objectweb.asm.Type
import org.utbot.common.StopWatch
import org.utbot.common.ThreadBasedExecutor
import org.utbot.common.withAccessibility
import java.security.ProtectionDomain
import java.util.IdentityHashMap
import kotlin.reflect.jvm.javaMethod
import org.utbot.framework.UtSettings
import org.utbot.framework.assemble.AssembleModelGenerator
import org.utbot.framework.concrete.constructors.ConstructOnlyUserClassesOrCachedObjectsStrategy
import org.utbot.framework.concrete.constructors.UtModelConstructor
import org.utbot.framework.concrete.mock.InstrumentationContext
import org.utbot.framework.concrete.phases.PhasesController
import org.utbot.framework.concrete.phases.start
import org.utbot.framework.plugin.api.Coverage
import org.utbot.framework.plugin.api.EnvironmentModels
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.UtExecutionFailure
import org.utbot.framework.plugin.api.UtExecutionResult
import org.utbot.framework.plugin.api.UtExecutionSuccess
import org.utbot.framework.plugin.api.UtExplicitlyThrownException
import org.utbot.framework.plugin.api.UtImplicitlyThrownException
import org.utbot.framework.plugin.api.UtInstrumentation
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
import org.utbot.framework.plugin.api.UtSandboxFailure
import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation
import org.utbot.framework.plugin.api.visible.UtStreamConsumingException
import org.utbot.framework.plugin.api.UtStreamConsumingFailure
import org.utbot.framework.plugin.api.UtTimeoutException
import org.utbot.framework.plugin.api.util.UtContext
import org.utbot.framework.plugin.api.util.id
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.util.isInaccessibleViaReflection
import org.utbot.instrumentation.instrumentation.ArgumentList
import org.utbot.instrumentation.instrumentation.Instrumentation
import org.utbot.instrumentation.instrumentation.InvokeInstrumentation
import org.utbot.instrumentation.instrumentation.et.EtInstruction
import org.utbot.instrumentation.instrumentation.et.ExplicitThrowInstruction
import org.utbot.instrumentation.instrumentation.et.TraceHandler
import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter
import org.utbot.instrumentation.instrumentation.mock.MockClassVisitor
import java.security.AccessControlException
import java.security.ProtectionDomain
import java.util.IdentityHashMap
import kotlin.reflect.jvm.javaMethod

/**
* Consists of the data needed to execute the method concretely. Also includes method arguments stored in models.
Expand Down Expand Up @@ -142,107 +122,78 @@ object UtExecutionInstrumentation : Instrumentation<UtConcreteExecutionResult> {
throw IllegalArgumentException("Argument parameters must be of type UtConcreteExecutionData, but was: ${parameters?.javaClass}")
}
val (stateBefore, instrumentations, timeout) = parameters // smart cast to UtConcreteExecutionData
val parametersModels = listOfNotNull(stateBefore.thisInstance) + stateBefore.parameters

val methodId = clazz.singleExecutableId(methodSignature)
val returnClassId = methodId.returnType
traceHandler.resetTrace()

return MockValueConstructor(instrumentationContext).use { constructor ->
val params = try {
constructor.constructMethodParameters(parametersModels)
} catch (e: Throwable) {
if (e.cause is AccessControlException) {
return@use UtConcreteExecutionResult(
MissingState,
UtSandboxFailure(e.cause!!),
Coverage()
)
}

throw e
return PhasesController(
instrumentationContext,
traceHandler,
delegateInstrumentation
).computeConcreteExecutionResult {
// construction
val (params, statics, cache) = valueConstructionContext.start {
val params = constructParameters(stateBefore)
val statics = constructStatics(stateBefore)

mock(instrumentations)

Triple(params, statics, getCache())
}
val staticFields = constructor
.constructStatics(
stateBefore
.statics
.filterKeys { !it.isInaccessibleViaReflection }
)
.mapValues { (_, value) -> value.value }

val concreteExecutionResult = withStaticFields(staticFields) {
val staticMethodsInstrumentation = instrumentations.filterIsInstance<UtStaticMethodInstrumentation>()
constructor.mockStaticMethods(staticMethodsInstrumentation)
val newInstanceInstrumentation = instrumentations.filterIsInstance<UtNewInstanceInstrumentation>()
constructor.mockNewInstances(newInstanceInstrumentation)

traceHandler.resetTrace()
val stopWatch = StopWatch()
val context = UtContext(utContext.classLoader, stopWatch)
val concreteResult = ThreadBasedExecutor.threadLocal.invokeWithTimeout(timeout, stopWatch) {
withUtContext(context) {
delegateInstrumentation.invoke(clazz, methodSignature, params.map { it.value })
}
}?.getOrThrow() as? Result<*> ?: Result.failure<Any?>(TimeoutException("Timeout $timeout elapsed"))
val traceList = traceHandler.computeInstructionList()

val cache = constructor.objectToModelCache
val utCompositeModelStrategy = ConstructOnlyUserClassesOrCachedObjectsStrategy(pathsToUserClasses, cache)
val utModelConstructor = UtModelConstructor(cache, utCompositeModelStrategy)
utModelConstructor.run {
val concreteUtModelResult = concreteResult.fold({
try {
val model = construct(it, returnClassId)
UtExecutionSuccess(model)
} catch (e: Exception) {
processExceptionDuringModelConstruction(e)
}
}) {
sortOutException(it)

// preparation
val savedStatics = preparationContext.start {
val savedStatics = setStaticFields(statics)
resetTrace()
savedStatics
}

try {
// invocation
val concreteResult = invocationContext.start {
invoke(clazz, methodSignature, params.map { it.value }, timeout)
}

// statistics collection
val coverage = statisticsCollectionContext.start {
getCoverage(clazz)
}

// model construction
val (executionResult, stateAfter) = modelConstructionContext.start {
configureConstructor {
this.cache = cache
strategy = ConstructOnlyUserClassesOrCachedObjectsStrategy(pathsToUserClasses, cache)
}

val stateAfterParametersWithThis = params.map { construct(it.value, it.clazz.id) }
val stateAfterStatics = (staticFields.keys/* + traceHandler.computePutStatics()*/)
.associateWith { fieldId ->
fieldId.jField.run {
val computedValue = withAccessibility { get(null) }
val knownModel = stateBefore.statics[fieldId]
val knownValue = staticFields[fieldId]
if (knownModel != null && knownValue != null && knownValue == computedValue) {
knownModel
} else {
construct(computedValue, fieldId.type)
}
}
}
val executionResult = convertToExecutionResult(concreteResult, returnClassId)

val stateAfterParametersWithThis = constructParameters(params)
val stateAfterStatics = constructStatics(statics.keys/* + traceHandler.computePutStatics()*/)
val (stateAfterThis, stateAfterParameters) = if (stateBefore.thisInstance == null) {
null to stateAfterParametersWithThis
} else {
stateAfterParametersWithThis.first() to stateAfterParametersWithThis.drop(1)
}
val stateAfter = EnvironmentModels(stateAfterThis, stateAfterParameters, stateAfterStatics)
UtConcreteExecutionResult(
stateAfter,
concreteUtModelResult,
traceList.toApiCoverage(
traceHandler.processingStorage.getInstructionsCount(
Type.getInternalName(clazz)
)
)
)

executionResult to stateAfter
}
}

concreteExecutionResult
UtConcreteExecutionResult(
stateAfter,
executionResult,
coverage
)
} finally {
// postprocessing
postprocessingContext.start {
resetStaticFields(savedStatics)
}
}
}
}

private fun processExceptionDuringModelConstruction(e: Exception): UtExecutionResult =
when (e) {
is UtStreamConsumingException -> UtStreamConsumingFailure(e)
else -> throw e
}

override fun getStaticField(fieldId: FieldId): Result<UtModel> =
delegateInstrumentation.getStaticField(fieldId).map { value ->
val cache = IdentityHashMap<Any, UtModel>()
Expand All @@ -254,30 +205,6 @@ object UtExecutionInstrumentation : Instrumentation<UtConcreteExecutionResult> {
}
}

private fun sortOutException(exception: Throwable): UtExecutionFailure {
if (exception is TimeoutException) {
return UtTimeoutException(exception)
}
if (exception is AccessControlException ||
exception is ExceptionInInitializerError && exception.exception is AccessControlException) {
return UtSandboxFailure(exception)
}
// there also can be other cases, when we need to wrap internal exception... I suggest adding them on demand

val instrs = traceHandler.computeInstructionList()
val isNested = if (instrs.isEmpty()) {
false
} else {
instrs.first().callId != instrs.last().callId
}
return if (instrs.isNotEmpty() && instrs.last().instructionData is ExplicitThrowInstruction) {
UtExplicitlyThrownException(exception, isNested)
} else {
UtImplicitlyThrownException(exception, isNested)
}

}

override fun transform(
loader: ClassLoader?,
className: String,
Expand Down Expand Up @@ -305,36 +232,4 @@ object UtExecutionInstrumentation : Instrumentation<UtConcreteExecutionResult> {

return instrumenter.classByteCode
}

private fun <T> withStaticFields(staticFields: Map<FieldId, Any?>, block: () -> T): T {
val savedFields = mutableMapOf<FieldId, Any?>()
try {
staticFields.forEach { (fieldId, value) ->
fieldId.jField.run {
withAccessibility {
savedFields[fieldId] = get(null)
set(null, value)
}
}
}
return block()
} finally {
savedFields.forEach { (fieldId, value) ->
fieldId.jField.run {
withAccessibility {
set(null, value)
}
}
}
}
}
}

/**
* Transforms a list of internal [EtInstruction]s to a list of api [Instruction]s.
*/
private fun List<EtInstruction>.toApiCoverage(instructionsCount: Long? = null): Coverage =
Coverage(
map { Instruction(it.className, it.methodSignature, it.line, it.id) },
instructionsCount
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.framework.concrete
package org.utbot.framework.concrete.constructors

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ConstructorId
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
package org.utbot.framework.concrete
package org.utbot.framework.concrete.constructors

import java.io.Closeable
import java.lang.reflect.Modifier
import java.util.IdentityHashMap
import kotlin.reflect.KClass
import org.mockito.Mockito
import org.mockito.stubbing.Answer
import org.objectweb.asm.Type
import org.utbot.common.Reflection
import org.utbot.common.invokeCatching
import org.utbot.engine.util.lambda.CapturedArgument
import org.utbot.engine.util.lambda.constructLambda
import org.utbot.engine.util.lambda.constructStaticLambda
import org.utbot.framework.concrete.mock.InstanceMockController
import org.utbot.framework.concrete.mock.InstrumentationContext
import org.utbot.framework.concrete.mock.MethodMockController
import org.utbot.framework.concrete.mock.MockController
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.ExecutableId
Expand All @@ -20,6 +35,7 @@ import org.utbot.framework.plugin.api.UtConcreteValue
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
import org.utbot.framework.plugin.api.UtEnumConstantModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtLambdaModel
import org.utbot.framework.plugin.api.UtMockValue
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
Expand All @@ -31,25 +47,12 @@ import org.utbot.framework.plugin.api.UtVoidModel
import org.utbot.framework.plugin.api.isMockModel
import org.utbot.framework.plugin.api.util.constructor
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.plugin.api.util.jField
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.method
import org.utbot.framework.plugin.api.util.utContext
import org.utbot.framework.util.anyInstance
import java.io.Closeable
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.util.IdentityHashMap
import kotlin.reflect.KClass
import org.mockito.Mockito
import org.mockito.stubbing.Answer
import org.objectweb.asm.Type
import org.utbot.common.Reflection
import org.utbot.engine.util.lambda.CapturedArgument
import org.utbot.engine.util.lambda.constructLambda
import org.utbot.engine.util.lambda.constructStaticLambda
import org.utbot.framework.plugin.api.UtLambdaModel
import org.utbot.framework.plugin.api.util.isStatic
import org.utbot.instrumentation.process.runSandbox

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.utbot.framework.concrete
package org.utbot.framework.concrete.constructors

import java.util.Optional
import java.util.OptionalDouble
import java.util.OptionalInt
import java.util.OptionalLong
import kotlin.reflect.KFunction1
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtAssembleModel
Expand All @@ -10,11 +15,6 @@ import org.utbot.framework.plugin.api.util.intClassId
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.framework.plugin.api.util.longClassId
import org.utbot.framework.plugin.api.util.objectClassId
import java.util.Optional
import java.util.OptionalDouble
import java.util.OptionalInt
import java.util.OptionalLong
import kotlin.reflect.KFunction1


internal sealed class OptionalConstructorBase : UtAssembleModelConstructorBase() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.framework.concrete
package org.utbot.framework.concrete.constructors

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.UtAssembleModel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.framework.concrete
package org.utbot.framework.concrete.constructors

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.MethodId
Expand Down
Loading

0 comments on commit b5f40fc

Please sign in to comment.