Skip to content

Commit db7c178

Browse files
Sync from Piper @mkruskal/footmitten
PROTOBUF_SYNC_PIPER
2 parents 7703636 + d0936c3 commit db7c178

File tree

11 files changed

+110
-26
lines changed

11 files changed

+110
-26
lines changed

CHANGES.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,15 @@
4040
* Optimized Java proto serialization gencode for protos having many extension ranges with few fields in between.
4141
* More thoroughly annotate public generated code in Java lite protocol buffers.
4242
* 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.
43-
* Refactoring java full runtime to reuse sub-message builders and prepare to migrate parsing logic from parse constructor to builder.
4443
* Fix Java reflection serialization of empty packed fields.
44+
* Refactoring java full runtime to reuse sub-message builders and prepare to
45+
migrate parsing logic from parse constructor to builder.
46+
* Move proto wireformat parsing functionality from the private "parsing
47+
constructor" to the Builder class.
48+
* Change the Lite runtime to prefer merging from the wireformat into mutable
49+
messages rather than building up a new immutable object before merging. This
50+
way results in fewer allocations and copy operations.
51+
* Make message-type extensions merge from wire-format instead of building up instances and merging afterwards. This has much better performance.
4552

4653
Python
4754
* Changes ordering of printed fields in .pyi files from lexicographic to the same ordering found in the proto descriptor.

java/core/src/main/java/com/google/protobuf/MessageReflection.java

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ MergeTarget newEmptyTargetForField(
378378
static class BuilderAdapter implements MergeTarget {
379379

380380
private final Message.Builder builder;
381+
private boolean hasNestedBuilders = true;
381382

382383
@Override
383384
public Descriptors.Descriptor getDescriptorForType() {
@@ -393,13 +394,30 @@ public Object getField(Descriptors.FieldDescriptor field) {
393394
return builder.getField(field);
394395
}
395396

397+
private Message.Builder getFieldBuilder(Descriptors.FieldDescriptor field) {
398+
if (hasNestedBuilders) {
399+
try {
400+
return builder.getFieldBuilder(field);
401+
} catch (UnsupportedOperationException e) {
402+
hasNestedBuilders = false;
403+
}
404+
}
405+
return null;
406+
}
407+
396408
@Override
397409
public boolean hasField(Descriptors.FieldDescriptor field) {
398410
return builder.hasField(field);
399411
}
400412

401413
@Override
402414
public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) {
415+
if (!field.isRepeated() && value instanceof MessageLite.Builder) {
416+
if (value != getFieldBuilder(field)) {
417+
builder.setField(field, ((MessageLite.Builder) value).buildPartial());
418+
}
419+
return this;
420+
}
403421
builder.setField(field, value);
404422
return this;
405423
}
@@ -413,12 +431,18 @@ public MergeTarget clearField(Descriptors.FieldDescriptor field) {
413431
@Override
414432
public MergeTarget setRepeatedField(
415433
Descriptors.FieldDescriptor field, int index, Object value) {
434+
if (value instanceof MessageLite.Builder) {
435+
value = ((MessageLite.Builder) value).buildPartial();
436+
}
416437
builder.setRepeatedField(field, index, value);
417438
return this;
418439
}
419440

420441
@Override
421442
public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) {
443+
if (value instanceof MessageLite.Builder) {
444+
value = ((MessageLite.Builder) value).buildPartial();
445+
}
422446
builder.addRepeatedField(field, value);
423447
return this;
424448
}
@@ -536,11 +560,19 @@ public void mergeGroup(
536560
Message defaultInstance)
537561
throws IOException {
538562
if (!field.isRepeated()) {
563+
Message.Builder subBuilder;
539564
if (hasField(field)) {
540-
input.readGroup(field.getNumber(), builder.getFieldBuilder(field), extensionRegistry);
541-
return;
565+
subBuilder = getFieldBuilder(field);
566+
if (subBuilder != null) {
567+
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
568+
return;
569+
} else {
570+
subBuilder = newMessageFieldInstance(field, defaultInstance);
571+
subBuilder.mergeFrom((Message) getField(field));
572+
}
573+
} else {
574+
subBuilder = newMessageFieldInstance(field, defaultInstance);
542575
}
543-
Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
544576
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
545577
Object unused = setField(field, subBuilder.buildPartial());
546578
} else {
@@ -558,11 +590,19 @@ public void mergeMessage(
558590
Message defaultInstance)
559591
throws IOException {
560592
if (!field.isRepeated()) {
593+
Message.Builder subBuilder;
561594
if (hasField(field)) {
562-
input.readMessage(builder.getFieldBuilder(field), extensionRegistry);
563-
return;
595+
subBuilder = getFieldBuilder(field);
596+
if (subBuilder != null) {
597+
input.readMessage(subBuilder, extensionRegistry);
598+
return;
599+
} else {
600+
subBuilder = newMessageFieldInstance(field, defaultInstance);
601+
subBuilder.mergeFrom((Message) getField(field));
602+
}
603+
} else {
604+
subBuilder = newMessageFieldInstance(field, defaultInstance);
564605
}
565-
Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance);
566606
input.readMessage(subBuilder, extensionRegistry);
567607
Object unused = setField(field, subBuilder.buildPartial());
568608
} else {
@@ -586,11 +626,14 @@ private Message.Builder newMessageFieldInstance(
586626
public MergeTarget newMergeTargetForField(
587627
Descriptors.FieldDescriptor field, Message defaultInstance) {
588628
Message.Builder subBuilder;
589-
if (defaultInstance != null) {
590-
subBuilder = defaultInstance.newBuilderForType();
591-
} else {
592-
subBuilder = builder.newBuilderForField(field);
629+
if (!field.isRepeated() && hasField(field)) {
630+
subBuilder = getFieldBuilder(field);
631+
if (subBuilder != null) {
632+
return new BuilderAdapter(subBuilder);
633+
}
593634
}
635+
636+
subBuilder = newMessageFieldInstance(field, defaultInstance);
594637
if (!field.isRepeated()) {
595638
Message originalMessage = (Message) getField(field);
596639
if (originalMessage != null) {
@@ -626,7 +669,7 @@ public WireFormat.Utf8Validation getUtf8Validation(Descriptors.FieldDescriptor d
626669

627670
@Override
628671
public Object finish() {
629-
return builder.buildPartial();
672+
return builder;
630673
}
631674
}
632675

ruby/compatibility_tests/v3.0.0/tests/basic.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,8 +667,8 @@ def test_map_msg_enum_valuetypes
667667
assert m["z"] == :C
668668
m["z"] = 2
669669
assert m["z"] == :B
670-
m["z"] = 4
671-
assert m["z"] == 4
670+
m["z"] = 5
671+
assert m["z"] == 5
672672
assert_raise RangeError do
673673
m["z"] = :Z
674674
end

ruby/ext/google/protobuf_c/message.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,15 +1263,20 @@ VALUE build_module_from_enumdesc(VALUE _enumdesc) {
12631263
int n = upb_EnumDef_ValueCount(e);
12641264
for (int i = 0; i < n; i++) {
12651265
const upb_EnumValueDef* ev = upb_EnumDef_Value(e, i);
1266-
const char* name = upb_EnumValueDef_Name(ev);
1266+
char* name = strdup(upb_EnumValueDef_Name(ev));
12671267
int32_t value = upb_EnumValueDef_Number(ev);
12681268
if (name[0] < 'A' || name[0] > 'Z') {
1269-
rb_warn(
1269+
if (name[0] >= 'a' && name[0] <= 'z') {
1270+
name[0] -= 32; // auto capitalize
1271+
} else {
1272+
rb_warn(
12701273
"Enum value '%s' does not start with an uppercase letter "
12711274
"as is required for Ruby constants.",
12721275
name);
1276+
}
12731277
}
12741278
rb_define_const(mod, name, INT2NUM(value));
1279+
free(name);
12751280
}
12761281

12771282
rb_define_singleton_method(mod, "lookup", enum_lookup, 1);

ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,10 @@ private RubyModule buildModuleFromDescriptor(ThreadContext context) {
162162
boolean defaultValueRequiredButNotFound =
163163
descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3;
164164
for (EnumValueDescriptor value : descriptor.getValues()) {
165-
String name = value.getName();
166-
// Make sure its a valid constant name before trying to create it
167-
if (Character.isUpperCase(name.codePointAt(0))) {
165+
String name = fixEnumConstantName(value.getName());
166+
// Make sure it's a valid constant name before trying to create it
167+
int ch = name.codePointAt(0);
168+
if (Character.isUpperCase(ch)) {
168169
enumModule.defineConstant(name, runtime.newFixnum(value.getNumber()));
169170
} else {
170171
runtime
@@ -189,6 +190,22 @@ private RubyModule buildModuleFromDescriptor(ThreadContext context) {
189190
return enumModule;
190191
}
191192

193+
private static String fixEnumConstantName(String name) {
194+
if (name != null && name.length() > 0) {
195+
int ch = name.codePointAt(0);
196+
if (ch >= 'a' && ch <= 'z') {
197+
// Protobuf enums can start with lowercase letters, while Ruby's constant should
198+
// always start with uppercase letters. We tolerate this case by capitalizing
199+
// the first character if possible.
200+
return new StringBuilder()
201+
.appendCodePoint(Character.toUpperCase(ch))
202+
.append(name.substring(1))
203+
.toString();
204+
}
205+
}
206+
return name;
207+
}
208+
192209
private EnumDescriptor descriptor;
193210
private EnumDescriptorProto.Builder builder;
194211
private IRubyObject name;

ruby/tests/basic_test.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ enum TestEnum {
7373
A = 1;
7474
B = 2;
7575
C = 3;
76+
v0 = 4;
7677
}
7778

7879
message TestEmbeddedMessageParent {

ruby/tests/basic_test_proto2.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ enum TestEnum {
6969
A = 1;
7070
B = 2;
7171
C = 3;
72+
v0 = 4;
7273
}
7374

7475
enum TestNonZeroEnum {

ruby/tests/common_tests.rb

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -331,14 +331,16 @@ def test_rptfield_enum
331331
l.push :A
332332
l.push :B
333333
l.push :C
334-
assert l.count == 3
334+
l.push :v0
335+
assert l.count == 4
335336
assert_raise RangeError do
336337
l.push :D
337338
end
338339
assert l[0] == :A
340+
assert l[3] == :v0
339341

340-
l.push 4
341-
assert l[3] == 4
342+
l.push 5
343+
assert l[4] == 5
342344
end
343345

344346
def test_rptfield_initialize
@@ -542,8 +544,8 @@ def test_map_msg_enum_valuetypes
542544
assert m["z"] == :C
543545
m["z"] = 2
544546
assert m["z"] == :B
545-
m["z"] = 4
546-
assert m["z"] == 4
547+
m["z"] = 5
548+
assert m["z"] == 5
547549
assert_raise RangeError do
548550
m["z"] = :Z
549551
end
@@ -712,14 +714,17 @@ def test_enum_lookup
712714
assert proto_module::TestEnum::A == 1
713715
assert proto_module::TestEnum::B == 2
714716
assert proto_module::TestEnum::C == 3
717+
assert proto_module::TestEnum::V0 == 4
715718

716719
assert proto_module::TestEnum::lookup(1) == :A
717720
assert proto_module::TestEnum::lookup(2) == :B
718721
assert proto_module::TestEnum::lookup(3) == :C
722+
assert proto_module::TestEnum::lookup(4) == :v0
719723

720724
assert proto_module::TestEnum::resolve(:A) == 1
721725
assert proto_module::TestEnum::resolve(:B) == 2
722726
assert proto_module::TestEnum::resolve(:C) == 3
727+
assert proto_module::TestEnum::resolve(:v0) == 4
723728
end
724729

725730
def test_enum_const_get_helpers
@@ -788,7 +793,7 @@ def test_enum_getter_only_enums
788793
assert_raise(NoMethodError) { m.a }
789794
assert_raise(NoMethodError) { m.a_const_const }
790795
end
791-
796+
792797
def test_repeated_push
793798
m = proto_module::TestMessage.new
794799

@@ -1762,7 +1767,7 @@ def test_freeze
17621767
assert_raise(FrozenErrorType) { m.repeated_msg = proto_module::TestMessage2.new }
17631768
assert_raise(FrozenErrorType) { m.repeated_enum = :A }
17641769
end
1765-
1770+
17661771
def test_eq
17671772
m1 = proto_module::TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2'])
17681773
m2 = proto_module::TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2'])

ruby/tests/generated_code.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ enum TestEnum {
6767
A = 1;
6868
B = 2;
6969
C = 3;
70+
71+
v0 = 4;
7072
}
7173

7274
message testLowercaseNested {

ruby/tests/generated_code_proto2.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ enum TestEnum {
6868
A = 1;
6969
B = 2;
7070
C = 3;
71+
72+
v0 = 4;
7173
}
7274

7375
message TestUnknown {

0 commit comments

Comments
 (0)