Skip to content

Commit faf3bc9

Browse files
authored
Handle Number.isNaN (#266)
1 parent 93871de commit faf3bc9

File tree

2 files changed

+61
-23
lines changed

2 files changed

+61
-23
lines changed

usvm-ts/src/main/kotlin/org/usvm/machine/TsContext.kt

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,22 @@ import org.jacodb.ets.base.EtsUnionType
1313
import org.jacodb.ets.base.EtsUnknownType
1414
import org.jacodb.ets.model.EtsScene
1515
import org.usvm.UAddressSort
16+
import org.usvm.UBoolExpr
1617
import org.usvm.UBoolSort
1718
import org.usvm.UBv32Sort
1819
import org.usvm.UConcreteHeapRef
1920
import org.usvm.UContext
2021
import org.usvm.UExpr
22+
import org.usvm.UHeapRef
2123
import org.usvm.USort
24+
import org.usvm.api.typeStreamOf
2225
import org.usvm.collection.field.UFieldLValue
2326
import org.usvm.isTrue
2427
import org.usvm.machine.expr.TsUndefinedSort
2528
import org.usvm.machine.expr.TsUnresolvedSort
2629
import org.usvm.machine.interpreter.TsStepScope
2730
import org.usvm.machine.types.FakeType
31+
import org.usvm.types.UTypeStream
2832
import org.usvm.types.single
2933
import org.usvm.util.mkFieldLValue
3034
import kotlin.contracts.ExperimentalContracts
@@ -44,8 +48,11 @@ class TsContext(
4448
* In TS we treat undefined value as a null reference in other objects.
4549
* For real null represented in the language we create another reference.
4650
*/
47-
private val undefinedValue: UExpr<UAddressSort>
48-
get() = mkNullRef()
51+
private val undefinedValue: UExpr<UAddressSort> = mkNullRef()
52+
fun mkUndefinedValue(): UExpr<UAddressSort> = undefinedValue
53+
54+
private val nullValue: UConcreteHeapRef = mkConcreteHeapRef(addressCounter.freshStaticAddress())
55+
fun mkTsNullValue(): UConcreteHeapRef = nullValue
4956

5057
fun typeToSort(type: EtsType): USort = when (type) {
5158
is EtsBooleanType -> boolSort
@@ -59,6 +66,14 @@ class TsContext(
5966
else -> TODO("Support all JacoDB types, encountered $type")
6067
}
6168

69+
fun UHeapRef.getTypeStream(scope: TsStepScope): UTypeStream<EtsType> =
70+
scope.calcOnState {
71+
memory.typeStreamOf(this@getTypeStream)
72+
}
73+
74+
fun UConcreteHeapRef.getFakeType(scope: TsStepScope): FakeType =
75+
getTypeStream(scope).single() as FakeType
76+
6277
@OptIn(ExperimentalContracts::class)
6378
fun UExpr<out USort>.isFakeObject(): Boolean {
6479
contract {
@@ -136,22 +151,35 @@ class TsContext(
136151
return mkConcreteHeapRef(address)
137152
}
138153

139-
fun mkUndefinedValue(): UExpr<UAddressSort> = undefinedValue
140-
141-
fun mkTsNullValue(): UConcreteHeapRef = nullValue
142-
private val nullValue: UConcreteHeapRef = mkConcreteHeapRef(addressCounter.freshStaticAddress())
143-
144154
fun getIntermediateBoolLValue(addr: Int): UFieldLValue<IntermediateLValueField, UBoolSort> {
155+
require(addr > MAGIC_OFFSET)
145156
return mkFieldLValue(boolSort, mkConcreteHeapRef(addr), IntermediateLValueField.BOOL)
146157
}
147158

148159
fun getIntermediateFpLValue(addr: Int): UFieldLValue<IntermediateLValueField, KFp64Sort> {
160+
require(addr > MAGIC_OFFSET)
149161
return mkFieldLValue(fp64Sort, mkConcreteHeapRef(addr), IntermediateLValueField.FP)
150162
}
151163

152164
fun getIntermediateRefLValue(addr: Int): UFieldLValue<IntermediateLValueField, UAddressSort> {
165+
require(addr > MAGIC_OFFSET)
153166
return mkFieldLValue(addressSort, mkConcreteHeapRef(addr), IntermediateLValueField.REF)
154167
}
168+
169+
fun UConcreteHeapRef.extractBool(scope: TsStepScope): UBoolExpr {
170+
val lValue = getIntermediateBoolLValue(address)
171+
return scope.calcOnState { memory.read(lValue) }
172+
}
173+
174+
fun UConcreteHeapRef.extractFp(scope: TsStepScope): UExpr<KFp64Sort> {
175+
val lValue = getIntermediateFpLValue(address)
176+
return scope.calcOnState { memory.read(lValue) }
177+
}
178+
179+
fun UConcreteHeapRef.extractRef(scope: TsStepScope): UHeapRef {
180+
val lValue = getIntermediateRefLValue(address)
181+
return scope.calcOnState { memory.read(lValue) }
182+
}
155183
}
156184

157185
const val MAGIC_OFFSET = 1000000

usvm-ts/src/main/kotlin/org/usvm/machine/expr/TsExprResolver.kt

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import org.jacodb.ets.model.EtsFieldSignature
7777
import org.jacodb.ets.model.EtsMethod
7878
import org.jacodb.ets.model.EtsMethodSignature
7979
import org.usvm.UAddressSort
80+
import org.usvm.UBoolExpr
8081
import org.usvm.UBoolSort
8182
import org.usvm.UExpr
8283
import org.usvm.UHeapRef
@@ -404,25 +405,34 @@ class TsExprResolver(
404405

405406
// region CALL
406407

408+
private fun handleNumberIsNaN(arg: UExpr<out USort>): UBoolExpr? = with(ctx) {
409+
// 21.1.2.4 Number.isNaN ( number )
410+
// 1. If number is not a Number, return false.
411+
// 2. If number is NaN, return true.
412+
// 3. Otherwise, return false.
413+
414+
if (arg.isFakeObject()) {
415+
val fakeType = arg.getFakeType(scope)
416+
val value = arg.extractFp(scope)
417+
return mkIte(
418+
condition = fakeType.fpTypeExpr,
419+
trueBranch = mkFpIsNaNExpr(value),
420+
falseBranch = mkFalse(),
421+
)
422+
}
423+
424+
if (arg.sort == fp64Sort) {
425+
mkFpIsNaNExpr(arg.asExpr(fp64Sort))
426+
} else {
427+
mkFalse()
428+
}
429+
}
430+
407431
override fun visit(expr: EtsInstanceCallExpr): UExpr<out USort>? = with(ctx) {
408432
if (expr.instance.name == "Number") {
409433
if (expr.method.name == "isNaN") {
410-
val arg = resolve(expr.args.single()) ?: return null
411-
if (arg.sort == fp64Sort) {
412-
return mkFpIsNaNExpr(arg.asExpr(fp64Sort))
413-
}
414-
if (arg.isFakeObject()) {
415-
val fakeType = scope.calcOnState {
416-
memory.types.getTypeStream(arg).single() as FakeType
417-
}
418-
scope.calcOnState {
419-
if (fakeType.fpTypeExpr.isTrue) {
420-
val lValue = getIntermediateFpLValue(arg.address)
421-
val value = memory.read(lValue).asExpr(fp64Sort)
422-
return@calcOnState mkFpIsNaNExpr(value)
423-
}
424-
null
425-
}?.let { return it }
434+
return resolveAfterResolved(expr.args.single()) { arg ->
435+
handleNumberIsNaN(arg)
426436
}
427437
}
428438
}

0 commit comments

Comments
 (0)