Skip to content

Commit 62f8400

Browse files
authored
Add mkNumericExpr (#255)
1 parent 4dccb2d commit 62f8400

File tree

5 files changed

+393
-29
lines changed

5 files changed

+393
-29
lines changed

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

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.usvm.machine.expr
22

3+
import io.ksmt.sort.KFp64Sort
34
import io.ksmt.utils.asExpr
45
import org.usvm.UBoolExpr
56
import org.usvm.UBoolSort
@@ -12,15 +13,17 @@ import org.usvm.machine.interpreter.TsStepScope
1213
import org.usvm.machine.types.ExprWithTypeConstraint
1314
import org.usvm.machine.types.FakeType
1415
import org.usvm.types.single
16+
import org.usvm.util.boolToFp
1517

16-
fun TsContext.mkTruthyExpr(expr: UExpr<out USort>, scope: TsStepScope): UBoolExpr = scope.calcOnState {
18+
fun TsContext.mkTruthyExpr(
19+
expr: UExpr<out USort>,
20+
scope: TsStepScope,
21+
): UBoolExpr = scope.calcOnState {
1722
if (expr.isFakeObject()) {
1823
val falseBranchGround = makeSymbolicPrimitive(boolSort)
1924

2025
val conjuncts = mutableListOf<ExprWithTypeConstraint<UBoolSort>>()
21-
val possibleType = scope.calcOnState {
22-
memory.types.getTypeStream(expr.asExpr(addressSort))
23-
}.single() as FakeType
26+
val possibleType = memory.types.getTypeStream(expr.asExpr(addressSort)).single() as FakeType
2427

2528
scope.assert(possibleType.mkExactlyOneTypeConstraint(this@mkTruthyExpr))
2629

@@ -82,3 +85,43 @@ fun TsContext.mkTruthyExpr(expr: UExpr<out USort>, scope: TsStepScope): UBoolExp
8285
}
8386
}
8487
}
88+
89+
fun TsContext.mkNumericExpr(
90+
expr: UExpr<out USort>,
91+
scope: TsStepScope,
92+
): UExpr<KFp64Sort> = scope.calcOnState {
93+
94+
// 7.1.4 ToNumber ( argument )
95+
//
96+
// 1. If argument is a Number, return argument.
97+
// 2. If argument is either a Symbol or a BigInt, throw a TypeError exception.
98+
// 3. If argument is undefined, return NaN.
99+
// 4. If argument is either null or false, return +0𝔽.
100+
// 5. If argument is true, return 1𝔽.
101+
// 6. If argument is a String, return StringToNumber(argument).
102+
// 7. Assert: argument is an Object.
103+
// 8. Let primValue be ToPrimitive(argument, "number").
104+
// 9. Assert: primValue is not an Object.
105+
// 10. Return ToNumber(primValue).
106+
107+
if (expr.sort == fp64Sort) {
108+
return@calcOnState expr.asExpr(fp64Sort)
109+
}
110+
111+
if (expr == mkUndefinedValue()) {
112+
return@calcOnState mkFp64NaN()
113+
}
114+
115+
if (expr == mkTsNullValue()) {
116+
return@calcOnState mkFp64(0.0)
117+
}
118+
119+
if (expr.sort == boolSort) {
120+
return@calcOnState boolToFp(expr.asExpr(boolSort))
121+
}
122+
123+
// TODO: ToPrimitive, then ToNumber again
124+
// TODO: probably we need to implement Object (Ref/Fake) -> Number conversion here directly, without ToPrimitive
125+
126+
error("Unsupported sort: ${expr.sort}")
127+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,13 @@ class TsExprResolver(
424424
}
425425

426426
override fun visit(expr: EtsStaticCallExpr): UExpr<out USort>? {
427+
if (expr.method.name == "Number" && expr.method.enclosingClass.name == "") {
428+
check(expr.args.size == 1) { "Number constructor should have exactly one argument" }
429+
return resolveAfterResolved(expr.args.single()) {
430+
ctx.mkNumericExpr(it, scope)
431+
}
432+
}
433+
427434
return resolveInvoke(
428435
method = expr.method,
429436
instance = null,

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

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ package org.usvm.machine.operator
33
import io.ksmt.sort.KFp64Sort
44
import io.ksmt.utils.asExpr
55
import org.usvm.UAddressSort
6-
import org.usvm.UBoolSort
6+
import org.usvm.UBoolExpr
77
import org.usvm.UConcreteHeapRef
88
import org.usvm.UExpr
99
import org.usvm.USort
1010
import org.usvm.machine.TsContext
11+
import org.usvm.machine.expr.mkNumericExpr
1112
import org.usvm.machine.expr.mkTruthyExpr
1213
import org.usvm.machine.interpreter.TsStepScope
1314

1415
sealed interface TsUnaryOperator {
1516

1617
fun TsContext.resolveBool(
17-
arg: UExpr<UBoolSort>,
18+
arg: UBoolExpr,
1819
scope: TsStepScope,
1920
): UExpr<out USort>
2021

@@ -50,70 +51,61 @@ sealed interface TsUnaryOperator {
5051

5152
data object Not : TsUnaryOperator {
5253
override fun TsContext.resolveBool(
53-
arg: UExpr<UBoolSort>,
54+
arg: UBoolExpr,
5455
scope: TsStepScope,
55-
): UExpr<out USort> {
56+
): UBoolExpr {
5657
return mkNot(arg)
5758
}
5859

5960
override fun TsContext.resolveFp(
6061
arg: UExpr<KFp64Sort>,
6162
scope: TsStepScope,
62-
): UExpr<out USort> {
63+
): UBoolExpr {
6364
return mkNot(mkTruthyExpr(arg, scope))
6465
}
6566

6667
override fun TsContext.resolveRef(
6768
arg: UExpr<UAddressSort>,
6869
scope: TsStepScope,
69-
): UExpr<out USort> {
70+
): UBoolExpr {
7071
return mkNot(mkTruthyExpr(arg, scope))
7172
}
7273

7374
override fun TsContext.resolveFake(
7475
arg: UConcreteHeapRef,
7576
scope: TsStepScope,
76-
): UExpr<out USort> {
77-
TODO("Not yet implemented")
77+
): UBoolExpr {
78+
return mkNot(mkTruthyExpr(arg, scope))
7879
}
7980
}
8081

8182
data object Neg : TsUnaryOperator {
8283
override fun TsContext.resolveBool(
83-
arg: UExpr<UBoolSort>,
84+
arg: UBoolExpr,
8485
scope: TsStepScope,
85-
): UExpr<out USort> {
86-
// -true = -1.0
87-
// -false = -0.0
88-
@Suppress("MagicNumber")
89-
return mkIte(arg, mkFp64(-1.0), mkFp64(-0.0))
86+
): UExpr<KFp64Sort> {
87+
return mkFpNegationExpr(mkNumericExpr(arg, scope))
9088
}
9189

9290
override fun TsContext.resolveFp(
9391
arg: UExpr<KFp64Sort>,
9492
scope: TsStepScope,
95-
): UExpr<out USort> {
93+
): UExpr<KFp64Sort> {
9694
return mkFpNegationExpr(arg)
9795
}
9896

9997
override fun TsContext.resolveRef(
10098
arg: UExpr<UAddressSort>,
10199
scope: TsStepScope,
102-
): UExpr<out USort> {
103-
// -undefined = NaN
104-
if (arg == mkUndefinedValue()) {
105-
return mkFp64NaN()
106-
}
107-
108-
// TODO: convert to numeric value and then negate
109-
TODO("Not yet implemented")
100+
): UExpr<KFp64Sort> {
101+
return mkFpNegationExpr(mkNumericExpr(arg, scope))
110102
}
111103

112104
override fun TsContext.resolveFake(
113105
arg: UConcreteHeapRef,
114106
scope: TsStepScope,
115-
): UExpr<out USort> {
116-
TODO("Not yet implemented")
107+
): UExpr<KFp64Sort> {
108+
return mkFpNegationExpr(mkNumericExpr(arg, scope))
117109
}
118110
}
119111
}

0 commit comments

Comments
 (0)