CVE-2022-3510: Insufficient Information (NVD-CWE-noinfo)
value: A parsing issue similar to CVE-2022-3171, but with Message-Type Extensions in protobuf-java core and lite versions prior to 3.21.7, 3.20.3, 3.19.6 and 3.16.3 can lead to a denial of service attack. Inputs containing multiple instances of non-repeated embedded messages with repeated or unknown fields causes objects to be converted back-n-forth between mutable and immutable forms, resulting in potentially long garbage collection pauses. We recommend updating to the versions mentioned above.


CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 7.5
Published: 2022-12-12T13:15Z
Repository: https://github.com/protocolbuffers/protobuf
Language: Other
Files changed: CHANGES.txt, java/core/src/main/java/com/google/protobuf/MessageReflection.java, ruby/compatibility_tests/v3.0.0/tests/basic.rb, ruby/ext/google/protobuf_c/message.c, ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java, ruby/tests/basic_test.proto, ruby/tests/basic_test_proto2.proto, ruby/tests/common_tests.rb, ruby/tests/generated_code.proto, ruby/tests/generated_code_proto2.proto, ruby/tests/repeated_field_test.rb
Commit date: 2022-09-29 08:28:01 -0700
Commit message: Sync from Piper @mkruskal/footmitten

PROTOBUF_SYNC_PIPER

Diff: diff --cc CHANGES.txt
index fa223a22d,fa223a22d..6f5751767
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -38,12 -38,12 +38,19 @@@
    * Performance improvement for repeated use of FieldMaskUtil#merge by caching
      constructed FieldMaskTrees.
    * Optimized Java proto serialization gencode for protos having many extension ranges with few fields in between.
    * More thoroughly annotate public generated code in Java lite protocol buffers.
    * Fixed Bug in proto3 java lite repeated enum fields. Failed to call copyOnWrite before modifying previously built message. Causes modification to already "built" messages that should be immutable.
--  * Refactoring java full runtime to reuse sub-message builders and prepare to migrate parsing logic from parse constructor to builder.
    * Fix Java reflection serialization of empty packed fields.
++  * Refactoring java full runtime to reuse sub-message builders and prepare to
++    migrate parsing logic from parse constructor to builder.
++  * Move proto wireformat parsing functionality from the private "parsing
++    constructor" to the Builder class.
++  * Change the Lite runtime to prefer merging from the wireformat into mutable
++    messages rather than building up a new immutable object before merging. This
++    way results in fewer allocations and copy operations.
++  * Make message-type extensions merge from wire-format instead of building up instances and merging afterwards. This has much better performance.
  
    Python
    * Changes ordering of printed fields in .pyi files from lexicographic to the same ordering found in the proto descriptor.
    * Adds GeneratedCodeInfo annotations to python proto .pyi outputs as a base64 encoded docstring in the last line of the .pyi file for code analysis tools.
    * Fix message factory's behavior in python cpp extension to return same message classes for same descriptor, even if the factories are different.
diff --cc java/core/src/main/java/com/google/protobuf/MessageReflection.java
index e9bc9f133,b7f5d52d4..294d84177
--- a/java/core/src/main/java/com/google/protobuf/MessageReflection.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
@@@ -376,10 -347,10 +376,11 @@@ class MessageReflection 
    }
  
    static class BuilderAdapter implements MergeTarget {
  
      private final Message.Builder builder;
++    private boolean hasNestedBuilders = true;
  
      @Override
      public Descriptors.Descriptor getDescriptorForType() {
        return builder.getDescriptorForType();
      }
@@@ -391,17 -362,17 +392,34 @@@
      @Override
      public Object getField(Descriptors.FieldDescriptor field) {
        return builder.getField(field);
      }
  
++    private Message.Builder getFieldBuilder(Descriptors.FieldDescriptor field) {
++      if (hasNestedBuilders) {
++        try {
++          return builder.getFieldBuilder(field);
++        } catch (UnsupportedOperationException e) {
++          hasNestedBuilders = false;
++        }
++      }
++      return null;
++    }
++
      @Override
      public boolean hasField(Descriptors.FieldDescriptor field) {
        return builder.hasField(field);
      }
  
      @Override
      public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) {
++      if (!field.isRepeated() && value instanceof MessageLite.Builder) {
++        if (value != getFieldBuilder(field)) {
++          builder.setField(field, ((MessageLite.Builder) value).buildPartial());
++        }
++        return this;
++      }
        builder.setField(field, value);
        return this;
      }
  
      @Override
@@@ -411,16 -382,16 +429,22 @@@
      }
  
      @Override
      public MergeTarget setRepeatedField(
          Descriptors.FieldDescriptor field, int index, Object value) {
++      if (value instanceof MessageLite.Builder) {
++        value = ((MessageLite.Builder) value).buildPartial();
++      }
        builder.setRepeatedField(field, index, value);
        return this;
      }
  
      @Override
      public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) {
++      if (value instanceof MessageLite.Builder) {
++        value = ((MessageLite.Builder) value).buildPartial();
++      }
        builder.addRepeatedField(field, value);
        return this;
      }
  
      @Override
@@@ -526,73 -497,19 +550,92 @@@
        }
        subBuilder.mergeFrom(bytes, extensionRegistry);
        return subBuilder.buildPartial();
      }
  
 +    @Override
 +    public void mergeGroup(
 +        CodedInputStream input,
 +        ExtensionRegistryLite extensionRegistry,
 +        FieldDescriptor field,
 +        Message defaultInstance)
 +        throws IOException {
 +      if (!field.isRepeated()) {
++        Message.Builder subBuilder;
 +        if (hasField(field)) {
-           input.readGroup(field.getNumber(), builder.getFieldBuilder(field), extensionRegistry);
-           return;
++          subBuilder = getFieldBuilder(field);
++          if (subBuilder != null) {
++            input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
++            return;
++          } else {
++            subBuilder = newMessageFieldInstance(field, defaultInstance);
++            subBuilder.mergeFrom((Message) getField(field));
++          }
++        } else {
++          subBuilder = newMessageFieldInstance(field, defaultInstance);
 +        }
-         Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
 +        input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
 +        Object unused = setField(field, subBuilder.buildPartial());
 +      } else {
 +        Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
 +        input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
 +        Object unused = addRepeatedField(field, subBuilder.buildPartial());
 +      }
 +    }
 +
 +    @Override
 +    public void mergeMessage(
 +        CodedInputStream input,
 +        ExtensionRegistryLite extensionRegistry,
 +        Descriptors.FieldDescriptor field,
 +        Message defaultInstance)
 +        throws IOException {
 +      if (!field.isRepeated()) {
++        Message.Builder subBuilder;
 +        if (hasField(field)) {
-           input.readMessage(builder.getFieldBuilder(field), extensionRegistry);
-           return;
++          subBuilder = getFieldBuilder(field);
++          if (subBuilder != null) {
++            input.readMessage(subBuilder, extensionRegistry);
++            return;
++          } else {
++            subBuilder = newMessageFieldInstance(field, defaultInstance);
++            subBuilder.mergeFrom((Message) getField(field));
++          }
++        } else {
++          subBuilder = newMessageFieldInstance(field, defaultInstance);
 +        }
-         Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
 +        input.readMessage(subBuilder, extensionRegistry);
 +        Object unused = setField(field, subBuilder.buildPartial());
 +      } else {
 +        Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
 +        input.readMessage(subBuilder, extensionRegistry);
 +        Object unused = addRepeatedField(field, subBuilder.buildPartial());
 +      }
 +    }
 +
 +    private Message.Builder newMessageFieldInstance(
 +        FieldDescriptor field, Message defaultInstance) {
 +      // When default instance is not null. The field is an extension field.
 +      if (defaultInstance != null) {
 +        return defaultInstance.newBuilderForType();
 +      } else {
 +        return builder.newBuilderForField(field);
 +      }
 +    }
 +
      @Override
      public MergeTarget newMergeTargetForField(
          Descriptors.FieldDescriptor field, Message defaultInstance) {
        Message.Builder subBuilder;
--      if (defaultInstance != null) {
--        subBuilder = defaultInstance.newBuilderForType();
--      } else {
--        subBuilder = builder.newBuilderForField(field);
++      if (!field.isRepeated() && hasField(field)) {
++        subBuilder = getFieldBuilder(field);
++        if (subBuilder != null) {
++          return new BuilderAdapter(subBuilder);
++        }
        }
++
++      subBuilder = newMessageFieldInstance(field, defaultInstance);
        if (!field.isRepeated()) {
          Message originalMessage = (Message) getField(field);
          if (originalMessage != null) {
            subBuilder.mergeFrom(originalMessage);
          }
@@@ -624,11 -541,11 +667,11 @@@
        return WireFormat.Utf8Validation.LOOSE;
      }
  
      @Override
      public Object finish() {
--      return builder.buildPartial();
++      return builder;
      }
    }
  
  
    static class ExtensionAdapter implements MergeTarget {
diff --cc ruby/compatibility_tests/v3.0.0/tests/basic.rb
index 7228144cb,7228144cb..d45c19678
--- a/ruby/compatibility_tests/v3.0.0/tests/basic.rb
+++ b/ruby/compatibility_tests/v3.0.0/tests/basic.rb
@@@ -665,12 -665,12 +665,12 @@@ module BasicTes
                                      { "x" => :A, "y" => :B, "z" => :C })
        assert m.length == 3
        assert m["z"] == :C
        m["z"] = 2
        assert m["z"] == :B
--      m["z"] = 4
--      assert m["z"] == 4
++      m["z"] = 5
++      assert m["z"] == 5
        assert_raise RangeError do
          m["z"] = :Z
        end
        assert_raise RangeError do
          m["z"] = "z"
diff --cc ruby/ext/google/protobuf_c/message.c
index b11878545,b11878545..5acff8096
--- a/ruby/ext/google/protobuf_c/message.c
+++ b/ruby/ext/google/protobuf_c/message.c
@@@ -1261,19 -1261,19 +1261,24 @@@ VALUE build_module_from_enumdesc(VALUE 
    VALUE mod = rb_define_module_id(rb_intern(upb_EnumDef_FullName(e)));
  
    int n = upb_EnumDef_ValueCount(e);
    for (int i = 0; i < n; i++) {
      const upb_EnumValueDef* ev = upb_EnumDef_Value(e, i);
--    const char* name = upb_EnumValueDef_Name(ev);
++    char* name = strdup(upb_EnumValueDef_Name(ev));
      int32_t value = upb_EnumValueDef_Number(ev);
      if (name[0] < 'A' || name[0] > 'Z') {
--      rb_warn(
++      if (name[0] >= 'a' && name[0] <= 'z') {
++        name[0] -= 32; // auto capitalize
++      } else {
++        rb_warn(
            "Enum value '%s' does not start with an uppercase letter "
            "as is required for Ruby constants.",
            name);
++      }
      }
      rb_define_const(mod, name, INT2NUM(value));
++    free(name);
    }
  
    rb_define_singleton_method(mod, "lookup", enum_lookup, 1);
    rb_define_singleton_method(mod, "resolve", enum_resolve, 1);
    rb_define_singleton_method(mod, "descriptor", enum_descriptor, 0);
diff --cc ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
index 65328676e,65328676e..0eb7c939c
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
@@@ -160,13 -160,13 +160,14 @@@ public class RubyEnumDescriptor extend
  
      RubyModule enumModule = RubyModule.newModule(runtime);
      boolean defaultValueRequiredButNotFound =
          descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3;
      for (EnumValueDescriptor value : descriptor.getValues()) {
--      String name = value.getName();
--      // Make sure its a valid constant name before trying to create it
--      if (Character.isUpperCase(name.codePointAt(0))) {
++      String name = fixEnumConstantName(value.getName());
++      // Make sure it's a valid constant name before trying to create it
++      int ch = name.codePointAt(0);
++      if (Character.isUpperCase(ch)) {
          enumModule.defineConstant(name, runtime.newFixnum(value.getNumber()));
        } else {
          runtime
              .getWarnings()
              .warn(
@@@ -187,10 -187,10 +188,26 @@@
      enumModule.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
      enumModule.defineAnnotatedMethods(RubyEnum.class);
      return enumModule;
    }
  
++  private static String fixEnumConstantName(String name) {
++    if (name != null && name.length() > 0) {
++      int ch = name.codePointAt(0);
++      if (ch >= 'a' && ch <= 'z') {
++        // Protobuf enums can start with lowercase letters, while Ruby's constant should
++        // always start with uppercase letters. We tolerate this case by capitalizing
++        // the first character if possible.
++        return new StringBuilder()
++                .appendCodePoint(Character.toUpperCase(ch))
++                .append(name.substring(1))
++                .toString();
++      }
++    }
++    return name;
++  }
++
    private EnumDescriptor descriptor;
    private EnumDescriptorProto.Builder builder;
    private IRubyObject name;
    private RubyModule module;
  }
diff --cc ruby/tests/basic_test.proto
index fb70f479d,fb70f479d..d480d48e5
--- a/ruby/tests/basic_test.proto
+++ b/ruby/tests/basic_test.proto
@@@ -71,10 -71,10 +71,11 @@@ message TestMessage2 
  enum TestEnum {
    Default = 0;
    A = 1;
    B = 2;
    C = 3;
++  v0 = 4;
  }
  
  message TestEmbeddedMessageParent {
    TestEmbeddedMessageChild child_msg = 1;
    int32 number = 2;
diff --cc ruby/tests/basic_test_proto2.proto
index 0c1a2b983,0c1a2b983..ac705ed63
--- a/ruby/tests/basic_test_proto2.proto
+++ b/ruby/tests/basic_test_proto2.proto
@@@ -67,10 -67,10 +67,11 @@@ message TestMessageDefaults 
  enum TestEnum {
    Default = 0;
    A = 1;
    B = 2;
    C = 3;
++  v0 = 4;
  }
  
  enum TestNonZeroEnum {
    A2 = 1;
    B2 = 2;
diff --cc ruby/tests/common_tests.rb
index 5918c8a8b,5918c8a8b..928842553
--- a/ruby/tests/common_tests.rb
+++ b/ruby/tests/common_tests.rb
@@@ -329,18 -329,18 +329,20 @@@ module CommonTest
    def test_rptfield_enum
      l = Google::Protobuf::RepeatedField.new(:enum, proto_module::TestEnum)
      l.push :A
      l.push :B
      l.push :C
--    assert l.count == 3
++    l.push :v0
++    assert l.count == 4
      assert_raise RangeError do
        l.push :D
      end
      assert l[0] == :A
++    assert l[3] == :v0
  
--    l.push 4
--    assert l[3] == 4
++    l.push 5
++    assert l[4] == 5
    end
  
    def test_rptfield_initialize
      assert_raise ArgumentError do
        Google::Protobuf::RepeatedField.new
@@@ -540,12 -540,12 +542,12 @@@
                                    { "x" => :A, "y" => :B, "z" => :C })
      assert m.length == 3
      assert m["z"] == :C
      m["z"] = 2
      assert m["z"] == :B
--    m["z"] = 4
--    assert m["z"] == 4
++    m["z"] = 5
++    assert m["z"] == 5
      assert_raise RangeError do
        m["z"] = :Z
      end
      assert_raise RangeError do
        m["z"] = "z"
@@@ -710,18 -710,18 +712,21 @@@
  
    def test_enum_lookup
      assert proto_module::TestEnum::A == 1
      assert proto_module::TestEnum::B == 2
      assert proto_module::TestEnum::C == 3
++    assert proto_module::TestEnum::V0 == 4
  
      assert proto_module::TestEnum::lookup(1) == :A
      assert proto_module::TestEnum::lookup(2) == :B
      assert proto_module::TestEnum::lookup(3) == :C
++    assert proto_module::TestEnum::lookup(4) == :v0
  
      assert proto_module::TestEnum::resolve(:A) == 1
      assert proto_module::TestEnum::resolve(:B) == 2
      assert proto_module::TestEnum::resolve(:C) == 3
++    assert proto_module::TestEnum::resolve(:v0) == 4
    end
  
    def test_enum_const_get_helpers
      m = proto_module::TestMessage.new
      assert_equal proto_module::TestEnum::Default, m.optional_enum_const
@@@ -786,11 -786,11 +791,11 @@@
      assert_equal :B, m.optional_enum
  
      assert_raise(NoMethodError) { m.a }
      assert_raise(NoMethodError) { m.a_const_const }
    end
--  
++
    def test_repeated_push
      m = proto_module::TestMessage.new
  
      m.repeated_string += ['one']
      m.repeated_string += %w[two three]
@@@ -1760,11 -1760,11 +1765,11 @@@
      assert_raise(FrozenErrorType) { m.repeated_string = '8' }
      assert_raise(FrozenErrorType) { m.repeated_bytes = nil }
      assert_raise(FrozenErrorType) { m.repeated_msg = proto_module::TestMessage2.new }
      assert_raise(FrozenErrorType) { m.repeated_enum = :A }
    end
--  
++
    def test_eq
      m1 = proto_module::TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2'])
      m2 = proto_module::TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2'])
  
      h = {}
diff --cc ruby/tests/generated_code.proto
index bfdfa5aa7,bfdfa5aa7..5f017ba7c
--- a/ruby/tests/generated_code.proto
+++ b/ruby/tests/generated_code.proto
@@@ -65,10 -65,10 +65,12 @@@ message TestMessage 
  enum TestEnum {
    Default = 0;
    A = 1;
    B = 2;
    C = 3;
++
++  v0 = 4;
  }
  
  message testLowercaseNested {
    message lowercase{}
  }
diff --cc ruby/tests/generated_code_proto2.proto
index 1e957219f,1e957219f..1a50b84be
--- a/ruby/tests/generated_code_proto2.proto
+++ b/ruby/tests/generated_code_proto2.proto
@@@ -66,10 -66,10 +66,12 @@@ message TestMessage 
  enum TestEnum {
    Default = 0;
    A = 1;
    B = 2;
    C = 3;
++
++  v0 = 4;
  }
  
  message TestUnknown {
    optional TestUnknown optional_unknown = 11;
    repeated TestUnknown repeated_unknown = 31;
diff --cc ruby/tests/repeated_field_test.rb
index 881810cdc,881810cdc..de9686996
--- a/ruby/tests/repeated_field_test.rb
+++ b/ruby/tests/repeated_field_test.rb
@@@ -695,10 -695,10 +695,11 @@@ class RepeatedFieldTest < Test::Unit::T
      add_enum "TestEnum" do
        value :Default, 0
        value :A, 1
        value :B, 2
        value :C, 3
++      value :v0, 4
      end
    end
  
    TestMessage = pool.lookup("TestMessage").msgclass
    TestMessage2 = pool.lookup("TestMessage2").msgclass

Vulnerable code: -  * Refactoring java full runtime to reuse sub-message builders and prepare to migrate parsing logic from parse constructor to builder.
           input.readGroup(field.getNumber(), builder.getFieldBuilder(field), extensionRegistry);
           return;
         Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
           input.readMessage(builder.getFieldBuilder(field), extensionRegistry);
           return;
         Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
-      if (defaultInstance != null) {
-        subBuilder = defaultInstance.newBuilderForType();
-      } else {
-        subBuilder = builder.newBuilderForField(field);
-      return builder.buildPartial();
-      m["z"] = 4
-      assert m["z"] == 4
-    const char* name = upb_EnumValueDef_Name(ev);
-      rb_warn(
-      String name = value.getName();
-      // Make sure its a valid constant name before trying to create it
-      if (Character.isUpperCase(name.codePointAt(0))) {
-    assert l.count == 3
-    l.push 4
-    assert l[3] == 4
-    m["z"] = 4
-    assert m["z"] == 4
-  
-  
Fixed code: +  * Refactoring java full runtime to reuse sub-message builders and prepare to
+    migrate parsing logic from parse constructor to builder.
+  * Move proto wireformat parsing functionality from the private "parsing
+    constructor" to the Builder class.
+  * Change the Lite runtime to prefer merging from the wireformat into mutable
+    messages rather than building up a new immutable object before merging. This
+    way results in fewer allocations and copy operations.
+  * Make message-type extensions merge from wire-format instead of building up instances and merging afterwards. This has much better performance.
+    private boolean hasNestedBuilders = true;
+    private Message.Builder getFieldBuilder(Descriptors.FieldDescriptor field) {
+      if (hasNestedBuilders) {
+        try {
+          return builder.getFieldBuilder(field);
+        } catch (UnsupportedOperationException e) {
+          hasNestedBuilders = false;
+        }
+      }
+      return null;
+    }
+
+      if (!field.isRepeated() && value instanceof MessageLite.Builder) {
+        if (value != getFieldBuilder(field)) {
+          builder.setField(field, ((MessageLite.Builder) value).buildPartial());
+        }
+        return this;
+      }
+      if (value instanceof MessageLite.Builder) {
+        value = ((MessageLite.Builder) value).buildPartial();
+      }
+      if (value instanceof MessageLite.Builder) {
+        value = ((MessageLite.Builder) value).buildPartial();
+      }
+        Message.Builder subBuilder;
+          subBuilder = getFieldBuilder(field);
+          if (subBuilder != null) {
+            input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
+            return;
+          } else {
+            subBuilder = newMessageFieldInstance(field, defaultInstance);
+            subBuilder.mergeFrom((Message) getField(field));
+          }
+        } else {
+          subBuilder = newMessageFieldInstance(field, defaultInstance);
+        Message.Builder subBuilder;
+          subBuilder = getFieldBuilder(field);
+          if (subBuilder != null) {
+            input.readMessage(subBuilder, extensionRegistry);
+            return;
+          } else {
+            subBuilder = newMessageFieldInstance(field, defaultInstance);
+            subBuilder.mergeFrom((Message) getField(field));
+          }
+        } else {
+          subBuilder = newMessageFieldInstance(field, defaultInstance);
+      if (!field.isRepeated() && hasField(field)) {
+        subBuilder = getFieldBuilder(field);
+        if (subBuilder != null) {
+          return new BuilderAdapter(subBuilder);
+        }
+
+      subBuilder = newMessageFieldInstance(field, defaultInstance);
+      return builder;
+      m["z"] = 5
+      assert m["z"] == 5
+    char* name = strdup(upb_EnumValueDef_Name(ev));
+      if (name[0] >= 'a' && name[0] <= 'z') {
+        name[0] -= 32; // auto capitalize
+      } else {
+        rb_warn(
+      }
+    free(name);
+      String name = fixEnumConstantName(value.getName());
+      // Make sure it's a valid constant name before trying to create it
+      int ch = name.codePointAt(0);
+      if (Character.isUpperCase(ch)) {
+  private static String fixEnumConstantName(String name) {
+    if (name != null && name.length() > 0) {
+      int ch = name.codePointAt(0);
+      if (ch >= 'a' && ch <= 'z') {
+        // Protobuf enums can start with lowercase letters, while Ruby's constant should
+        // always start with uppercase letters. We tolerate this case by capitalizing
+        // the first character if possible.
+        return new StringBuilder()
+                .appendCodePoint(Character.toUpperCase(ch))
+                .append(name.substring(1))
+                .toString();
+      }
+    }
+    return name;
+  }
+
+  v0 = 4;
+  v0 = 4;
+    l.push :v0
+    assert l.count == 4
+    assert l[3] == :v0
+    l.push 5
+    assert l[4] == 5
+    m["z"] = 5
+    assert m["z"] == 5
+    assert proto_module::TestEnum::V0 == 4
+    assert proto_module::TestEnum::lookup(4) == :v0
+    assert proto_module::TestEnum::resolve(:v0) == 4
+
+
+
+  v0 = 4;
+
+  v0 = 4;
+      value :v0, 4