@@ -15,8 +15,6 @@ import org.jacodb.ets.model.EtsBitOrExpr
1515import org.jacodb.ets.model.EtsBitXorExpr
1616import org.jacodb.ets.model.EtsBooleanConstant
1717import org.jacodb.ets.model.EtsCastExpr
18- import org.jacodb.ets.model.EtsClassSignature
19- import org.jacodb.ets.model.EtsClassType
2018import org.jacodb.ets.model.EtsConstant
2119import org.jacodb.ets.model.EtsDeleteExpr
2220import org.jacodb.ets.model.EtsDivExpr
@@ -99,10 +97,13 @@ import org.usvm.machine.state.TsState
9997import org.usvm.machine.state.lastStmt
10098import org.usvm.machine.state.localsCount
10199import org.usvm.machine.state.newStmt
102- import org.usvm.machine.types.AuxiliaryType
100+ import org.usvm.machine.types.EtsAuxiliaryType
103101import org.usvm.machine.types.mkFakeValue
104102import org.usvm.memory.ULValue
105103import org.usvm.sizeSort
104+ import org.usvm.util.EtsFieldResolutionResult
105+ import org.usvm.util.EtsHierarchy
106+ import org.usvm.util.createFakeField
106107import org.usvm.util.isResolved
107108import org.usvm.util.mkArrayIndexLValue
108109import org.usvm.util.mkArrayLengthLValue
@@ -125,7 +126,8 @@ const val ADHOC_STRING__STRING = 2222.0 // 'string'
125126class TsExprResolver (
126127 private val ctx : TsContext ,
127128 private val scope : TsStepScope ,
128- private val localToIdx : (EtsMethod , EtsValue ) -> Int ,
129+ private val localToIdx : (EtsMethod , EtsValue ) -> Int? ,
130+ private val hierarchy : EtsHierarchy ,
129131) : EtsEntity.Visitor<UExpr<out USort>?> {
130132
131133 val simpleValueResolver: TsSimpleValueResolver =
@@ -630,24 +632,33 @@ class TsExprResolver(
630632 instance : EtsLocal ? ,
631633 instanceRef : UHeapRef ,
632634 field : EtsFieldSignature ,
635+ hierarchy : EtsHierarchy ,
633636 ): UExpr <out USort >? = with (ctx) {
634637 val resolvedAddr = if (instanceRef.isFakeObject()) instanceRef.extractRef(scope) else instanceRef
635638 scope.doWithState {
636- pathConstraints + = if (field.enclosingClass != EtsClassSignature .UNKNOWN ) {
637- // If we know an enclosing class of the field,
638- // we can add a type constraint about the instance type.
639- // Probably, it's redundant since either both class and field
640- // know exactly their types or none of them.
641- val type = EtsClassType (field.enclosingClass)
642- memory.types.evalIsSubtype(resolvedAddr, type)
643- } else {
644- // Otherwise, we add a type constraint that every type containing such field is fine
645- memory.types.evalIsSubtype(resolvedAddr, AuxiliaryType (setOf (field.name)))
646- }
639+ // If we accessed some field, we make an assumption that
640+ // this field should present in the object.
641+ // That's not true in the common case for TS, but that's the decision we made.
642+ val auxiliaryType = EtsAuxiliaryType (properties = setOf (field.name))
643+ pathConstraints + = memory.types.evalIsSubtype(resolvedAddr, auxiliaryType)
647644 }
648645
649- val etsField = resolveEtsField(instance, field)
650- val sort = typeToSort(etsField.type)
646+ val etsField = resolveEtsField(instance, field, hierarchy)
647+
648+ val sort = when (etsField) {
649+ is EtsFieldResolutionResult .Empty -> {
650+ logger.error(" Field $etsField not found, creating fake field" )
651+ // If we didn't find any real fields, let's create a fake one.
652+ // It is possible due to mistakes in the IR or if the field was added explicitly
653+ // in the code.
654+ // Probably, the right behaviour here is to fork the state.
655+ resolvedAddr.createFakeField(field.name, scope)
656+ addressSort
657+ }
658+
659+ is EtsFieldResolutionResult .Unique -> typeToSort(etsField.field.type)
660+ is EtsFieldResolutionResult .Ambiguous -> unresolvedSort
661+ }
651662
652663 if (sort == unresolvedSort) {
653664 val boolLValue = mkFieldLValue(boolSort, instanceRef, field)
@@ -714,7 +725,7 @@ class TsExprResolver(
714725 }
715726 }
716727
717- return handleFieldRef(value.instance, instanceRef, value.field)
728+ return handleFieldRef(value.instance, instanceRef, value.field, hierarchy )
718729 }
719730
720731 override fun visit (value : EtsStaticFieldRef ): UExpr <out USort >? = with (ctx) {
@@ -747,7 +758,7 @@ class TsExprResolver(
747758 }
748759 }
749760
750- return handleFieldRef(instance = null , instanceRef, value.field)
761+ return handleFieldRef(instance = null , instanceRef, value.field, hierarchy )
751762 }
752763
753764 // endregion
@@ -811,14 +822,39 @@ class TsExprResolver(
811822class TsSimpleValueResolver (
812823 private val ctx : TsContext ,
813824 private val scope : TsStepScope ,
814- private val localToIdx : (EtsMethod , EtsValue ) -> Int ,
825+ private val localToIdx : (EtsMethod , EtsValue ) -> Int? ,
815826) : EtsValue.Visitor<UExpr<out USort>?> {
816827
817828 private fun resolveLocal (local : EtsValue ): ULValue <* , USort > {
818829 val currentMethod = scope.calcOnState { lastEnteredMethod }
819830 val entrypoint = scope.calcOnState { entrypoint }
820831
821832 val localIdx = localToIdx(currentMethod, local)
833+
834+ // If there is no local variable corresponding to the local,
835+ // we treat it as a field of some global object with the corresponding name.
836+ // It helps us to support global variables that are missed in the IR.
837+ if (localIdx == null ) {
838+ require(local is EtsLocal )
839+
840+ val globalObject = scope.calcOnState { globalObject }
841+
842+ val localName = local.name
843+ // Check whether this local was already created or not
844+ if (localName in scope.calcOnState { addedArtificialLocals }) {
845+ return mkFieldLValue(ctx.addressSort, globalObject, local.name)
846+ }
847+
848+ logger.warn { " Cannot resolve local $local " }
849+
850+ globalObject.createFakeField(localName, scope)
851+ scope.doWithState {
852+ addedArtificialLocals + = localName
853+ }
854+
855+ return mkFieldLValue(ctx.addressSort, globalObject, local.name)
856+ }
857+
822858 val sort = scope.calcOnState {
823859 val type = local.tryGetKnownType(currentMethod)
824860 getOrPutSortForLocal(localIdx, type)
0 commit comments