Skip to content

Commit 3639bfb

Browse files
committed
Fix strict equality for null and undefined
1 parent fd05b02 commit 3639bfb

5 files changed

Lines changed: 78 additions & 10 deletions

File tree

usvm-ts/src/main/kotlin/org/usvm/machine/operator/TsBinaryOperator.kt

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -421,28 +421,28 @@ sealed interface TsBinaryOperator {
421421
if (lhs.sort == boolSort && rhs.sort == boolSort) {
422422
val lhs = lhs.asExpr(boolSort)
423423
val rhs = rhs.asExpr(boolSort)
424-
return mkEq(lhs, rhs)
424+
return onBool(lhs, rhs, scope)
425425
}
426426

427427
// fp == fp
428428
if (lhs.sort == fp64Sort && rhs.sort == fp64Sort) {
429429
val lhs = lhs.asExpr(fp64Sort)
430430
val rhs = rhs.asExpr(fp64Sort)
431-
return mkFpEqualExpr(lhs, rhs)
431+
return onFp(lhs, rhs, scope)
432432
}
433433

434434
// bool == fp
435435
if (lhs.sort == boolSort && rhs.sort == fp64Sort) {
436436
val lhs = lhs.asExpr(boolSort)
437437
val rhs = rhs.asExpr(fp64Sort)
438-
return mkFpEqualExpr(boolToFp(lhs), rhs)
438+
return onFp(boolToFp(lhs), rhs, scope)
439439
}
440440

441441
// fp == bool
442442
if (lhs.sort == fp64Sort && rhs.sort == boolSort) {
443443
val lhs = lhs.asExpr(fp64Sort)
444444
val rhs = rhs.asExpr(boolSort)
445-
return mkFpEqualExpr(lhs, boolToFp(rhs))
445+
return onFp(lhs, boolToFp(rhs), scope)
446446
}
447447

448448
// ref == ref
@@ -578,7 +578,7 @@ sealed interface TsBinaryOperator {
578578
lhsType.boolTypeExpr eq rhsType.boolTypeExpr,
579579
lhsType.fpTypeExpr eq rhsType.fpTypeExpr,
580580
// TODO support type equality
581-
lhsType.refTypeExpr eq rhsType.refTypeExpr
581+
lhsType.refTypeExpr eq rhsType.refTypeExpr,
582582
)
583583
}
584584

@@ -633,11 +633,23 @@ sealed interface TsBinaryOperator {
633633
}
634634
}
635635

636-
val loosyEqualityConstraint = with(Eq) {
636+
check(!lhsValue.isFakeObject())
637+
check(!rhsValue.isFakeObject())
638+
639+
if (lhsValue.sort == addressSort && rhsValue.sort == addressSort) {
640+
val left = lhsValue.asExpr(addressSort)
641+
val right = rhsValue.asExpr(addressSort)
642+
return mkAnd(
643+
typeConstraint,
644+
mkHeapRefEq(left, right)
645+
)
646+
}
647+
648+
val looseEqualityConstraint = with(Eq) {
637649
resolve(lhsValue, rhsValue, scope)?.asExpr(boolSort) ?: error("Should not be encountered")
638650
}
639651

640-
return mkAnd(typeConstraint, loosyEqualityConstraint)
652+
return mkAnd(typeConstraint, looseEqualityConstraint)
641653
}
642654

643655
override fun TsContext.internalResolve(

usvm-ts/src/test/kotlin/org/usvm/samples/lang/Null.kt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package org.usvm.samples.lang
22

33
import org.jacodb.ets.model.EtsScene
4-
import org.junit.jupiter.api.Test
54
import org.usvm.api.TsTestValue
65
import org.usvm.util.TsMethodTestRunner
76
import org.usvm.util.eq
7+
import kotlin.test.Test
88

99
class Null : TsMethodTestRunner() {
1010
private val tsPath = "/samples/lang/Null.ts"
@@ -16,8 +16,29 @@ class Null : TsMethodTestRunner() {
1616
val method = getMethod("isNull")
1717
discoverProperties<TsTestValue, TsTestValue.TsNumber>(
1818
method,
19-
{ a, r -> (r eq 1) && (a is TsTestValue.TsNull) },
20-
{ a, r -> (r eq 2) && (a !is TsTestValue.TsNull) },
19+
{ a, r ->
20+
(r eq 1) && (a is TsTestValue.TsNull)
21+
},
22+
{ a, r ->
23+
(r eq 2) && (a !is TsTestValue.TsNull)
24+
},
25+
)
26+
}
27+
28+
@Test
29+
fun `test isNullOrUndefined`() {
30+
val method = getMethod("isNullOrUndefined")
31+
discoverProperties<TsTestValue, TsTestValue.TsNumber>(
32+
method,
33+
{ a, r ->
34+
(r eq 1) && (a is TsTestValue.TsNull)
35+
},
36+
{ a, r ->
37+
(r eq 2) && (a is TsTestValue.TsUndefined)
38+
},
39+
{ a, r ->
40+
(r eq 3) && (a !is TsTestValue.TsNull) && (a !is TsTestValue.TsUndefined)
41+
},
2142
)
2243
}
2344
}

usvm-ts/src/test/kotlin/org/usvm/samples/lang/Undefined.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,21 @@ class Undefined : TsMethodTestRunner() {
2424
},
2525
)
2626
}
27+
28+
@Test
29+
fun `test isUndefinedOrNull`() {
30+
val method = getMethod("isUndefinedOrNull")
31+
discoverProperties<TsTestValue, TsTestValue.TsNumber>(
32+
method,
33+
{ a, r ->
34+
(r eq 1) && (a is TsTestValue.TsUndefined)
35+
},
36+
{ a, r ->
37+
(r eq 2) && (a is TsTestValue.TsNull)
38+
},
39+
{ a, r ->
40+
(r eq 3) && (a !is TsTestValue.TsUndefined) && (a !is TsTestValue.TsNull)
41+
},
42+
)
43+
}
2744
}

usvm-ts/src/test/resources/samples/lang/Null.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,13 @@ class Null {
66
if (x === null) return 1;
77
return 2;
88
}
9+
10+
isNullOrUndefined(x): number {
11+
if (x == null) {
12+
if (x === null) return 1;
13+
if (x === undefined) return 2;
14+
return -1; // unreachable
15+
}
16+
return 3; // not null or undefined
17+
}
918
}

usvm-ts/src/test/resources/samples/lang/Undefined.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,13 @@ class Undefined {
66
if (x === undefined) return 1;
77
return 2;
88
}
9+
10+
isUndefinedOrNull(x): number {
11+
if (x == undefined) {
12+
if (x === undefined) return 1;
13+
if (x === null) return 2;
14+
return -1; // unreachable
15+
}
16+
return 3; // not null or undefined
17+
}
918
}

0 commit comments

Comments
 (0)