google / cel-java

Fast, portable, non-Turing complete expression evaluation with gradual typing (Java)
https://cel.dev
Apache License 2.0
81 stars 15 forks source link

Query to map in DynamicMessage generated with JsonFormat.Parser cause ClassCastException #333

Closed Kmotiko closed 2 weeks ago

Kmotiko commented 2 weeks ago

Hi, thanks for cel-java.

First of all, sorry if this is not the correct place to ask questions.

I tried to run a query to access to map against DynamicMessage which built with JsonFormat.Parser, then I get following exception.

Exception in thread "main" java.lang.ClassCastException: class com.google.protobuf.DynamicMessage cannot be cast to class com.google.protobuf.MapEntry (com.google.protobuf.DynamicMessage and com.google.protobuf.MapEntry are in unnamed module of loader 'app')

According to the stacktrace, it looks like that exception is happening ~here~ here [^1]. I run same code with debugger and stop program at that line, then Message built using JsonFormat.Parser seems to contain map entry as list of DynamicMessage and it can not cast to MapEntry. I run same query against a Message which build with specific message's builder and put MapEntry using builder's putAll function. In this case, the query ended without exception.

I'm not sure whether this behavior is bug or not. Are there any way to run a query to access a map against DynamicMessage generated with JsonFormat.Parser? If currently this operation it not supported, are there any plan to support queries on objects that hold map as a List of DynamicMessage in the future?

To Reproduce

First, define following message and generate java source code with protoc command.

message Test {
  map<string, string> a = 1;
  Contents contents = 2;
  message Contents{
    map<string, string> b = 1;
  }
}

Then build a CelRuntime.Program instance with these code.

        CelBuilder celBuilder = CelFactory.standardCelBuilder()
                .addMessageTypes(Test.getDescriptor())
                .setOptions(CelOptions.newBuilder()
                        .enableCelValue(true)
                        .enableOptionalSyntax(true)
                        .build());
        Cel cel = celBuilder
                .addVar("a", MapType.create(SimpleType.STRING, SimpleType.STRING))
                .addVar("contents", StructTypeReference.create(Test.Contents.getDescriptor().getFullName()))
                .build();
        CelValidationResult validationResult = cel.compile("contents.b.size() > 0");
        CelRuntime.Program program = cel.createProgram(validationResult.getAst());

Finally, build a DynamicMessage value and query to it, then exception is thrown.

        String stringJson = """
                {
                    "a" : {
                        "test_key_a" : "test_value_a"
                    },
                    "contents" : {
                        "b" : {
                            "test_key_b_1" : "test_value_b_1",
                            "test_key_b_2" : "test_value_b_2"
                        }
                    }
                }
                """;
        DynamicMessage.Builder messageBuilder = DynamicMessage.newBuilder(Test.getDescriptor());
        JsonFormat.parser().ignoringUnknownFields().merge(stringJson, messageBuilder);
        // Following cause Exception : Exception in thread "main" java.lang.ClassCastException: class com.google.protobuf.DynamicMessage cannot be cast to class com.google.protobuf.MapEntry (com.google.protobuf.DynamicMessage and com.google.protobuf.MapEntry are in unnamed module of loader 'app')
        Object queryResult2 = program.eval(messageBuilder.build());

My code is located here, and all of stacktrace is following.

Exception in thread "main" java.lang.ClassCastException: class com.google.protobuf.DynamicMessage cannot be cast to class com.google.protobuf.MapEntry (com.google.protobuf.DynamicMessage and com.google.protobuf.MapEntry are in unnamed module of loader 'app')
    at dev.cel.common.internal.ProtoAdapter.adaptFieldToValue(ProtoAdapter.java:216)
    at dev.cel.runtime.Activation.fromProto(Activation.java:180)
    at dev.cel.runtime.CelRuntime$Program.eval(CelRuntime.java:59)
    at org.example.Main.main(Main.java:69)

[^1]:I edit because the link I wrote first was wrong

Kmotiko commented 2 weeks ago

I'm truly sorry. I close this Issue because it is possible that my test code was wrong. I will try again by myself and confirm the problem. I apologize once again.