Skip to content

Commit 8dd83da

Browse files
committed
Binary compatibility shims for GeneratedMessageV3, SingleFieldBuilderV3, RepeatedFieldBuilderV3, and their nested classes to restore binary compatibility with <=v3.x.x generated code built against v3.x.x prior to v4.26.0 breaking release.
3.x.x descriptor.proto generated code is *not* supported with 4.x.x runtime, since this results in an ODR violation with the descriptor.proto built into the 4.x.x runtime. This is expected to result in undefined behavior / failures. Tested against //java/core:v25_generated_message_test_jar (binary compatibility) and //java/core:v25_generated_message_test_srcjar (source compatibility) PiperOrigin-RevId: 664819152
1 parent 8c96e55 commit 8dd83da

File tree

11 files changed

+1500
-57
lines changed

11 files changed

+1500
-57
lines changed

WORKSPACE

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,6 @@ http_archive(
233233
url = "https://github.com/protocolbuffers/protobuf/releases/download/v25.0/protobuf-25.0.tar.gz",
234234
)
235235

236-
# Needed as a dependency of @com_google_protobuf_v25.x, which was before
237-
# utf8_range was merged in.
238-
http_archive(
239-
name = "utf8_range",
240-
strip_prefix = "utf8_range-d863bc33e15cba6d873c878dcca9e6fe52b2f8cb",
241-
url = "https://github.com/protocolbuffers/utf8_range/archive/d863bc33e15cba6d873c878dcca9e6fe52b2f8cb.zip",
242-
)
236+
# Needed as a dependency of @com_google_protobuf_v25.0
237+
load("@com_google_protobuf_v25.0//:protobuf_deps.bzl", protobuf_v25_deps="protobuf_deps")
238+
protobuf_v25_deps()

compatibility/BUILD.bazel

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@
66

77
load("//compatibility:runtime_conformance.bzl", "java_runtime_conformance")
88

9+
java_library(
10+
name = "v25_test_protos_srcjar",
11+
testonly = True,
12+
srcs = glob([
13+
"v3.25.0/*.srcjar",
14+
]),
15+
visibility = ["//java/core:__pkg__"],
16+
deps = ["//java/core"],
17+
)
18+
19+
java_library(
20+
name = "v25_test_protos_jar",
21+
testonly = True,
22+
srcs = glob([
23+
"v3.25.0/*.srcjar",
24+
]),
25+
visibility = ["//java/core:__pkg__"],
26+
deps = ["@com_google_protobuf_v25.0//java/core"],
27+
)
28+
929
# main gencode builds with main runtime as a proof of concept.
1030
java_runtime_conformance(
1131
name = "java_conformance_main",
24.5 MB
Binary file not shown.
17.4 MB
Binary file not shown.
2.34 MB
Binary file not shown.

java/core/BUILD.bazel

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,102 @@ junit_tests(
581581
],
582582
)
583583

584+
protobuf_java_library(
585+
name = "v25_test_util_srcjar",
586+
testonly = True,
587+
srcs = [
588+
"src/test/java/com/google/protobuf/TestUtil.java",
589+
"src/test/java/com/google/protobuf/TestUtilLite.java",
590+
],
591+
deps = [
592+
":core",
593+
"//compatibility:v25_test_protos_srcjar",
594+
"@maven//:com_google_guava_guava",
595+
"@maven//:junit_junit",
596+
],
597+
)
598+
599+
# Tests source compatibility against v25 gencode jar compiled against current runtime
600+
junit_tests(
601+
name = "v25_core_tests_srcjar",
602+
size = "small",
603+
srcs = glob(
604+
["src/test/java/**/*.java"],
605+
exclude = [
606+
# Depends on test protos or API changes added in v4.x.x (e.g. editions)
607+
"src/test/java/com/google/protobuf/TextFormatTest.java",
608+
"src/test/java/com/google/protobuf/DescriptorsTest.java",
609+
"src/test/java/com/google/protobuf/DebugFormatTest.java",
610+
"src/test/java/com/google/protobuf/CodedOutputStreamTest.java",
611+
"src/test/java/com/google/protobuf/ProtobufToStringOutputTest.java",
612+
# Excluded in core_tests
613+
"src/test/java/com/google/protobuf/DecodeUtf8Test.java",
614+
"src/test/java/com/google/protobuf/IsValidUtf8Test.java",
615+
"src/test/java/com/google/protobuf/TestUtil.java",
616+
"src/test/java/com/google/protobuf/TestUtilLite.java",
617+
"src/test/java/com/google/protobuf/RuntimeVersionTest.java",
618+
],
619+
),
620+
test_prefix = "v25SrcJar",
621+
deps = [
622+
":core",
623+
":v25_test_util_srcjar",
624+
"//compatibility:v25_test_protos_srcjar",
625+
"@maven//:com_google_guava_guava",
626+
"@maven//:com_google_truth_truth",
627+
"@maven//:junit_junit",
628+
"@maven//:org_mockito_mockito_core",
629+
],
630+
)
631+
632+
protobuf_java_library(
633+
name = "v25_test_util_jar",
634+
testonly = True,
635+
srcs = [
636+
"src/test/java/com/google/protobuf/TestUtil.java",
637+
"src/test/java/com/google/protobuf/TestUtilLite.java",
638+
],
639+
deps = [
640+
":core",
641+
"//compatibility:v25_test_protos_jar",
642+
"@maven//:com_google_guava_guava",
643+
"@maven//:junit_junit",
644+
],
645+
)
646+
647+
# Tests binary compatibility against v25 gencode ja compiled against v25 runtime
648+
junit_tests(
649+
name = "v25_core_tests_jar",
650+
size = "small",
651+
srcs = glob(
652+
["src/test/java/**/*.java"],
653+
exclude = [
654+
# Depends on test protos or API changes added in v4.x.x (e.g. editions)
655+
"src/test/java/com/google/protobuf/TextFormatTest.java",
656+
"src/test/java/com/google/protobuf/DescriptorsTest.java",
657+
"src/test/java/com/google/protobuf/DebugFormatTest.java",
658+
"src/test/java/com/google/protobuf/CodedOutputStreamTest.java",
659+
"src/test/java/com/google/protobuf/ProtobufToStringOutputTest.java",
660+
# Excluded in core_tests
661+
"src/test/java/com/google/protobuf/DecodeUtf8Test.java",
662+
"src/test/java/com/google/protobuf/IsValidUtf8Test.java",
663+
"src/test/java/com/google/protobuf/TestUtil.java",
664+
"src/test/java/com/google/protobuf/TestUtilLite.java",
665+
"src/test/java/com/google/protobuf/RuntimeVersionTest.java",
666+
],
667+
),
668+
test_prefix = "v25Jar",
669+
deps = [
670+
":core",
671+
":v25_test_util_jar",
672+
"//compatibility:v25_test_protos_jar",
673+
"@maven//:com_google_guava_guava",
674+
"@maven//:com_google_truth_truth",
675+
"@maven//:junit_junit",
676+
"@maven//:org_mockito_mockito_core",
677+
],
678+
)
679+
584680
pkg_files(
585681
name = "dist_files",
586682
srcs = glob([

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

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -886,16 +886,16 @@ public interface ExtendableMessageOrBuilder<MessageT extends ExtendableMessage<M
886886
Message getDefaultInstanceForType();
887887

888888
/** Check if a singular extension is present. */
889-
<T> boolean hasExtension(ExtensionLite<MessageT, T> extension);
889+
<T> boolean hasExtension(ExtensionLite<? extends MessageT, T> extension);
890890

891891
/** Get the number of elements in a repeated extension. */
892-
<T> int getExtensionCount(ExtensionLite<MessageT, List<T>> extension);
892+
<T> int getExtensionCount(ExtensionLite<? extends MessageT, List<T>> extension);
893893

894894
/** Get the value of an extension. */
895-
<T> T getExtension(ExtensionLite<MessageT, T> extension);
895+
<T> T getExtension(ExtensionLite<? extends MessageT, T> extension);
896896

897897
/** Get one element of a repeated extension. */
898-
<T> T getExtension(ExtensionLite<MessageT, List<T>> extension, int index);
898+
<T> T getExtension(ExtensionLite<? extends MessageT, List<T>> extension, int index);
899899
}
900900

901901
/**
@@ -946,7 +946,7 @@ protected ExtendableMessage(ExtendableBuilder<MessageT, ?> builder) {
946946
this.extensions = builder.buildExtensions();
947947
}
948948

949-
private void verifyExtensionContainingType(final Extension<MessageT, ?> extension) {
949+
private void verifyExtensionContainingType(final Extension<? extends MessageT, ?> extension) {
950950
if (extension.getDescriptor().getContainingType() != getDescriptorForType()) {
951951
// This can only happen if someone uses unchecked operations.
952952
throw new IllegalArgumentException(
@@ -960,7 +960,8 @@ private void verifyExtensionContainingType(final Extension<MessageT, ?> extensio
960960

961961
/** Check if a singular extension is present. */
962962
@Override
963-
public final <T> boolean hasExtension(final ExtensionLite<MessageT, T> extensionLite) {
963+
public final <T> boolean hasExtension(
964+
final ExtensionLite<? extends MessageT, T> extensionLite) {
964965
Extension<MessageT, T> extension = checkNotLite(extensionLite);
965966

966967
verifyExtensionContainingType(extension);
@@ -969,7 +970,8 @@ public final <T> boolean hasExtension(final ExtensionLite<MessageT, T> extension
969970

970971
/** Get the number of elements in a repeated extension. */
971972
@Override
972-
public final <T> int getExtensionCount(final ExtensionLite<MessageT, List<T>> extensionLite) {
973+
public final <T> int getExtensionCount(
974+
final ExtensionLite<? extends MessageT, List<T>> extensionLite) {
973975
Extension<MessageT, List<T>> extension = checkNotLite(extensionLite);
974976

975977
verifyExtensionContainingType(extension);
@@ -980,7 +982,7 @@ public final <T> int getExtensionCount(final ExtensionLite<MessageT, List<T>> ex
980982
/** Get the value of an extension. */
981983
@Override
982984
@SuppressWarnings("unchecked")
983-
public final <T> T getExtension(final ExtensionLite<MessageT, T> extensionLite) {
985+
public final <T> T getExtension(final ExtensionLite<? extends MessageT, T> extensionLite) {
984986
Extension<MessageT, T> extension = checkNotLite(extensionLite);
985987

986988
verifyExtensionContainingType(extension);
@@ -1003,7 +1005,7 @@ public final <T> T getExtension(final ExtensionLite<MessageT, T> extensionLite)
10031005
@Override
10041006
@SuppressWarnings("unchecked")
10051007
public final <T> T getExtension(
1006-
final ExtensionLite<MessageT, List<T>> extensionLite, final int index) {
1008+
final ExtensionLite<? extends MessageT, List<T>> extensionLite, final int index) {
10071009
Extension<MessageT, List<T>> extension = checkNotLite(extensionLite);
10081010

10091011
verifyExtensionContainingType(extension);
@@ -1036,7 +1038,8 @@ protected class ExtensionWriter {
10361038
private Map.Entry<FieldDescriptor, Object> next;
10371039
private final boolean messageSetWireFormat;
10381040

1039-
private ExtensionWriter(final boolean messageSetWireFormat) {
1041+
// TODO: Should be marked private in v5.x.x once GeneratedMessageV3 is removed.
1042+
protected ExtensionWriter(final boolean messageSetWireFormat) {
10401043
if (iter.hasNext()) {
10411044
next = iter.next();
10421045
}
@@ -1254,7 +1257,8 @@ private void verifyExtensionContainingType(final Extension<MessageT, ?> extensio
12541257

12551258
/** Check if a singular extension is present. */
12561259
@Override
1257-
public final <T> boolean hasExtension(final ExtensionLite<MessageT, T> extensionLite) {
1260+
public final <T> boolean hasExtension(
1261+
final ExtensionLite<? extends MessageT, T> extensionLite) {
12581262
Extension<MessageT, T> extension = checkNotLite(extensionLite);
12591263

12601264
verifyExtensionContainingType(extension);
@@ -1263,7 +1267,8 @@ public final <T> boolean hasExtension(final ExtensionLite<MessageT, T> extension
12631267

12641268
/** Get the number of elements in a repeated extension. */
12651269
@Override
1266-
public final <T> int getExtensionCount(final ExtensionLite<MessageT, List<T>> extensionLite) {
1270+
public final <T> int getExtensionCount(
1271+
final ExtensionLite<? extends MessageT, List<T>> extensionLite) {
12671272
Extension<MessageT, List<T>> extension = checkNotLite(extensionLite);
12681273

12691274
verifyExtensionContainingType(extension);
@@ -1273,7 +1278,7 @@ public final <T> int getExtensionCount(final ExtensionLite<MessageT, List<T>> ex
12731278

12741279
/** Get the value of an extension. */
12751280
@Override
1276-
public final <T> T getExtension(final ExtensionLite<MessageT, T> extensionLite) {
1281+
public final <T> T getExtension(final ExtensionLite<? extends MessageT, T> extensionLite) {
12771282
Extension<MessageT, T> extension = checkNotLite(extensionLite);
12781283

12791284
verifyExtensionContainingType(extension);
@@ -1295,7 +1300,7 @@ public final <T> T getExtension(final ExtensionLite<MessageT, T> extensionLite)
12951300
/** Get one element of a repeated extension. */
12961301
@Override
12971302
public final <T> T getExtension(
1298-
final ExtensionLite<MessageT, List<T>> extensionLite, final int index) {
1303+
final ExtensionLite<? extends MessageT, List<T>> extensionLite, final int index) {
12991304
Extension<MessageT, List<T>> extension = checkNotLite(extensionLite);
13001305

13011306
verifyExtensionContainingType(extension);
@@ -1309,7 +1314,7 @@ public final <T> T getExtension(
13091314

13101315
/** Set the value of an extension. */
13111316
public final <T> BuilderT setExtension(
1312-
final ExtensionLite<MessageT, T> extensionLite, final T value) {
1317+
final ExtensionLite<? extends MessageT, T> extensionLite, final T value) {
13131318
Extension<MessageT, T> extension = checkNotLite(extensionLite);
13141319

13151320
verifyExtensionContainingType(extension);
@@ -1322,7 +1327,9 @@ public final <T> BuilderT setExtension(
13221327

13231328
/** Set the value of one element of a repeated extension. */
13241329
public final <T> BuilderT setExtension(
1325-
final ExtensionLite<MessageT, List<T>> extensionLite, final int index, final T value) {
1330+
final ExtensionLite<? extends MessageT, List<T>> extensionLite,
1331+
final int index,
1332+
final T value) {
13261333
Extension<MessageT, List<T>> extension = checkNotLite(extensionLite);
13271334

13281335
verifyExtensionContainingType(extension);
@@ -1335,7 +1342,7 @@ public final <T> BuilderT setExtension(
13351342

13361343
/** Append a value to a repeated extension. */
13371344
public final <T> BuilderT addExtension(
1338-
final ExtensionLite<MessageT, List<T>> extensionLite, final T value) {
1345+
final ExtensionLite<? extends MessageT, List<T>> extensionLite, final T value) {
13391346
Extension<MessageT, List<T>> extension = checkNotLite(extensionLite);
13401347

13411348
verifyExtensionContainingType(extension);
@@ -1347,7 +1354,8 @@ public final <T> BuilderT addExtension(
13471354
}
13481355

13491356
/** Clear an extension. */
1350-
public final <T> BuilderT clearExtension(final ExtensionLite<MessageT, T> extensionLite) {
1357+
public final <T> BuilderT clearExtension(
1358+
final ExtensionLite<? extends MessageT, T> extensionLite) {
13511359
Extension<MessageT, T> extension = checkNotLite(extensionLite);
13521360

13531361
verifyExtensionContainingType(extension);
@@ -1563,7 +1571,8 @@ public Message.Builder newBuilderForField(final FieldDescriptor field) {
15631571
}
15641572
}
15651573

1566-
protected final void mergeExtensionFields(final ExtendableMessage<?> other) {
1574+
// TODO: Should be marked final in v5.x.x once GeneratedMessageV3 is removed.
1575+
protected void mergeExtensionFields(final ExtendableMessage<?> other) {
15671576
if (other.extensions != null) {
15681577
ensureExtensionsIsMutable();
15691578
extensions.mergeFrom(other.extensions);
@@ -1939,7 +1948,8 @@ protected MapField internalGetMapField(int fieldNumber) {
19391948
* Users should ignore this class. This class provides the implementation with access to the
19401949
* fields of a message object using Java reflection.
19411950
*/
1942-
public static final class FieldAccessorTable {
1951+
// TODO: Should be marked final in v5.x.x once GeneratedMessageV3 is removed.
1952+
public static class FieldAccessorTable {
19431953

19441954
/**
19451955
* Construct a FieldAccessorTable for a particular message class. Only one FieldAccessorTable
@@ -3136,7 +3146,7 @@ protected Object writeReplace() throws ObjectStreamException {
31363146
* Checks that the {@link Extension} is non-Lite and returns it as a {@link GeneratedExtension}.
31373147
*/
31383148
private static <MessageT extends ExtendableMessage<MessageT>, T>
3139-
Extension<MessageT, T> checkNotLite(ExtensionLite<MessageT, T> extension) {
3149+
Extension<MessageT, T> checkNotLite(ExtensionLite<? extends MessageT, T> extension) {
31403150
if (extension.isLite()) {
31413151
throw new IllegalArgumentException("Expected non-lite extension.");
31423152
}

0 commit comments

Comments
 (0)