|
17 | 17 | import com.google.common.collect.ImmutableList; |
18 | 18 | import com.google.common.collect.ImmutableSet; |
19 | 19 | import com.google.common.collect.Iterables; |
| 20 | +import com.google.common.collect.Streams; |
20 | 21 | import com.microsoft.z3.ArithExpr; |
21 | 22 | import com.microsoft.z3.ArrayExpr; |
22 | 23 | import com.microsoft.z3.BoolExpr; |
@@ -83,6 +84,8 @@ final class CelAstToZ3Translator { |
83 | 84 | private static final String EMPTY_LIST_PREFIX = "!empty_list"; |
84 | 85 | private static final String EMPTY_MAP_PREFIX = "!empty_map"; |
85 | 86 | private static final String NULL_VALUE_FIELD = "null_value"; |
| 87 | + private static final String SECONDS_FIELD = "seconds"; |
| 88 | + private static final String NANOS_FIELD = "nanos"; |
86 | 89 | private static final long MAX_UINT32 = 0xFFFFFFFFL; |
87 | 90 | private final Context ctx; |
88 | 91 | private final CelZ3TypeSystem typeSystem; |
@@ -406,7 +409,12 @@ private TranslatedValue translateStruct(CelExpr celExpr, CelAbstractSyntaxTree a |
406 | 409 | if (CelTypes.isWrapperType(createStruct.messageName())) { |
407 | 410 | return translateWrapperWktStruct(celExpr, createStruct, ast); |
408 | 411 | } |
409 | | - // TODO: Support google.protobuf.Timestamp and google.protobuf.Duration. |
| 412 | + if (createStruct.messageName().equals(CelTypes.TIMESTAMP_MESSAGE)) { |
| 413 | + return translateTimestampWktStruct(celExpr, createStruct, ast); |
| 414 | + } |
| 415 | + if (createStruct.messageName().equals(CelTypes.DURATION_MESSAGE)) { |
| 416 | + return translateDurationWktStruct(celExpr, createStruct, ast); |
| 417 | + } |
410 | 418 |
|
411 | 419 | // Bypass SMT when the struct is empty (return the cached SMT default pointer) |
412 | 420 | if (createStruct.entries().isEmpty()) { |
@@ -599,6 +607,105 @@ private TranslatedValue translateWrapperWktStruct( |
599 | 607 | return TranslatedValue.propagateStrict(ctx, typeSystem, currentVal, celExpr, entryTvs); |
600 | 608 | } |
601 | 609 |
|
| 610 | + @FunctionalInterface |
| 611 | + private interface TimeWktValueFactory { |
| 612 | + Expr<?> create(IntExpr seconds, IntExpr nanos, IntExpr totalNanos); |
| 613 | + } |
| 614 | + |
| 615 | + private TranslatedValue translateTimestampWktStruct( |
| 616 | + CelExpr celExpr, CelExpr.CelStruct createStruct, CelAbstractSyntaxTree ast) { |
| 617 | + return translateTimeWktStruct( |
| 618 | + celExpr, |
| 619 | + createStruct, |
| 620 | + ast, |
| 621 | + /* minNanos= */ 0, |
| 622 | + (seconds, nanos, totalNanos) -> |
| 623 | + typeSystem.withRuntimeError( |
| 624 | + typeSystem.wrapTimestamp(totalNanos), |
| 625 | + typeSystem.checkTimestampOverflow(totalNanos))); |
| 626 | + } |
| 627 | + |
| 628 | + private TranslatedValue translateDurationWktStruct( |
| 629 | + CelExpr celExpr, CelExpr.CelStruct createStruct, CelAbstractSyntaxTree ast) { |
| 630 | + return translateTimeWktStruct( |
| 631 | + celExpr, |
| 632 | + createStruct, |
| 633 | + ast, |
| 634 | + CelZ3TypeSystem.MIN_DURATION_NANOS, |
| 635 | + (seconds, nanos, totalNanos) -> { |
| 636 | + BoolExpr signMismatch = |
| 637 | + ctx.mkOr( |
| 638 | + ctx.mkAnd(ctx.mkGt(seconds, ctx.mkInt(0)), ctx.mkLt(nanos, ctx.mkInt(0))), |
| 639 | + ctx.mkAnd(ctx.mkLt(seconds, ctx.mkInt(0)), ctx.mkGt(nanos, ctx.mkInt(0)))); |
| 640 | + return typeSystem.withRuntimeError( |
| 641 | + typeSystem.wrapDuration(totalNanos), |
| 642 | + signMismatch, |
| 643 | + typeSystem.checkDurationOverflow(totalNanos)); |
| 644 | + }); |
| 645 | + } |
| 646 | + |
| 647 | + private TranslatedValue translateTimeWktStruct( |
| 648 | + CelExpr celExpr, |
| 649 | + CelExpr.CelStruct createStruct, |
| 650 | + CelAbstractSyntaxTree ast, |
| 651 | + int minNanos, |
| 652 | + TimeWktValueFactory valueFactory) { |
| 653 | + List<TranslatedValue> entryTvs = new ArrayList<>(); |
| 654 | + Expr<?> secondsVal = |
| 655 | + resolveStructFieldValue(createStruct, SECONDS_FIELD, typeSystem.mkInt(0), ast, entryTvs); |
| 656 | + Expr<?> nanosVal = |
| 657 | + resolveStructFieldValue(createStruct, NANOS_FIELD, typeSystem.mkInt(0), ast, entryTvs); |
| 658 | + |
| 659 | + IntExpr seconds = typeSystem.getInt(secondsVal); |
| 660 | + IntExpr nanos = typeSystem.getInt(nanosVal); |
| 661 | + IntExpr totalNanos = |
| 662 | + (IntExpr) |
| 663 | + ctx.mkAdd(ctx.mkMul(seconds, ctx.mkInt(CelZ3TypeSystem.NANOS_PER_SECOND)), nanos); |
| 664 | + |
| 665 | + BoolExpr typeError = |
| 666 | + ctx.mkOr(ctx.mkNot(typeSystem.isInt(secondsVal)), ctx.mkNot(typeSystem.isInt(nanosVal))); |
| 667 | + BoolExpr nanosOutOfRange = |
| 668 | + ctx.mkOr( |
| 669 | + ctx.mkLt(nanos, ctx.mkInt(minNanos)), |
| 670 | + ctx.mkGe(nanos, ctx.mkInt(CelZ3TypeSystem.NANOS_PER_SECOND))); |
| 671 | + |
| 672 | + Expr<?> result = |
| 673 | + typeSystem.withRuntimeError( |
| 674 | + valueFactory.create(seconds, nanos, totalNanos), typeError, nanosOutOfRange); |
| 675 | + return TranslatedValue.propagateStrict(ctx, typeSystem, result, celExpr, entryTvs); |
| 676 | + } |
| 677 | + |
| 678 | + private Expr<?> resolveStructFieldValue( |
| 679 | + CelExpr.CelStruct createStruct, |
| 680 | + String fieldName, |
| 681 | + Expr<?> defaultValue, |
| 682 | + CelAbstractSyntaxTree ast, |
| 683 | + List<TranslatedValue> entryTvs) { |
| 684 | + return Streams.findLast( |
| 685 | + createStruct.entries().stream().filter(entry -> entry.fieldKey().equals(fieldName))) |
| 686 | + .<Expr<?>>map( |
| 687 | + entry -> { |
| 688 | + TranslatedValue entryTv = translateExpr(entry.value(), ast); |
| 689 | + Expr<?> val = entryTv.z3Expr(); |
| 690 | + if (entry.optionalEntry()) { |
| 691 | + Expr<?> checkedVal = |
| 692 | + typeSystem.withRuntimeError(val, ctx.mkNot(typeSystem.isOptional(val))); |
| 693 | + entryTv = |
| 694 | + TranslatedValue.create( |
| 695 | + checkedVal, entry.value(), typeSystem, entryTv.isApproximate()); |
| 696 | + Expr<?> optRef = typeSystem.getOptionalRef(checkedVal); |
| 697 | + val = |
| 698 | + ctx.mkITE( |
| 699 | + typeSystem.optHasValue(optRef), |
| 700 | + typeSystem.getOptionalValue(optRef), |
| 701 | + defaultValue); |
| 702 | + } |
| 703 | + entryTvs.add(entryTv); |
| 704 | + return val; |
| 705 | + }) |
| 706 | + .orElse(defaultValue); |
| 707 | + } |
| 708 | + |
602 | 709 | private Expr<?> getDefaultValueForType(CelType type) { |
603 | 710 | if (type instanceof NullableType) { |
604 | 711 | return typeSystem.mkNull(); |
@@ -1392,14 +1499,14 @@ private BoolExpr createTypeConstraintForType(Expr<?> val, CelType type) { |
1392 | 1499 | return typeSystem.isBytes(val); |
1393 | 1500 | } |
1394 | 1501 | if (type.equals(SimpleType.TIMESTAMP)) { |
1395 | | - IntExpr seconds = typeSystem.getTimestamp(val); |
| 1502 | + IntExpr nanos = typeSystem.getTimestamp(val); |
1396 | 1503 | return ctx.mkAnd( |
1397 | | - typeSystem.isTimestamp(val), ctx.mkNot(typeSystem.checkTimestampOverflow(seconds))); |
| 1504 | + typeSystem.isTimestamp(val), ctx.mkNot(typeSystem.checkTimestampOverflow(nanos))); |
1398 | 1505 | } |
1399 | 1506 | if (type.equals(SimpleType.DURATION)) { |
1400 | | - IntExpr seconds = typeSystem.getDuration(val); |
| 1507 | + IntExpr nanos = typeSystem.getDuration(val); |
1401 | 1508 | return ctx.mkAnd( |
1402 | | - typeSystem.isDuration(val), ctx.mkNot(typeSystem.checkDurationOverflow(seconds))); |
| 1509 | + typeSystem.isDuration(val), ctx.mkNot(typeSystem.checkDurationOverflow(nanos))); |
1403 | 1510 | } |
1404 | 1511 |
|
1405 | 1512 | if (type instanceof ListType) { |
|
0 commit comments