diff --git a/core/src/main/java/org/projectnessie/cel/common/types/ListT.java b/core/src/main/java/org/projectnessie/cel/common/types/ListT.java index 6aa842e6..f40c6cd6 100644 --- a/core/src/main/java/org/projectnessie/cel/common/types/ListT.java +++ b/core/src/main/java/org/projectnessie/cel/common/types/ListT.java @@ -18,6 +18,7 @@ import static java.util.Arrays.asList; import static org.projectnessie.cel.common.types.BoolT.False; import static org.projectnessie.cel.common.types.BoolT.True; +import static org.projectnessie.cel.common.types.DoubleT.doubleOf; import static org.projectnessie.cel.common.types.Err.isError; import static org.projectnessie.cel.common.types.Err.newErr; import static org.projectnessie.cel.common.types.Err.newTypeConversionError; @@ -63,6 +64,22 @@ public static Val newGenericArrayList(TypeAdapter adapter, Object[] value) { return new GenericListT(adapter, value); } + public static Val newGenericList(TypeAdapter adapter, List value) { + return new ListBackedListT(adapter, value); + } + + public static Val newIntArrayList(TypeAdapter adapter, int[] value) { + return new IntArrayListT(adapter, value); + } + + public static Val newLongArrayList(TypeAdapter adapter, long[] value) { + return new LongArrayListT(adapter, value); + } + + public static Val newDoubleArrayList(TypeAdapter adapter, double[] value) { + return new DoubleArrayListT(adapter, value); + } + public static Val newValArrayList(TypeAdapter adapter, Val[] value) { return new ValListT(adapter, value); } @@ -225,6 +242,50 @@ public Val size() { return intOf(size); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Val)) { + return false; + } + return equal((Val) o) == True; + } + + @Override + public int hashCode() { + int result = 1; + for (long i = 0; i < size; i++) { + result = 31 * result + get(intOf(i)).hashCode(); + } + return result; + } + + int checkedIndex(Val index, int size) { + switch (index.type().typeEnum()) { + case Int: + case Uint: + break; + case Double: + double od = index.doubleValue(); + if (Math.rint(od) != od) { + throw new InvalidIndexException(newErr("invalid_argument")); + } + break; + default: + throw new InvalidIndexException( + valOrErr(index, "unsupported index type '%s' in list", index.type())); + } + int i = (int) index.intValue(); + if (i < 0 || i >= size) { + // Note: the conformance tests assert on 'invalid_argument' + throw new InvalidIndexException( + newErr("invalid_argument: index '%d' out of range in list of size '%d'", i, size)); + } + return i; + } + private final class ArrayListIteratorT extends BaseVal implements IteratorT { private long index; @@ -268,6 +329,19 @@ public Object value() { } } + private static final class InvalidIndexException extends RuntimeException { + private final Val error; + + private InvalidIndexException(Val error) { + this.error = error; + } + + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } + } + static final class GenericListT extends BaseListT { private final Object[] array; @@ -287,32 +361,26 @@ public Val add(Val other) { return noSuchOverload(this, "add", other); } Lister otherList = (Lister) other; - Object[] otherArray = (Object[]) otherList.value(); - Object[] newArray = Arrays.copyOf(array, array.length + otherArray.length); - System.arraycopy(otherArray, 0, newArray, array.length, otherArray.length); + int otherSize = (int) otherList.size().intValue(); + Object[] newArray = Arrays.copyOf(array, array.length + otherSize); + Class componentType = array.getClass().getComponentType(); + for (int i = 0; i < otherSize; i++) { + Val otherValue = otherList.get(intOf(i)); + newArray[array.length + i] = + componentType.isInstance(otherValue) + ? otherValue + : otherValue.convertToNative(componentType); + } return new GenericListT(adapter, newArray); } @Override public Val get(Val index) { - switch (index.type().typeEnum()) { - case Int: - case Uint: - break; - case Double: - double od = index.doubleValue(); - if (Math.rint(od) != od) { - return newErr("invalid_argument"); - } - break; - default: - return valOrErr(index, "unsupported index type '%s' in list", index.type()); - } - int sz = array.length; - int i = (int) index.intValue(); - if (i < 0 || i >= sz) { - // Note: the conformance tests assert on 'invalid_argument' - return newErr("invalid_argument: index '%d' out of range in list of size '%d'", i, sz); + int i; + try { + i = checkedIndex(index, array.length); + } catch (InvalidIndexException e) { + return e.error; } return adapter.nativeToValue(array[i]); @@ -331,6 +399,49 @@ public String toString() { } } + static final class ListBackedListT extends BaseListT { + private final List list; + + ListBackedListT(TypeAdapter adapter, List list) { + super(adapter, list.size()); + this.list = list; + } + + @Override + public Object value() { + return list; + } + + @Override + public Val add(Val other) { + if (!(other instanceof Lister)) { + return noSuchOverload(this, "add", other); + } + Lister otherList = (Lister) other; + int otherSize = (int) otherList.size().intValue(); + Object[] newArray = new Object[list.size() + otherSize]; + for (int i = 0; i < list.size(); i++) { + newArray[i] = list.get(i); + } + for (int i = 0; i < otherSize; i++) { + newArray[list.size() + i] = otherList.get(intOf(i)); + } + return new GenericListT(adapter, newArray); + } + + @Override + public Val get(Val index) { + int i; + try { + i = checkedIndex(index, list.size()); + } catch (InvalidIndexException e) { + return e.error; + } + + return adapter.nativeToValue(list.get(i)); + } + } + static final class ValListT extends BaseListT { private final Val[] array; @@ -371,47 +482,15 @@ public Val add(Val other) { @Override public Val get(Val index) { - switch (index.type().typeEnum()) { - case Int: - case Uint: - break; - case Double: - double od = index.doubleValue(); - if (Math.rint(od) != od) { - return newErr("invalid_argument"); - } - break; - default: - return valOrErr(index, "unsupported index type '%s' in list", index.type()); - } - int sz = array.length; - int i = (int) index.intValue(); - if (i < 0 || i >= sz) { - // Note: the conformance tests assert on 'invalid_argument' - return newErr("invalid_argument: index '%d' out of range in list of size '%d'", i, sz); + int i; + try { + i = checkedIndex(index, array.length); + } catch (InvalidIndexException e) { + return e.error; } return array[i]; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ValListT valListT = (ValListT) o; - return Arrays.equals(array, valListT.array); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + Arrays.hashCode(array); - return result; - } - @Override public String toString() { return "ValListT{" @@ -425,9 +504,107 @@ public String toString() { } } + abstract static class PrimitiveArrayListT extends BaseListT { + PrimitiveArrayListT(TypeAdapter adapter, long size) { + super(adapter, size); + } + + @Override + public Val add(Val other) { + if (!(other instanceof Lister)) { + return noSuchOverload(this, "add", other); + } + Lister otherLister = (Lister) other; + int thisSize = (int) size; + int otherSize = (int) otherLister.size().intValue(); + Val[] newArray = new Val[thisSize + otherSize]; + for (int i = 0; i < thisSize; i++) { + newArray[i] = get(intOf(i)); + } + for (int i = 0; i < otherSize; i++) { + newArray[thisSize + i] = otherLister.get(intOf(i)); + } + return new ValListT(adapter, newArray); + } + } + + static final class IntArrayListT extends PrimitiveArrayListT { + private final int[] array; + + IntArrayListT(TypeAdapter adapter, int[] array) { + super(adapter, array.length); + this.array = array; + } + + @Override + public Object value() { + return array; + } + + @Override + public Val get(Val index) { + int i; + try { + i = checkedIndex(index, array.length); + } catch (InvalidIndexException e) { + return e.error; + } + return intOf(array[i]); + } + } + + static final class LongArrayListT extends PrimitiveArrayListT { + private final long[] array; + + LongArrayListT(TypeAdapter adapter, long[] array) { + super(adapter, array.length); + this.array = array; + } + + @Override + public Object value() { + return array; + } + + @Override + public Val get(Val index) { + int i; + try { + i = checkedIndex(index, array.length); + } catch (InvalidIndexException e) { + return e.error; + } + return intOf(array[i]); + } + } + + static final class DoubleArrayListT extends PrimitiveArrayListT { + private final double[] array; + + DoubleArrayListT(TypeAdapter adapter, double[] array) { + super(adapter, array.length); + this.array = array; + } + + @Override + public Object value() { + return array; + } + + @Override + public Val get(Val index) { + int i; + try { + i = checkedIndex(index, array.length); + } catch (InvalidIndexException e) { + return e.error; + } + return doubleOf(array[i]); + } + } + /** NewJSONList returns a traits.Lister based on structpb.ListValue instance. */ public static Val newJSONList(TypeAdapter adapter, ListValue l) { - List vals = l.getValuesList(); - return newGenericArrayList(adapter, vals.toArray()); + return newGenericList(adapter, l.getValuesList()); } } diff --git a/core/src/main/java/org/projectnessie/cel/common/types/MapT.java b/core/src/main/java/org/projectnessie/cel/common/types/MapT.java index e9008eae..e15b7c43 100644 --- a/core/src/main/java/org/projectnessie/cel/common/types/MapT.java +++ b/core/src/main/java/org/projectnessie/cel/common/types/MapT.java @@ -55,7 +55,23 @@ public static Val newWrappedMap(TypeAdapter adapter, Map value) { return new ValMapT(adapter, value); } + @SuppressWarnings("unchecked") public static Val newMaybeWrappedMap(TypeAdapter adapter, Map value) { + boolean alreadyWrapped = true; + for (Map.Entry entry : value.entrySet()) { + if (!(entry.getKey() instanceof Val) || !(entry.getValue() instanceof Val)) { + alreadyWrapped = false; + break; + } + Val key = (Val) entry.getKey(); + if (key.type().typeEnum() == TypeEnum.Null) { + return newErr("unsupported key type"); + } + } + if (alreadyWrapped) { + return newWrappedMap(adapter, (Map) value); + } + Map newMap = new HashMap<>(value.size() * 4 / 3 + 1); for (Map.Entry entry : value.entrySet()) { Val k = adapter.nativeToValue(entry.getKey()); diff --git a/core/src/main/java/org/projectnessie/cel/common/types/pb/FieldDescription.java b/core/src/main/java/org/projectnessie/cel/common/types/pb/FieldDescription.java index fbfd2a3b..a4801d7a 100644 --- a/core/src/main/java/org/projectnessie/cel/common/types/pb/FieldDescription.java +++ b/core/src/main/java/org/projectnessie/cel/common/types/pb/FieldDescription.java @@ -15,6 +15,12 @@ */ package org.projectnessie.cel.common.types.pb; +import static org.projectnessie.cel.common.types.BoolT.True; +import static org.projectnessie.cel.common.types.Err.newTypeConversionError; +import static org.projectnessie.cel.common.types.Err.noMoreElements; +import static org.projectnessie.cel.common.types.IntT.intOf; +import static org.projectnessie.cel.common.types.TypeT.TypeType; +import static org.projectnessie.cel.common.types.Types.boolOf; import static org.projectnessie.cel.common.types.pb.PbTypeDescription.reflectTypeOf; import static org.projectnessie.cel.common.types.pb.PbTypeDescription.unwrapDynamic; @@ -31,12 +37,17 @@ import com.google.protobuf.Message; import com.google.protobuf.NullValue; import java.lang.reflect.Array; -import java.util.ArrayList; +import java.util.AbstractList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import org.projectnessie.cel.common.ULong; +import org.projectnessie.cel.common.types.IteratorT; +import org.projectnessie.cel.common.types.MapT; +import org.projectnessie.cel.common.types.ref.BaseVal; +import org.projectnessie.cel.common.types.ref.TypeAdapter; +import org.projectnessie.cel.common.types.ref.Val; /** FieldDescription holds metadata related to fields declared within a type. */ public final class FieldDescription extends Description { @@ -440,7 +451,17 @@ public boolean hasField(Object target) { } public Object getField(Object target) { - return getValueFromField(desc, (Message) target); + return getField(target, DefaultTypeAdapter.Instance); + } + + public Object getField(Object target, TypeAdapter adapter) { + Message message = (Message) target; + FieldDescriptor fd = fieldDescriptorFor(message); + Object value = message.getField(fd); + if (fd.isMapField() && value instanceof List) { + return new ProtoMapT(adapter, fd, (List) value); + } + return getValueFromField(fd, message); } public static Object getValueFromField(FieldDescriptor desc, Message message) { @@ -509,18 +530,204 @@ public static Object getValueFromField(FieldDescriptor desc, Message message) { || type == FieldDescriptor.Type.UINT64 || type == FieldDescriptor.Type.FIXED32 || type == FieldDescriptor.Type.FIXED64)) { - List result = new ArrayList<>(); - List repeated = (List) v; - for (Object o : repeated) { - ULong casted = ULong.valueOf(((Number) o).longValue()); - result.add(casted); - } - v = result; + v = new UnsignedLongList((List) v); } } return v; } + private FieldDescriptor fieldDescriptorFor(Message message) { + Descriptor messageDesc = message.getDescriptorForType(); + if (messageDesc == desc.getContainingType()) { + return desc; + } + return messageDesc.findFieldByName(name()); + } + + private static final class UnsignedLongList extends AbstractList { + private final List repeated; + + private UnsignedLongList(List repeated) { + this.repeated = repeated; + } + + @Override + public ULong get(int index) { + return ULong.valueOf(((Number) repeated.get(index)).longValue()); + } + + @Override + public int size() { + return repeated.size(); + } + } + + private static final class ProtoMapT extends MapT { + private final TypeAdapter adapter; + private final List entries; + private final FieldDescriptor keyDesc; + private final FieldDescriptor valueDesc; + + private ProtoMapT(TypeAdapter adapter, FieldDescriptor field, List entries) { + this.adapter = adapter; + this.entries = entries; + this.keyDesc = field.getMessageType().findFieldByNumber(1); + this.valueDesc = field.getMessageType().findFieldByNumber(2); + } + + @SuppressWarnings("unchecked") + @Override + public T convertToNative(Class typeDesc) { + return (T) MapT.newMaybeWrappedMap(adapter, toJavaMap()).convertToNative(typeDesc); + } + + @Override + public Val convertToType(org.projectnessie.cel.common.types.ref.Type typeValue) { + if (typeValue == MapT.MapType) { + return this; + } + if (typeValue == TypeType) { + return MapT.MapType; + } + return newTypeConversionError(MapT.MapType, typeValue); + } + + @Override + public IteratorT iterator() { + return new EntryKeyIterator(); + } + + @Override + public Val equal(Val other) { + return MapT.newMaybeWrappedMap(adapter, toJavaMap()).equal(other); + } + + @Override + public Object value() { + return toJavaMap(); + } + + @Override + public Val contains(Val value) { + return boolOf(find(value) != null); + } + + @Override + public Val get(Val index) { + return find(index); + } + + @Override + public Val size() { + return intOf(entries.size()); + } + + @Override + public Val find(Val key) { + for (Object entry : entries) { + Val candidate = adapter.nativeToValue(mapEntryKey(entry)); + if (candidate.equal(key) == True) { + return adapter.nativeToValue(mapEntryValue(entry)); + } + } + return null; + } + + private Map toJavaMap() { + Map map = new HashMap<>(entries.size() * 4 / 3 + 1); + for (Object entry : entries) { + map.put(mapEntryKey(entry), mapEntryValue(entry)); + } + return map; + } + + private Object mapEntryKey(Object entry) { + return normalizeUnsignedValue(entryKeyDescriptor(entry), rawMapEntryValue(entry, 1)); + } + + private Object mapEntryValue(Object entry) { + return normalizeUnsignedValue(entryValueDescriptor(entry), rawMapEntryValue(entry, 2)); + } + + private FieldDescriptor entryKeyDescriptor(Object entry) { + if (entry instanceof DynamicMessage) { + List fields = ((DynamicMessage) entry).getDescriptorForType().getFields(); + if (fields.size() == 2) { + return fields.get(0); + } + } + return keyDesc; + } + + private FieldDescriptor entryValueDescriptor(Object entry) { + if (entry instanceof DynamicMessage) { + List fields = ((DynamicMessage) entry).getDescriptorForType().getFields(); + if (fields.size() == 2) { + return fields.get(1); + } + } + return valueDesc; + } + + private Object rawMapEntryValue(Object entry, int fieldNumber) { + if (entry instanceof MapEntry) { + MapEntry mapEntry = (MapEntry) entry; + return fieldNumber == 1 ? mapEntry.getKey() : mapEntry.getValue(); + } + if (entry instanceof DynamicMessage) { + DynamicMessage dynMsg = (DynamicMessage) entry; + List fields = dynMsg.getDescriptorForType().getFields(); + if (fields.size() == 2) { + return dynMsg.getField(fields.get(fieldNumber - 1)); + } + } + throw new IllegalArgumentException( + String.format("Unexpected %s (%s) in list of map fields", entry.getClass(), entry)); + } + + private final class EntryKeyIterator extends BaseVal implements IteratorT { + private int index; + + @Override + public Val hasNext() { + return boolOf(index < entries.size()); + } + + @Override + public Val next() { + if (index < entries.size()) { + return adapter.nativeToValue(mapEntryKey(entries.get(index++))); + } + return noMoreElements(); + } + + @Override + public T convertToNative(Class typeDesc) { + throw new UnsupportedOperationException(); + } + + @Override + public Val convertToType(org.projectnessie.cel.common.types.ref.Type typeValue) { + throw new UnsupportedOperationException(); + } + + @Override + public Val equal(Val other) { + throw new UnsupportedOperationException(); + } + + @Override + public org.projectnessie.cel.common.types.ref.Type type() { + throw new UnsupportedOperationException(); + } + + @Override + public Object value() { + throw new UnsupportedOperationException(); + } + } + } + private static Object normalizeUnsignedValue(FieldDescriptor desc, Object value) { FieldDescriptor.Type type = desc.getType(); if (value instanceof Number diff --git a/core/src/main/java/org/projectnessie/cel/common/types/pb/PbObjectT.java b/core/src/main/java/org/projectnessie/cel/common/types/pb/PbObjectT.java index 48719745..d139b618 100644 --- a/core/src/main/java/org/projectnessie/cel/common/types/pb/PbObjectT.java +++ b/core/src/main/java/org/projectnessie/cel/common/types/pb/PbObjectT.java @@ -74,7 +74,7 @@ public Val get(Val index) { if (fd == null) { return noSuchField(protoFieldStr); } - return nativeToValue(fd.getField(value)); + return nativeToValue(fd.getField(value, adapter)); } @SuppressWarnings("unchecked") diff --git a/core/src/main/java/org/projectnessie/cel/common/types/pb/ProtoTypeRegistry.java b/core/src/main/java/org/projectnessie/cel/common/types/pb/ProtoTypeRegistry.java index 3d03d9bb..56e923b3 100644 --- a/core/src/main/java/org/projectnessie/cel/common/types/pb/ProtoTypeRegistry.java +++ b/core/src/main/java/org/projectnessie/cel/common/types/pb/ProtoTypeRegistry.java @@ -205,7 +205,8 @@ public FieldType findFieldType(String messageType, String fieldName) { if (field == null) { return null; } - return new FieldType(field.checkedType(), field::hasField, field::getField); + return new FieldType( + field.checkedType(), field::hasField, target -> field.getField(target, this)); } @Override diff --git a/core/src/main/java/org/projectnessie/cel/common/types/ref/TypeAdapterSupport.java b/core/src/main/java/org/projectnessie/cel/common/types/ref/TypeAdapterSupport.java index 15110953..a4ed052f 100644 --- a/core/src/main/java/org/projectnessie/cel/common/types/ref/TypeAdapterSupport.java +++ b/core/src/main/java/org/projectnessie/cel/common/types/ref/TypeAdapterSupport.java @@ -19,8 +19,12 @@ import static org.projectnessie.cel.common.types.DoubleT.doubleOf; import static org.projectnessie.cel.common.types.DurationT.durationOf; import static org.projectnessie.cel.common.types.IntT.intOf; +import static org.projectnessie.cel.common.types.ListT.newDoubleArrayList; import static org.projectnessie.cel.common.types.ListT.newGenericArrayList; +import static org.projectnessie.cel.common.types.ListT.newGenericList; +import static org.projectnessie.cel.common.types.ListT.newIntArrayList; import static org.projectnessie.cel.common.types.ListT.newJSONList; +import static org.projectnessie.cel.common.types.ListT.newLongArrayList; import static org.projectnessie.cel.common.types.ListT.newStringArrayList; import static org.projectnessie.cel.common.types.ListT.newValArrayList; import static org.projectnessie.cel.common.types.MapT.newJSONStruct; @@ -43,19 +47,16 @@ import java.time.Duration; import java.time.Instant; import java.time.ZonedDateTime; -import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.IdentityHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; import org.projectnessie.cel.common.ULong; -import org.projectnessie.cel.common.types.DoubleT; -import org.projectnessie.cel.common.types.IntT; import org.projectnessie.cel.common.types.NullT; -import org.projectnessie.cel.common.types.pb.DefaultTypeAdapter; /** * Helper class for {@link TypeAdapter} implementations to convert from a Java type to a CEL type. @@ -84,27 +85,9 @@ private TypeAdapterSupport() {} NativeToValueExact.put(Timestamp.class, (a, value) -> timestampOf((Timestamp) value)); NativeToValueExact.put(ZonedDateTime.class, (a, value) -> timestampOf((ZonedDateTime) value)); NativeToValueExact.put(Instant.class, (a, value) -> timestampOf((Instant) value)); - NativeToValueExact.put( - // TODO maybe add specialized ListT for int[] - int[].class, - (a, value) -> - newValArrayList( - DefaultTypeAdapter.Instance, - Arrays.stream((int[]) value).mapToObj(IntT::intOf).toArray(Val[]::new))); - NativeToValueExact.put( - // TODO maybe add specialized ListT for long[] - long[].class, - (a, value) -> - newValArrayList( - DefaultTypeAdapter.Instance, - Arrays.stream((long[]) value).mapToObj(IntT::intOf).toArray(Val[]::new))); - NativeToValueExact.put( - // TODO maybe add specialized ListT for double[] - double[].class, - (a, value) -> - newValArrayList( - DefaultTypeAdapter.Instance, - Arrays.stream((double[]) value).mapToObj(DoubleT::doubleOf).toArray(Val[]::new))); + NativeToValueExact.put(int[].class, (a, value) -> newIntArrayList(a, (int[]) value)); + NativeToValueExact.put(long[].class, (a, value) -> newLongArrayList(a, (long[]) value)); + NativeToValueExact.put(double[].class, (a, value) -> newDoubleArrayList(a, (double[]) value)); NativeToValueExact.put(String[].class, (a, value) -> newStringArrayList((String[]) value)); NativeToValueExact.put(Val[].class, (a, value) -> newValArrayList(a, (Val[]) value)); NativeToValueExact.put(NullValue.class, (a, value) -> NullT.NullValue); @@ -136,6 +119,9 @@ public static Val maybeNativeToValue(TypeAdapter a, Object value) { if (value instanceof Object[]) { return newGenericArrayList(a, (Object[]) value); } + if (value instanceof List) { + return newGenericList(a, (List) value); + } if (value instanceof Collection) { return newGenericArrayList(a, ((Collection) value).toArray()); } diff --git a/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java b/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java index 703f6770..5983a792 100644 --- a/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java +++ b/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java @@ -37,6 +37,7 @@ import org.projectnessie.cel.common.types.Err; import org.projectnessie.cel.common.types.IterableT; import org.projectnessie.cel.common.types.IteratorT; +import org.projectnessie.cel.common.types.MapT; import org.projectnessie.cel.common.types.Overloads; import org.projectnessie.cel.common.types.StringT; import org.projectnessie.cel.common.types.ref.FieldType; @@ -877,7 +878,7 @@ final class EvalMap extends AbstractEval implements Coster { /** Eval implements the Interpretable interface method. */ @Override public Val eval(org.projectnessie.cel.interpreter.Activation ctx) { - Map entries = new HashMap<>(); + Map entries = new HashMap<>(keys.length * 4 / 3 + 1); // If any argument is unknown or error early terminate. for (int i = 0; i < keys.length; i++) { Interpretable key = keys[i]; @@ -897,7 +898,7 @@ public Val eval(org.projectnessie.cel.interpreter.Activation ctx) { return newErr("Failed with repeated key"); } } - return adapter.nativeToValue(entries); + return MapT.newWrappedMap(adapter, entries); } /** Cost implements the Coster interface method. */