|
31 | 31 | import com.microsoft.z3.SeqExpr; |
32 | 32 | import com.microsoft.z3.SeqSort; |
33 | 33 | import com.microsoft.z3.Sort; |
| 34 | +import dev.cel.common.internal.ProtoTimeUtils; |
34 | 35 | import java.util.ArrayList; |
35 | 36 | import java.util.Arrays; |
36 | 37 | import java.util.Collection; |
@@ -82,6 +83,14 @@ public final class CelZ3TypeSystem { |
82 | 83 | private static final String IS_BYTES = "isBytes"; |
83 | 84 | private static final String GET_BYTES = "getBytes"; |
84 | 85 |
|
| 86 | + private static final String CONS_TIMESTAMP = "Timestamp"; |
| 87 | + private static final String IS_TIMESTAMP = "isTimestamp"; |
| 88 | + private static final String GET_TIMESTAMP = "getTimestamp"; |
| 89 | + |
| 90 | + private static final String CONS_DURATION = "Duration"; |
| 91 | + private static final String IS_DURATION = "isDuration"; |
| 92 | + private static final String GET_DURATION = "getDuration"; |
| 93 | + |
85 | 94 | private static final String CONS_ERROR = "CelError"; |
86 | 95 | private static final String IS_ERROR = "isError"; |
87 | 96 |
|
@@ -170,6 +179,8 @@ public int hashCode() { |
170 | 179 | private final Constructor doubleCons; |
171 | 180 | private final Constructor stringCons; |
172 | 181 | private final Constructor bytesCons; |
| 182 | + private final Constructor timestampCons; |
| 183 | + private final Constructor durationCons; |
173 | 184 | private final Constructor errorCons; |
174 | 185 | private final Constructor unknownCons; |
175 | 186 | private final Constructor nullCons; |
@@ -233,30 +244,38 @@ public Sort listRefSort() { |
233 | 244 | return listRefSort; |
234 | 245 | } |
235 | 246 |
|
236 | | - Constructor boolCons() { |
| 247 | + public Constructor boolCons() { |
237 | 248 | return boolCons; |
238 | 249 | } |
239 | 250 |
|
240 | | - Constructor intCons() { |
| 251 | + public Constructor intCons() { |
241 | 252 | return intCons; |
242 | 253 | } |
243 | 254 |
|
244 | | - Constructor uintCons() { |
| 255 | + public Constructor uintCons() { |
245 | 256 | return uintCons; |
246 | 257 | } |
247 | 258 |
|
248 | | - Constructor doubleCons() { |
| 259 | + public Constructor doubleCons() { |
249 | 260 | return doubleCons; |
250 | 261 | } |
251 | 262 |
|
252 | | - Constructor stringCons() { |
| 263 | + public Constructor stringCons() { |
253 | 264 | return stringCons; |
254 | 265 | } |
255 | 266 |
|
256 | | - Constructor bytesCons() { |
| 267 | + public Constructor bytesCons() { |
257 | 268 | return bytesCons; |
258 | 269 | } |
259 | 270 |
|
| 271 | + public Constructor timestampCons() { |
| 272 | + return timestampCons; |
| 273 | + } |
| 274 | + |
| 275 | + public Constructor durationCons() { |
| 276 | + return durationCons; |
| 277 | + } |
| 278 | + |
260 | 279 | Constructor optionalCons() { |
261 | 280 | return optionalCons; |
262 | 281 | } |
@@ -296,6 +315,16 @@ public Expr<?> wrapBytes(Expr<?> expr) { |
296 | 315 | return ctx.mkApp(bytesCons.ConstructorDecl(), expr); |
297 | 316 | } |
298 | 317 |
|
| 318 | + /** Wraps a Z3 integer expression into a timestamp CelValue. */ |
| 319 | + public Expr<?> wrapTimestamp(IntExpr expr) { |
| 320 | + return ctx.mkApp(timestampCons.ConstructorDecl(), expr); |
| 321 | + } |
| 322 | + |
| 323 | + /** Wraps a Z3 integer expression into a duration CelValue. */ |
| 324 | + public Expr<?> wrapDuration(IntExpr expr) { |
| 325 | + return ctx.mkApp(durationCons.ConstructorDecl(), expr); |
| 326 | + } |
| 327 | + |
299 | 328 | /** Creates a CelValue containing an integer. */ |
300 | 329 | public Expr<?> mkInt(long val) { |
301 | 330 | return ctx.mkApp(intCons.ConstructorDecl(), ctx.mkInt(val)); |
@@ -326,8 +355,8 @@ public BoolExpr isDouble(Expr<?> val) { |
326 | 355 | } |
327 | 356 |
|
328 | 357 | /** Extracts the double reference from a double CelValue. */ |
329 | | - public Expr<?> getDouble(Expr<?> val) { |
330 | | - return ctx.mkApp(doubleCons.getAccessorDecls()[0], val); |
| 358 | + public FPExpr getDouble(Expr<?> val) { |
| 359 | + return (FPExpr) ctx.mkApp(doubleCons.getAccessorDecls()[0], val); |
331 | 360 | } |
332 | 361 |
|
333 | 362 | /** |
@@ -372,7 +401,7 @@ public BoolExpr getStructuralEquality(Expr<?> arg0, Expr<?> arg1) { |
372 | 401 | // Doubles must be compared using native floating-point equality to follow IEEE-754. |
373 | 402 | // Z3's structural mkEq evaluates NaN == NaN as true and 0.0 == -0.0 as false. |
374 | 403 | BoolExpr isDoubleEq = ctx.mkAnd(isDouble(arg0), isDouble(arg1)); |
375 | | - BoolExpr doubleEq = ctx.mkFPEq((FPExpr) getDouble(arg0), (FPExpr) getDouble(arg1)); |
| 404 | + BoolExpr doubleEq = ctx.mkFPEq(getDouble(arg0), getDouble(arg1)); |
376 | 405 |
|
377 | 406 | // For primitives, generic equality matches the direct Z3 datatype wrapper. |
378 | 407 | BoolExpr genericEq = ctx.mkEq(arg0, arg1); |
@@ -409,10 +438,14 @@ public Expr<?> mkNull() { |
409 | 438 | return ctx.mkConst(nullCons.ConstructorDecl()); |
410 | 439 | } |
411 | 440 |
|
412 | | - Constructor errorCons() { |
| 441 | + public Constructor errorCons() { |
413 | 442 | return errorCons; |
414 | 443 | } |
415 | 444 |
|
| 445 | + public Constructor nullCons() { |
| 446 | + return nullCons; |
| 447 | + } |
| 448 | + |
416 | 449 | /** Creates a CelValue representing an unknown value. */ |
417 | 450 | public Expr<?> mkUnknown() { |
418 | 451 | return mkUnknown(ctx.mkConst(GENERIC_UNKNOWN_ID, unknownIdSort)); |
@@ -498,7 +531,7 @@ public Expr<?> withRuntimeError( |
498 | 531 | return ctx.mkITE(condition, mkError(), result); |
499 | 532 | } |
500 | 533 |
|
501 | | - Constructor unknownCons() { |
| 534 | + public Constructor unknownCons() { |
502 | 535 | return unknownCons; |
503 | 536 | } |
504 | 537 |
|
@@ -582,6 +615,26 @@ public IntExpr getUint(Expr<?> val) { |
582 | 615 | return (IntExpr) ctx.mkApp(uintCons.getAccessorDecls()[0], val); |
583 | 616 | } |
584 | 617 |
|
| 618 | + /** Checks if the given CelValue is a timestamp. */ |
| 619 | + public BoolExpr isTimestamp(Expr<?> val) { |
| 620 | + return (BoolExpr) ctx.mkApp(timestampCons.getTesterDecl(), val); |
| 621 | + } |
| 622 | + |
| 623 | + /** Extracts the integer expression from a timestamp CelValue. */ |
| 624 | + public IntExpr getTimestamp(Expr<?> val) { |
| 625 | + return (IntExpr) ctx.mkApp(timestampCons.getAccessorDecls()[0], val); |
| 626 | + } |
| 627 | + |
| 628 | + /** Checks if the given CelValue is a duration. */ |
| 629 | + public BoolExpr isDuration(Expr<?> val) { |
| 630 | + return (BoolExpr) ctx.mkApp(durationCons.getTesterDecl(), val); |
| 631 | + } |
| 632 | + |
| 633 | + /** Extracts the integer expression from a duration CelValue. */ |
| 634 | + public IntExpr getDuration(Expr<?> val) { |
| 635 | + return (IntExpr) ctx.mkApp(durationCons.getAccessorDecls()[0], val); |
| 636 | + } |
| 637 | + |
585 | 638 | /** Checks if the given CelValue is a string. */ |
586 | 639 | public BoolExpr isString(Expr<?> val) { |
587 | 640 | return (BoolExpr) ctx.mkApp(stringCons.getTesterDecl(), val); |
@@ -719,6 +772,20 @@ public BoolExpr checkIntOverflow(ArithExpr result) { |
719 | 772 | return ctx.mkOr(ctx.mkGt(result, ctx.mkInt(MAX_INT64)), ctx.mkLt(result, ctx.mkInt(MIN_INT64))); |
720 | 773 | } |
721 | 774 |
|
| 775 | + /** Checks if the given arithmetic expression overflows CEL Timestamp bounds. */ |
| 776 | + public BoolExpr checkTimestampOverflow(ArithExpr result) { |
| 777 | + return ctx.mkOr( |
| 778 | + ctx.mkGt(result, ctx.mkInt(ProtoTimeUtils.TIMESTAMP_SECONDS_MAX)), |
| 779 | + ctx.mkLt(result, ctx.mkInt(ProtoTimeUtils.TIMESTAMP_SECONDS_MIN))); |
| 780 | + } |
| 781 | + |
| 782 | + /** Checks if the given arithmetic expression overflows CEL Duration bounds. */ |
| 783 | + public BoolExpr checkDurationOverflow(ArithExpr result) { |
| 784 | + return ctx.mkOr( |
| 785 | + ctx.mkGt(result, ctx.mkInt(ProtoTimeUtils.DURATION_SECONDS_MAX)), |
| 786 | + ctx.mkLt(result, ctx.mkInt(ProtoTimeUtils.DURATION_SECONDS_MIN))); |
| 787 | + } |
| 788 | + |
722 | 789 | /** Checks if the given arithmetic expression overflows a 64-bit unsigned integer. */ |
723 | 790 | public BoolExpr checkUintOverflow(ArithExpr result) { |
724 | 791 | return ctx.mkOr(ctx.mkGt(result, ctx.mkInt(MAX_UINT64)), ctx.mkLt(result, ctx.mkInt(0))); |
@@ -890,6 +957,20 @@ public static BoolExpr mkNotFlattened(Context ctx, BoolExpr arg) { |
890 | 957 | this.bytesCons = |
891 | 958 | ctx.mkConstructor( |
892 | 959 | CONS_BYTES, IS_BYTES, new String[] {GET_BYTES}, new Sort[] {ctx.getStringSort()}, null); |
| 960 | + this.timestampCons = |
| 961 | + ctx.mkConstructor( |
| 962 | + CONS_TIMESTAMP, |
| 963 | + IS_TIMESTAMP, |
| 964 | + new String[] {GET_TIMESTAMP}, |
| 965 | + new Sort[] {ctx.getIntSort()}, |
| 966 | + null); |
| 967 | + this.durationCons = |
| 968 | + ctx.mkConstructor( |
| 969 | + CONS_DURATION, |
| 970 | + IS_DURATION, |
| 971 | + new String[] {GET_DURATION}, |
| 972 | + new Sort[] {ctx.getIntSort()}, |
| 973 | + null); |
893 | 974 | this.errorCons = ctx.mkConstructor(CONS_ERROR, IS_ERROR, null, null, null); |
894 | 975 |
|
895 | 976 | this.unknownIdSort = ctx.mkUninterpretedSort("UnknownId"); |
@@ -936,6 +1017,8 @@ public static BoolExpr mkNotFlattened(Context ctx, BoolExpr arg) { |
936 | 1017 | this.doubleCons, |
937 | 1018 | this.stringCons, |
938 | 1019 | this.bytesCons, |
| 1020 | + this.timestampCons, |
| 1021 | + this.durationCons, |
939 | 1022 | this.errorCons, |
940 | 1023 | this.unknownCons, |
941 | 1024 | this.optionalCons, |
|
0 commit comments