@@ -76,19 +76,25 @@ import org.usvm.api.mapTypeStreamNotNull
7676import org.usvm.api.memcpy
7777import org.usvm.api.objectTypeEquals
7878import org.usvm.api.objectTypeSubtype
79+ import org.usvm.api.readArrayIndex
80+ import org.usvm.api.readArrayLength
7981import org.usvm.api.readField
8082import org.usvm.api.writeField
8183import org.usvm.collection.array.UArrayIndexLValue
8284import org.usvm.collection.array.length.UArrayLengthLValue
8385import org.usvm.collection.field.UFieldLValue
86+ import org.usvm.getIntValue
8487import org.usvm.jvm.util.allInstanceFields
8588import org.usvm.jvm.util.javaName
8689import org.usvm.machine.interpreter.JcExprResolver
8790import org.usvm.machine.interpreter.JcStepScope
8891import org.usvm.machine.mocks.mockMethod
8992import org.usvm.machine.state.JcState
9093import org.usvm.machine.state.newStmt
94+ import org.usvm.machine.state.skipMethodInvocationAndBoxIfNeeded
9195import org.usvm.machine.state.skipMethodInvocationWithValue
96+ import org.usvm.memory.foldHeapRefWithStaticAsConcrete
97+ import org.usvm.mkSizeExpr
9298import org.usvm.sizeSort
9399import org.usvm.types.first
94100import org.usvm.types.singleOrNull
@@ -571,6 +577,84 @@ class JcMethodApproximationResolver(
571577 return false
572578 }
573579
580+ private fun JcState.arrayContentEquals (
581+ firstArray : UHeapRef ,
582+ secondArray : UHeapRef ,
583+ firstLength : UExpr <USizeSort >,
584+ secondLength : UExpr <USizeSort >,
585+ arrayType : JcArrayType ,
586+ ): UBoolExpr ? = with (ctx) {
587+ val arrayDesciptor = arrayDescriptorOf(arrayType)
588+ val elementType = arrayType.elementType
589+ val elementSort = typeToSort(elementType)
590+
591+ val concreteLength =
592+ getIntValue(firstLength)
593+ ? : getIntValue(secondLength)
594+ ? : return @with null
595+
596+ val arrayEquals = List (concreteLength) {
597+ val idx = mkSizeExpr(it)
598+ val first = memory.readArrayIndex(firstArray, idx, arrayDesciptor, elementSort)
599+ val second =
600+ memory.readArrayIndex(secondArray, idx, arrayDesciptor, elementSort)
601+ mkEq(first, second)
602+ }
603+
604+ return @with mkAnd(arrayEquals)
605+ }
606+
607+ private fun JcState.arrayEquals (methodCall : JcMethodCall , firstArray : UHeapRef , secondArray : UHeapRef ) = with (ctx) {
608+ val possibleElementTypes = primitiveTypes + cp.objectType
609+ val possibleArrayTypes = possibleElementTypes.map { cp.arrayTypeOf(it) }
610+
611+ val branches = mutableListOf<Pair <UBoolExpr , (JcState ) - > Unit >> ()
612+ var typeDiffersConstraint: UBoolExpr = trueExpr
613+
614+ val arrayRefsEqual = mkEq(firstArray, secondArray)
615+ val oneArrayIsNull = mkOr(mkEq(firstArray, nullRef), mkEq(secondArray, nullRef))
616+ branches + = arrayRefsEqual to { state ->
617+ state.skipMethodInvocationAndBoxIfNeeded(methodCall, cp.boolean, trueExpr)
618+ }
619+ branches + = mkAnd(mkNot(arrayRefsEqual), oneArrayIsNull) to { state ->
620+ state.skipMethodInvocationAndBoxIfNeeded(methodCall, cp.boolean, falseExpr)
621+ }
622+ val needToCheckContent = mkAnd(mkNot(arrayRefsEqual), mkNot(oneArrayIsNull))
623+ for (arrayType in possibleArrayTypes) {
624+ val typeConstraint = scope.calcOnState {
625+ mkAnd(
626+ memory.types.evalIsSubtype(firstArray, arrayType),
627+ memory.types.evalIsSubtype(secondArray, arrayType)
628+ )
629+ }
630+ typeDiffersConstraint = mkAnd(typeDiffersConstraint, mkNot(typeConstraint))
631+ val arrayDesciptor = arrayDescriptorOf(arrayType)
632+ val firstLength = memory.readArrayLength(firstArray, arrayDesciptor, sizeSort)
633+ val secondLength = memory.readArrayLength(secondArray, arrayDesciptor, sizeSort)
634+ val lengthsEqual = mkEq(firstLength, secondLength)
635+
636+ branches + = mkAnd(needToCheckContent, typeConstraint, mkNot(lengthsEqual)) to { state ->
637+ state.skipMethodInvocationAndBoxIfNeeded(methodCall, cp.boolean, falseExpr)
638+ }
639+
640+ branches + = mkAnd(needToCheckContent, typeConstraint, lengthsEqual) to { state ->
641+ val checkResult = state.arrayContentEquals(firstArray, secondArray, firstLength, secondLength, arrayType)
642+ if (checkResult == null ) {
643+ // Unable to check
644+ state.skipMethodInvocationWithValue(methodCall, nullRef)
645+ } else {
646+ state.skipMethodInvocationAndBoxIfNeeded(methodCall, cp.boolean, checkResult)
647+ }
648+ }
649+ }
650+
651+ branches + = typeDiffersConstraint to { state ->
652+ state.skipMethodInvocationAndBoxIfNeeded(methodCall, cp.boolean, falseExpr)
653+ }
654+
655+ scope.forkMulti(branches)
656+ }
657+
574658 private sealed interface StringConcatElement
575659 private data class StringConcatStrElement (val str : String ) : StringConcatElement
576660 private data class StringConcatRefElement (val ref : UHeapRef ) : StringConcatElement
@@ -956,6 +1040,19 @@ class JcMethodApproximationResolver(
9561040 val arg = it.arguments.single().asExpr(ctx.booleanSort)
9571041 scope.assert (arg)?.let { ctx.voidValue }
9581042 }
1043+ dispatchUsvmApiMethod(Engine ::assumeSymbolic) {
1044+ val instance = it.arguments[0 ].asExpr(ctx.addressSort)
1045+ val condition = it.arguments[1 ].asExpr(ctx.booleanSort)
1046+ foldHeapRefWithStaticAsConcrete<Unit ?>(
1047+ ref = instance,
1048+ initial = Unit ,
1049+ initialGuard = ctx.trueExpr,
1050+ ignoreNullRefs = true ,
1051+ collapseHeapRefs = true ,
1052+ blockOnConcrete = { _, _ -> Unit },
1053+ blockOnSymbolic = { acc, ref -> scope.assert (ctx.mkImplies(ref.guard, condition)) ? : acc }
1054+ )?.let { ctx.voidValue }
1055+ }
9591056 dispatchUsvmApiMethod(Engine ::makeSymbolicBoolean) {
9601057 scope.calcOnState { makeSymbolicPrimitive(ctx.booleanSort) }
9611058 }
@@ -1098,6 +1195,12 @@ class JcMethodApproximationResolver(
10981195 makeSymbolicArray(ctx.cp.objectType, sizeExpr)
10991196 }
11001197 }
1198+ dispatchUsvmApiMethod(Engine ::arrayEquals) {
1199+ val first = it.arguments[0 ].asExpr(ctx.addressSort)
1200+ val second = it.arguments[1 ].asExpr(ctx.addressSort)
1201+ scope.doWithState { arrayEquals(it, first, second) }
1202+ null
1203+ }
11011204 dispatchMkList(Engine ::makeSymbolicList) {
11021205 scope.calcOnState { mkSymbolicList(symbolicListType) }
11031206 }
0 commit comments