Skip to content

Commit

Permalink
Add ProtoWrapperValue
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 586717232
  • Loading branch information
l46kok authored and copybara-github committed Dec 1, 2023
1 parent aa6875c commit 77520bc
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 26 deletions.
1 change: 1 addition & 0 deletions common/src/main/java/dev/cel/common/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ java_library(
],
deps = [
"//common/annotations",
"@maven//:com_google_guava_guava",
"@maven//:com_google_protobuf_protobuf_java",
],
)
Expand Down
13 changes: 4 additions & 9 deletions common/src/main/java/dev/cel/common/internal/ProtoAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.util.Arrays.stream;

import dev.cel.expr.ExprValue;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -60,7 +59,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import org.jspecify.nullness.Nullable;

/**
Expand Down Expand Up @@ -139,10 +137,6 @@ public final class ProtoAdapter {
public static final BidiConverter<Number, Number> DOUBLE_CONVERTER =
BidiConverter.of(Number::doubleValue, Number::floatValue);

private static final ImmutableMap<String, WellKnownProto> WELL_KNOWN_PROTOS =
stream(WellKnownProto.values())
.collect(toImmutableMap(WellKnownProto::typeName, Function.identity()));

private final DynamicProto dynamicProto;
private final boolean enableUnsignedLongs;

Expand All @@ -163,7 +157,8 @@ public Object adaptProtoToValue(MessageOrBuilder proto) {
}
// If the proto is not a well-known type, then the input Message is what's expected as the
// output return value.
WellKnownProto wellKnownProto = WELL_KNOWN_PROTOS.get(typeName(proto.getDescriptorForType()));
WellKnownProto wellKnownProto =
WellKnownProto.getByDescriptorName(typeName(proto.getDescriptorForType()));
if (wellKnownProto == null) {
return proto;
}
Expand Down Expand Up @@ -328,7 +323,7 @@ private BidiConverter fieldToValueConverter(FieldDescriptor fieldDescriptor) {
* considered, such as a packing an {@code google.protobuf.StringValue} into a {@code Any} value.
*/
public Optional<Message> adaptValueToProto(Object value, String protoTypeName) {
WellKnownProto wellKnownProto = WELL_KNOWN_PROTOS.get(protoTypeName);
WellKnownProto wellKnownProto = WellKnownProto.getByDescriptorName(protoTypeName);
if (wellKnownProto == null) {
if (value instanceof Message) {
return Optional.of((Message) value);
Expand Down Expand Up @@ -644,7 +639,7 @@ private static boolean isWrapperType(FieldDescriptor fieldDescriptor) {
return false;
}
String fieldTypeName = fieldDescriptor.getMessageType().getFullName();
WellKnownProto wellKnownProto = WELL_KNOWN_PROTOS.get(fieldTypeName);
WellKnownProto wellKnownProto = WellKnownProto.getByDescriptorName(fieldTypeName);
return wellKnownProto != null && wellKnownProto.isWrapperType();
}

Expand Down
19 changes: 18 additions & 1 deletion common/src/main/java/dev/cel/common/internal/WellKnownProto.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

package dev.cel.common.internal;

import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.util.Arrays.stream;

import com.google.common.collect.ImmutableMap;
import com.google.protobuf.Any;
import com.google.protobuf.BoolValue;
import com.google.protobuf.BytesValue;
Expand All @@ -31,6 +35,7 @@
import com.google.protobuf.UInt64Value;
import com.google.protobuf.Value;
import dev.cel.common.annotations.Internal;
import java.util.function.Function;

/**
* WellKnownProto types used throughout CEL. These types are specially handled to ensure that
Expand Down Expand Up @@ -58,6 +63,14 @@ public enum WellKnownProto {
private final Descriptor descriptor;
private final boolean isWrapperType;

private static final ImmutableMap<String, WellKnownProto> WELL_KNOWN_PROTO_MAP;

static {
WELL_KNOWN_PROTO_MAP =
stream(WellKnownProto.values())
.collect(toImmutableMap(WellKnownProto::typeName, Function.identity()));
}

WellKnownProto(Descriptor descriptor) {
this(descriptor, /* isWrapperType= */ false);
}
Expand All @@ -75,7 +88,11 @@ public String typeName() {
return descriptor.getFullName();
}

boolean isWrapperType() {
public boolean isWrapperType() {
return isWrapperType;
}

public static WellKnownProto getByDescriptorName(String name) {
return WELL_KNOWN_PROTO_MAP.get(name);
}
}
1 change: 1 addition & 0 deletions common/src/main/java/dev/cel/common/values/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ CEL_VALUES_SOURCES = [
PROTO_MESSAGE_VALUE_SOURCES = [
"ProtoCelValueConverter.java",
"ProtoMessageValue.java",
"ProtoWrapperValue.java",
]

java_library(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.math.LongMath.checkedAdd;
import static com.google.common.math.LongMath.checkedSubtract;
import static java.util.Arrays.stream;

import com.google.common.collect.ImmutableMap;
import com.google.common.primitives.UnsignedLong;
import com.google.errorprone.annotations.Immutable;
import com.google.protobuf.Any;
Expand Down Expand Up @@ -56,7 +54,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
* {@code CelValueConverter} handles bidirectional conversion between native Java and protobuf
Expand All @@ -70,9 +67,6 @@
@Immutable
@Internal
public final class ProtoCelValueConverter extends CelValueConverter {
private static final ImmutableMap<String, WellKnownProto> WELL_KNOWN_PROTOS =
stream(WellKnownProto.values())
.collect(toImmutableMap(WellKnownProto::typeName, Function.identity()));
private final CelDescriptorPool celDescriptorPool;
private final DynamicProto dynamicProto;

Expand All @@ -90,7 +84,7 @@ public CelValue fromProtoMessageToCelValue(MessageOrBuilder message) {
}

WellKnownProto wellKnownProto =
WELL_KNOWN_PROTOS.get(message.getDescriptorForType().getFullName());
WellKnownProto.getByDescriptorName(message.getDescriptorForType().getFullName());
if (wellKnownProto == null) {
return ProtoMessageValue.create((Message) message, celDescriptorPool, this);
}
Expand Down
115 changes: 115 additions & 0 deletions common/src/main/java/dev/cel/common/values/ProtoWrapperValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dev.cel.common.values;

import com.google.auto.value.AutoValue;
import com.google.errorprone.annotations.Immutable;
import com.google.protobuf.ByteString;
import com.google.protobuf.FloatValue;
import com.google.protobuf.Int32Value;
import com.google.protobuf.Int64Value;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.UInt32Value;
import com.google.protobuf.UInt64Value;
import dev.cel.common.internal.WellKnownProto;
import dev.cel.common.types.NullableType;

/** ProtoWrapperValue represents a */
@Immutable
@AutoValue
public abstract class ProtoWrapperValue extends StructValue {

@Override
public abstract CelValue value();

abstract WellKnownProto wellKnownProto();

/**
* Retrieves the underlying value being held in the wrapper. For example, if this is a
* `google.protobuf.IntValue', a Java long is returned.
*/
public Object nativeValue() {
if (wellKnownProto().equals(WellKnownProto.BYTES_VALUE)) {
// Return the proto ByteString as the underlying primitive value rather than a mutable byte
// array.
return ByteString.copyFrom(((BytesValue) value()).value().toByteArray());
}
return value().value();
}

@Override
public abstract NullableType celType();

@Override
public boolean isZeroValue() {
return value().isZeroValue();
}

@Override
public CelValue select(String fieldName) {
throw new UnsupportedOperationException("Wrappers do not support field selection");
}

@Override
public boolean hasField(String fieldName) {
throw new UnsupportedOperationException("Wrappers do not support presence tests");
}

public static ProtoWrapperValue create(
MessageOrBuilder wrapperMessage, boolean enableUnsignedLongs) {
WellKnownProto wellKnownProto = getWellKnownProtoFromWrapperName(wrapperMessage);
CelValue celValue = newCelValueFromWrapper(wrapperMessage, wellKnownProto, enableUnsignedLongs);
NullableType nullableType = NullableType.create(celValue.celType());
return new AutoValue_ProtoWrapperValue(celValue, wellKnownProto, nullableType);
}

private static CelValue newCelValueFromWrapper(
MessageOrBuilder message, WellKnownProto wellKnownProto, boolean enableUnsignedLongs) {
switch (wellKnownProto) {
case BOOL_VALUE:
return BoolValue.create(((com.google.protobuf.BoolValue) message).getValue());
case BYTES_VALUE:
return BytesValue.create(
CelByteString.of(((com.google.protobuf.BytesValue) message).getValue().toByteArray()));
case DOUBLE_VALUE:
return DoubleValue.create(((com.google.protobuf.DoubleValue) message).getValue());
case FLOAT_VALUE:
return DoubleValue.create(((FloatValue) message).getValue());
case INT32_VALUE:
return IntValue.create(((Int32Value) message).getValue());
case INT64_VALUE:
return IntValue.create(((Int64Value) message).getValue());
case STRING_VALUE:
return StringValue.create(((com.google.protobuf.StringValue) message).getValue());
case UINT32_VALUE:
return UintValue.create(((UInt32Value) message).getValue(), enableUnsignedLongs);
case UINT64_VALUE:
return UintValue.create(((UInt64Value) message).getValue(), enableUnsignedLongs);
default:
throw new IllegalArgumentException(
"Should only be called for wrapper types. Got: " + wellKnownProto.name());
}
}

private static WellKnownProto getWellKnownProtoFromWrapperName(MessageOrBuilder message) {
WellKnownProto wellKnownProto =
WellKnownProto.getByDescriptorName(message.getDescriptorForType().getFullName());
if (!wellKnownProto.isWrapperType()) {
throw new IllegalArgumentException("Expected a wrapper type. Got: " + wellKnownProto.name());
}

return wellKnownProto;
}
}
24 changes: 16 additions & 8 deletions common/src/main/java/dev/cel/common/values/UintValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,25 @@
import dev.cel.common.types.SimpleType;

/**
* UintValue represents CelValue for unsigned longs, leveraging Guava's implementation of {@link
* UnsignedLong}.
*
* <p>TODO: Look into potentially accepting a primitive `long` to avoid boxing/unboxing
* when the interpreter is augmented to work directly on CelValue.
* UintValue represents CelValue for unsigned longs. This either leverages Guava's implementation of
* {@link UnsignedLong}, or just holds a primitive long.
*/
@AutoValue
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
@SuppressWarnings("Immutable") // value is either a boxed long or an immutable UnsignedLong.
public abstract class UintValue extends CelValue {

@Override
public abstract UnsignedLong value();
public abstract Number value();

@Override
public boolean isZeroValue() {
return UnsignedLong.ZERO.equals(value());
if (value() instanceof UnsignedLong) {
return UnsignedLong.ZERO.equals(value());
} else {
return value().longValue() == 0;
}
}

@Override
Expand All @@ -47,4 +50,9 @@ public CelType celType() {
public static UintValue create(UnsignedLong value) {
return new AutoValue_UintValue(value);
}

public static UintValue create(long value, boolean enableUnsignedLongs) {
Number unsignedLong = enableUnsignedLongs ? UnsignedLong.fromLongBits(value) : value;
return new AutoValue_UintValue(unsignedLong);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public void hasField_extensionField_throwsWhenDescriptorMissing() {
}

private enum SelectFieldTestCase {
// // Primitives
// Primitives
BOOL("single_bool", BoolValue.create(true)),
INT32("single_int32", IntValue.create(4L)),
INT64("single_int64", IntValue.create(5L)),
Expand Down
Loading

0 comments on commit 77520bc

Please sign in to comment.