Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Support anonymous classes
  • Loading branch information
ArsenHD authored and Damtev committed Sep 2, 2022
commit 976d42019c96f7271b10c832b730c3a057bb4eee
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.utbot.framework.plugin.api.util.method
import org.utbot.framework.plugin.api.util.primitiveTypeJvmNameOrNull
import org.utbot.framework.plugin.api.util.safeJField
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.ArrayType
Expand Down Expand Up @@ -697,8 +698,14 @@ open class ClassId @JvmOverloads constructor(
*/
val prettifiedName: String
get() {
val className = jClass.canonicalName ?: name // Explicit jClass reference to get null instead of exception
return className
val baseName = when {
// anonymous classes have empty simpleName and their canonicalName is null,
// so we create a specific name for them
isAnonymous -> "Anonymous${supertypeOfAnonymousClass.prettifiedName}"
// in other cases where canonical name is still null, we use ClassId.name instead
else -> jClass.canonicalName ?: name // Explicit jClass reference to get null instead of exception
}
return baseName
.substringAfterLast(".")
.replace(Regex("[^a-zA-Z0-9]"), "")
.let { if (this.isArray) it + "Array" else it }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,34 @@ infix fun ClassId.isSubtypeOf(type: ClassId): Boolean {

infix fun ClassId.isNotSubtypeOf(type: ClassId): Boolean = !(this isSubtypeOf type)

/**
* - Anonymous class that extends a class will have this class as its superclass and no interfaces.
* - Anonymous class that implements an interface, will have the only interface
* and [java.lang.Object] as its superclass.
*
* @return [ClassId] of a type that the given anonymous class inherits
*/
val ClassId.supertypeOfAnonymousClass: ClassId
get() {
if (this is BuiltinClassId) error("Cannot obtain info about supertypes of BuiltinClassId $canonicalName")
if (!isAnonymous) error("An anonymous class expected, but got $canonicalName")

val clazz = jClass
val superclass = clazz.superclass.id
val interfaces = clazz.interfaces.map { it.id }

return when {
// anonymous class actually inherits from Object, e.g. Object obj = new Object() { ... };
superclass == objectClassId && interfaces.isEmpty() -> objectClassId
// anonymous class implements some interface
superclass == objectClassId -> {
interfaces.singleOrNull() ?: error("Anonymous class can have no more than one interface")
}
// anonymous class inherits from some class other than java.lang.Object
else -> superclass
}
}

val ClassId.kClass: KClass<*>
get() = jClass.kotlin

Expand Down
11 changes: 10 additions & 1 deletion utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3418,9 +3418,18 @@ class Traverser(
if (returnValue != null) {
queuedSymbolicStateUpdates += constructConstraintForType(returnValue, returnValue.possibleConcreteTypes).asSoftConstraint()

// We only remove anonymous classes if there are regular classes available.
// If there are no other options, then we do use anonymous classes.
workaround(REMOVE_ANONYMOUS_CLASSES) {
val sootClass = returnValue.type.sootClass
if (!environment.state.isInNestedMethod() && (sootClass.isAnonymous || sootClass.isArtificialEntity)) {
val isInNestedMethod = environment.state.isInNestedMethod()

if (!isInNestedMethod && sootClass.isArtificialEntity) {
return
}

val onlyAnonymousTypesAvailable = returnValue.typeStorage.possibleConcreteTypes.all { (it as? RefType)?.sootClass?.isAnonymous == true }
if (!isInNestedMethod && sootClass.isAnonymous && !onlyAnonymousTypesAvailable) {
return
}
}
Expand Down
25 changes: 11 additions & 14 deletions utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.utbot.engine

import org.utbot.common.WorkaroundReason
import org.utbot.common.heuristic
import org.utbot.common.workaround
import org.utbot.engine.pc.UtAddrExpression
import org.utbot.engine.pc.UtBoolExpression
Expand Down Expand Up @@ -200,19 +199,17 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy
val leastCommonSootClass = (leastCommonType as? RefType)?.sootClass
val keepArtificialEntities = leastCommonSootClass?.isArtificialEntity == true

heuristic(WorkaroundReason.REMOVE_ANONYMOUS_CLASSES) {
possibleConcreteTypes.forEach {
val sootClass = (it.baseType as? RefType)?.sootClass ?: run {
// All not RefType should be included in the concreteTypes, e.g., arrays
concreteTypes += it
return@forEach
}
when {
sootClass.isAnonymous || sootClass.isUtMock -> unwantedTypes += it
sootClass.isArtificialEntity -> if (keepArtificialEntities) concreteTypes += it else Unit
workaround(WorkaroundReason.HACK) { leastCommonSootClass == OBJECT_TYPE && sootClass.isOverridden } -> Unit
else -> concreteTypes += it
}
possibleConcreteTypes.forEach {
val sootClass = (it.baseType as? RefType)?.sootClass ?: run {
// All not RefType should be included in the concreteTypes, e.g., arrays
concreteTypes += it
return@forEach
}
when {
sootClass.isAnonymous || sootClass.isUtMock -> unwantedTypes += it
sootClass.isArtificialEntity -> if (keepArtificialEntities) concreteTypes += it else Unit
workaround(WorkaroundReason.HACK) { leastCommonSootClass == OBJECT_TYPE && sootClass.isOverridden } -> Unit
else -> concreteTypes += it
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ import mu.KotlinLogging
import org.utbot.analytics.EngineAnalyticsContext
import org.utbot.analytics.FeatureProcessor
import org.utbot.analytics.Predictors
import org.utbot.common.WorkaroundReason.REMOVE_ANONYMOUS_CLASSES
import org.utbot.common.bracket
import org.utbot.common.debug
import org.utbot.common.workaround
import org.utbot.engine.MockStrategy.NO_MOCKS
import org.utbot.engine.pc.UtArraySelectExpression
import org.utbot.engine.pc.UtBoolExpression
Expand Down Expand Up @@ -74,7 +72,6 @@ import org.utbot.framework.plugin.api.UtNullModel
import org.utbot.framework.plugin.api.UtOverflowFailure
import org.utbot.framework.plugin.api.UtResult
import org.utbot.framework.plugin.api.UtSymbolicExecution
import org.utbot.framework.plugin.api.onSuccess
import org.utbot.framework.util.graph
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.plugin.api.util.id
Expand Down Expand Up @@ -473,15 +470,6 @@ class UtBotSymbolicEngine(
// in case an exception occurred from the concrete execution
concreteExecutionResult ?: return@forEach

workaround(REMOVE_ANONYMOUS_CLASSES) {
concreteExecutionResult.result.onSuccess {
if (it.classId.isAnonymous) {
logger.debug("Anonymous class found as a concrete result, symbolic one will be returned")
return@flow
}
}
}

val coveredInstructions = concreteExecutionResult.coverage.coveredInstructions
if (coveredInstructions.isNotEmpty()) {
val coverageKey = coveredInstructionTracker.add(coveredInstructions)
Expand Down Expand Up @@ -591,16 +579,6 @@ class UtBotSymbolicEngine(
instrumentation
)

workaround(REMOVE_ANONYMOUS_CLASSES) {
concreteExecutionResult.result.onSuccess {
if (it.classId.isAnonymous) {
logger.debug("Anonymous class found as a concrete result, symbolic one will be returned")
emit(symbolicUtExecution)
return
}
}
}

val concolicUtExecution = symbolicUtExecution.copy(
stateAfter = concreteExecutionResult.stateAfter,
result = concreteExecutionResult.result,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ class AssembleModelGenerator(private val methodUnderTest: UtMethod<*>) {
}

return IdentityHashMap<UtModel, UtModel>().apply {
models.forEach { getOrPut(it) { assembleModel(it) } }
models
.filterNot { it.classId.isAnonymous } // we cannot create an assemble model for an anonymous class instance
.forEach { getOrPut(it) { assembleModel(it) } }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import org.utbot.framework.plugin.api.util.intClassId
import org.utbot.framework.plugin.api.util.isArray
import org.utbot.framework.plugin.api.util.isPrimitiveWrapperOrString
import org.utbot.framework.plugin.api.util.stringClassId
import org.utbot.framework.plugin.api.util.supertypeOfAnonymousClass
import org.utbot.framework.plugin.api.util.wrapperByPrimitive
import java.lang.reflect.Field
import java.lang.reflect.Modifier
Expand Down Expand Up @@ -118,7 +119,9 @@ internal class CgVariableConstructor(val context: CgContext) :
val obj = if (model.isMock) {
mockFrameworkManager.createMockFor(model, baseName)
} else {
newVar(model.classId, baseName) { utilsClassId[createInstance](model.classId.name) }
val modelType = model.classId
val variableType = if (modelType.isAnonymous) modelType.supertypeOfAnonymousClass else modelType
newVar(variableType, baseName) { utilsClassId[createInstance](model.classId.name) }
}

valueByModelId[model.id] = obj
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ internal class CgStatementConstructorImpl(context: CgContext) :
val isGetFieldUtilMethod = (expression is CgMethodCall && expression.executableId.isGetFieldUtilMethod)
val shouldCastBeSafety = expression == nullLiteral() || isGetFieldUtilMethod

type = baseType
expr = typeCast(baseType, expression, shouldCastBeSafety)
type = expr.type
}
expression.type isNotSubtypeOf baseType && !typeAccessible -> {
type = if (expression.type.isArray) objectArrayClassId else objectClassId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import org.utbot.framework.plugin.api.util.builtinStaticMethodId
import org.utbot.framework.plugin.api.util.methodId
import org.utbot.framework.plugin.api.util.objectArrayClassId
import org.utbot.framework.plugin.api.util.objectClassId
import org.utbot.framework.plugin.api.util.supertypeOfAnonymousClass

internal data class EnvironmentFieldStateCache(
val thisInstance: FieldStateCache,
Expand Down Expand Up @@ -255,15 +256,21 @@ internal fun CgContextOwner.importIfNeeded(method: MethodId) {
/**
* Casts [expression] to [targetType].
*
* If [targetType] is anonymous, then we cannot use it in type cast,
* because anonymous classes cannot be accessed in the source code.
* So, in this case we find the supertype of anonymous class and use it as the [targetType] instead.
*
* @param isSafetyCast shows if we should render "as?" instead of "as" in Kotlin
*/
internal fun CgContextOwner.typeCast(
targetType: ClassId,
expression: CgExpression,
isSafetyCast: Boolean = false
): CgTypeCast {
if (targetType.simpleName.isEmpty()) {
error("Cannot cast an expression to the anonymous type $targetType")
): CgExpression {
@Suppress("NAME_SHADOWING")
val targetType = when {
targetType.isAnonymous -> targetType.supertypeOfAnonymousClass
else -> targetType
}
importIfNeeded(targetType)
return CgTypeCast(targetType, expression, isSafetyCast)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.utbot.framework.codegen.model.util

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.isArray

/**
* For now we will count class accessible if it is:
Expand All @@ -14,19 +13,13 @@ import org.utbot.framework.plugin.api.util.isArray
* @param packageName name of the package we check accessibility from
*/
infix fun ClassId.isAccessibleFrom(packageName: String): Boolean {

if (this.isLocal || this.isSynthetic) {
return false
val isOuterClassAccessible = if (isNested) {
outerClass!!.id.isAccessibleFrom(packageName)
} else {
true
}

val outerClassId = outerClass?.id
if (outerClassId != null && !outerClassId.isAccessibleFrom(packageName)) {
return false
}
val isAccessibleFromPackageByModifiers = isPublic || (this.packageName == packageName && (isPackagePrivate || isProtected))

return if (this.isArray) {
elementClassId!!.isAccessibleFrom(packageName)
} else {
isPublic || (this.packageName == packageName && (isPackagePrivate || isProtected))
}
return isOuterClassAccessible && isAccessibleFromPackageByModifiers && !isLocal && !isAnonymous && !isSynthetic
}