Java fixes for 22.x (#12035)
* Fix mutability bug in Java proto lite: sub-messages inside of oneofs were not
being set as immutable. This would allow code to get a builder for a
sub-message and modify the original (supposedly immutable) copy.
PiperOrigin-RevId: 511598810
* Add casts to make protobuf compatible with Java 1.8 runtime.
Fix for: https://github.com/protocolbuffers/protobuf/issues/11393
PiperOrigin-RevId: 511807920
---------
Co-authored-by: Protobuf Team Bot <protobuf-github-bot@google.com>
diff --git a/java/core/BUILD.bazel b/java/core/BUILD.bazel
index 6113169..c0e52a4 100644
--- a/java/core/BUILD.bazel
+++ b/java/core/BUILD.bazel
@@ -49,6 +49,7 @@
"src/main/java/com/google/protobuf/Internal.java",
"src/main/java/com/google/protobuf/InvalidProtocolBufferException.java",
"src/main/java/com/google/protobuf/IterableByteBufferInputStream.java",
+ "src/main/java/com/google/protobuf/Java8Compatibility.java",
"src/main/java/com/google/protobuf/JavaType.java",
"src/main/java/com/google/protobuf/LazyField.java",
"src/main/java/com/google/protobuf/LazyFieldLite.java",
diff --git a/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java b/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java
index 94b0994..75de57a 100644
--- a/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java
+++ b/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java
@@ -189,7 +189,7 @@
@Override
public AllocatedBuffer position(int position) {
- buffer.position(position);
+ Java8Compatibility.position(buffer, position);
return this;
}
diff --git a/java/core/src/main/java/com/google/protobuf/BinaryWriter.java b/java/core/src/main/java/com/google/protobuf/BinaryWriter.java
index 66cf51d..7eb7886 100644
--- a/java/core/src/main/java/com/google/protobuf/BinaryWriter.java
+++ b/java/core/src/main/java/com/google/protobuf/BinaryWriter.java
@@ -2019,8 +2019,8 @@
buffers.addFirst(allocatedBuffer);
buffer = nioBuffer;
- buffer.limit(buffer.capacity());
- buffer.position(0);
+ Java8Compatibility.limit(buffer, buffer.capacity());
+ Java8Compatibility.position(buffer, 0);
// Set byte order to little endian for fast writing of fixed 32/64.
buffer.order(ByteOrder.LITTLE_ENDIAN);
@@ -2046,7 +2046,7 @@
if (buffer != null) {
totalDoneBytes += bytesWrittenToCurrentBuffer();
// Update the indices on the netty buffer.
- buffer.position(pos + 1);
+ Java8Compatibility.position(buffer, pos + 1);
buffer = null;
pos = 0;
limitMinusOne = 0;
@@ -2475,7 +2475,7 @@
}
pos -= length;
- buffer.position(pos + 1);
+ Java8Compatibility.position(buffer, pos + 1);
buffer.put(value, offset, length);
}
@@ -2494,7 +2494,7 @@
}
pos -= length;
- buffer.position(pos + 1);
+ Java8Compatibility.position(buffer, pos + 1);
buffer.put(value, offset, length);
}
@@ -2506,7 +2506,7 @@
}
pos -= length;
- buffer.position(pos + 1);
+ Java8Compatibility.position(buffer, pos + 1);
buffer.put(value);
}
@@ -2526,7 +2526,7 @@
}
pos -= length;
- buffer.position(pos + 1);
+ Java8Compatibility.position(buffer, pos + 1);
buffer.put(value);
}
@@ -2576,8 +2576,8 @@
buffers.addFirst(allocatedBuffer);
buffer = nioBuffer;
- buffer.limit(buffer.capacity());
- buffer.position(0);
+ Java8Compatibility.limit(buffer, buffer.capacity());
+ Java8Compatibility.position(buffer, 0);
bufferOffset = UnsafeUtil.addressOffset(buffer);
limitMinusOne = bufferOffset + (buffer.limit() - 1);
@@ -2602,7 +2602,7 @@
if (buffer != null) {
totalDoneBytes += bytesWrittenToCurrentBuffer();
// Update the indices on the netty buffer.
- buffer.position(bufferPos() + 1);
+ Java8Compatibility.position(buffer, bufferPos() + 1);
buffer = null;
pos = 0;
limitMinusOne = 0;
@@ -3016,7 +3016,7 @@
}
pos -= length;
- buffer.position(bufferPos() + 1);
+ Java8Compatibility.position(buffer, bufferPos() + 1);
buffer.put(value, offset, length);
}
@@ -3035,7 +3035,7 @@
}
pos -= length;
- buffer.position(bufferPos() + 1);
+ Java8Compatibility.position(buffer, bufferPos() + 1);
buffer.put(value, offset, length);
}
@@ -3047,7 +3047,7 @@
}
pos -= length;
- buffer.position(bufferPos() + 1);
+ Java8Compatibility.position(buffer, bufferPos() + 1);
buffer.put(value);
}
@@ -3067,7 +3067,7 @@
}
pos -= length;
- buffer.position(bufferPos() + 1);
+ Java8Compatibility.position(buffer, bufferPos() + 1);
buffer.put(value);
}
diff --git a/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java b/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
index 2cb3ada..3970b0e 100644
--- a/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
+++ b/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
@@ -107,7 +107,7 @@
}
} finally {
// Restore the initial position.
- buffer.position(initialPos);
+ Java8Compatibility.position(buffer, initialPos);
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
index 4ad8309..44dd4dc 100644
--- a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -1271,7 +1271,7 @@
write(value.array(), value.arrayOffset(), value.capacity());
} else {
ByteBuffer duplicated = value.duplicate();
- duplicated.clear();
+ Java8Compatibility.clear(duplicated);
write(duplicated);
}
}
@@ -1522,7 +1522,7 @@
@Override
public void flush() {
// Update the position on the buffer.
- byteBuffer.position(initialPosition + getTotalBytesWritten());
+ Java8Compatibility.position(byteBuffer, initialPosition + getTotalBytesWritten());
}
}
@@ -1684,7 +1684,7 @@
write(value.array(), value.arrayOffset(), value.capacity());
} else {
ByteBuffer duplicated = value.duplicate();
- duplicated.clear();
+ Java8Compatibility.clear(duplicated);
write(duplicated);
}
}
@@ -1794,18 +1794,18 @@
// Save the current position and increment past the length field. We'll come back
// and write the length field after the encoding is complete.
final int startOfBytes = buffer.position() + minLengthVarIntSize;
- buffer.position(startOfBytes);
+ Java8Compatibility.position(buffer, startOfBytes);
// Encode the string.
encode(value);
// Now go back to the beginning and write the length.
int endOfBytes = buffer.position();
- buffer.position(startPos);
+ Java8Compatibility.position(buffer, startPos);
writeUInt32NoTag(endOfBytes - startOfBytes);
// Reposition the buffer past the written data.
- buffer.position(endOfBytes);
+ Java8Compatibility.position(buffer, endOfBytes);
} else {
final int length = Utf8.encodedLength(value);
writeUInt32NoTag(length);
@@ -1813,7 +1813,7 @@
}
} catch (UnpairedSurrogateException e) {
// Roll back the change and convert to an IOException.
- buffer.position(startPos);
+ Java8Compatibility.position(buffer, startPos);
// TODO(nathanmittler): We should throw an IOException here instead.
inefficientWriteStringNoTag(value, e);
@@ -1826,7 +1826,7 @@
@Override
public void flush() {
// Update the position of the original buffer.
- originalBuffer.position(buffer.position());
+ Java8Compatibility.position(originalBuffer, buffer.position());
}
@Override
@@ -2014,7 +2014,7 @@
write(value.array(), value.arrayOffset(), value.capacity());
} else {
ByteBuffer duplicated = value.duplicate();
- duplicated.clear();
+ Java8Compatibility.clear(duplicated);
write(duplicated);
}
}
@@ -2150,7 +2150,7 @@
// Save the current position and increment past the length field. We'll come back
// and write the length field after the encoding is complete.
int stringStart = bufferPos(position) + minLengthVarIntSize;
- buffer.position(stringStart);
+ Java8Compatibility.position(buffer, stringStart);
// Encode the string.
Utf8.encodeUtf8(value, buffer);
@@ -2187,7 +2187,7 @@
@Override
public void flush() {
// Update the position of the original buffer.
- originalBuffer.position(bufferPos(position));
+ Java8Compatibility.position(originalBuffer, bufferPos(position));
}
@Override
@@ -2201,7 +2201,7 @@
}
private void repositionBuffer(long pos) {
- buffer.position(bufferPos(pos));
+ Java8Compatibility.position(buffer, bufferPos(pos));
}
private int bufferPos(long pos) {
@@ -2478,7 +2478,7 @@
write(value.array(), value.arrayOffset(), value.capacity());
} else {
ByteBuffer duplicated = value.duplicate();
- duplicated.clear();
+ Java8Compatibility.clear(duplicated);
write(duplicated);
}
}
@@ -2792,7 +2792,7 @@
write(value.array(), value.arrayOffset(), value.capacity());
} else {
ByteBuffer duplicated = value.duplicate();
- duplicated.clear();
+ Java8Compatibility.clear(duplicated);
write(duplicated);
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/Internal.java b/java/core/src/main/java/com/google/protobuf/Internal.java
index 7a7270b..127a1656 100644
--- a/java/core/src/main/java/com/google/protobuf/Internal.java
+++ b/java/core/src/main/java/com/google/protobuf/Internal.java
@@ -313,7 +313,11 @@
}
// ByteBuffer.equals() will only compare the remaining bytes, but we want to
// compare all the content.
- return a.duplicate().clear().equals(b.duplicate().clear());
+ ByteBuffer aDuplicate = a.duplicate();
+ Java8Compatibility.clear(aDuplicate);
+ ByteBuffer bDuplicate = b.duplicate();
+ Java8Compatibility.clear(bDuplicate);
+ return aDuplicate.equals(bDuplicate);
}
/** Helper method for implementing {@link Message#equals(Object)} for bytes field. */
@@ -353,7 +357,7 @@
bytes.capacity() > DEFAULT_BUFFER_SIZE ? DEFAULT_BUFFER_SIZE : bytes.capacity();
final byte[] buffer = new byte[bufferSize];
final ByteBuffer duplicated = bytes.duplicate();
- duplicated.clear();
+ Java8Compatibility.clear(duplicated);
int h = bytes.capacity();
while (duplicated.remaining() > 0) {
final int length =
diff --git a/java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java b/java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java
index 713e806..1e571cf 100644
--- a/java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java
+++ b/java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java
@@ -140,9 +140,9 @@
updateCurrentByteBufferPos(length);
} else {
int prevPos = currentByteBuffer.position();
- currentByteBuffer.position(currentByteBufferPos);
+ Java8Compatibility.position(currentByteBuffer, currentByteBufferPos);
currentByteBuffer.get(output, offset, length);
- currentByteBuffer.position(prevPos);
+ Java8Compatibility.position(currentByteBuffer, prevPos);
updateCurrentByteBufferPos(length);
}
return length;
diff --git a/java/core/src/main/java/com/google/protobuf/Java8Compatibility.java b/java/core/src/main/java/com/google/protobuf/Java8Compatibility.java
new file mode 100644
index 0000000..ef181c2
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Java8Compatibility.java
@@ -0,0 +1,67 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.nio.Buffer;
+
+/**
+ * Wrappers around {@link Buffer} methods that are covariantly overridden in Java 9+. See
+ * https://github.com/protocolbuffers/protobuf/issues/11393
+ *
+ * <p>TODO(b/270454719) remove when Java 8 support is no longer needed.
+ */
+final class Java8Compatibility {
+ static void clear(Buffer b) {
+ b.clear();
+ }
+
+ static void flip(Buffer b) {
+ b.flip();
+ }
+
+ static void limit(Buffer b, int limit) {
+ b.limit(limit);
+ }
+
+ static void mark(Buffer b) {
+ b.mark();
+ }
+
+ static void position(Buffer b, int position) {
+ b.position(position);
+ }
+
+ static void reset(Buffer b) {
+ b.reset();
+ }
+
+ private Java8Compatibility() {}
+}
diff --git a/java/core/src/main/java/com/google/protobuf/MessageSchema.java b/java/core/src/main/java/com/google/protobuf/MessageSchema.java
index 084d687..ae5dddc 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageSchema.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageSchema.java
@@ -5510,6 +5510,12 @@
getMessageFieldSchema(pos).makeImmutable(UNSAFE.getObject(message, offset));
}
break;
+ case 60: // ONEOF_MESSAGE
+ case 68: // ONEOF_GROUP
+ if (isOneofPresent(message, numberAt(pos), pos)) {
+ getMessageFieldSchema(pos).makeImmutable(UNSAFE.getObject(message, offset));
+ }
+ break;
case 18: // DOUBLE_LIST:
case 19: // FLOAT_LIST:
case 20: // INT64_LIST:
diff --git a/java/core/src/main/java/com/google/protobuf/NioByteString.java b/java/core/src/main/java/com/google/protobuf/NioByteString.java
index 1e594ff..f625eda 100644
--- a/java/core/src/main/java/com/google/protobuf/NioByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/NioByteString.java
@@ -37,7 +37,6 @@
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
-import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.InvalidMarkException;
@@ -110,7 +109,7 @@
protected void copyToInternal(
byte[] target, int sourceOffset, int targetOffset, int numberToCopy) {
ByteBuffer slice = buffer.slice();
- ((Buffer) slice).position(sourceOffset);
+ Java8Compatibility.position(slice, sourceOffset);
slice.get(target, targetOffset, numberToCopy);
}
@@ -224,7 +223,7 @@
@Override
public void mark(int readlimit) {
- buf.mark();
+ Java8Compatibility.mark(buf);
}
@Override
@@ -235,7 +234,7 @@
@Override
public void reset() throws IOException {
try {
- buf.reset();
+ Java8Compatibility.reset(buf);
} catch (InvalidMarkException e) {
throw new IOException(e);
}
@@ -286,8 +285,8 @@
}
ByteBuffer slice = buffer.slice();
- ((Buffer) slice).position(beginIndex - buffer.position());
- ((Buffer) slice).limit(endIndex - buffer.position());
+ Java8Compatibility.position(slice, beginIndex - buffer.position());
+ Java8Compatibility.limit(slice, endIndex - buffer.position());
return slice;
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/TextFormat.java b/java/core/src/main/java/com/google/protobuf/TextFormat.java
index 8c3ac5c..9d28d06 100644
--- a/java/core/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java
@@ -1710,7 +1710,7 @@
if (n == -1) {
break;
}
- buffer.flip();
+ Java8Compatibility.flip(buffer);
text.append(buffer, 0, n);
}
return text;
diff --git a/java/core/src/main/java/com/google/protobuf/Utf8.java b/java/core/src/main/java/com/google/protobuf/Utf8.java
index c74497c..7c6823d 100644
--- a/java/core/src/main/java/com/google/protobuf/Utf8.java
+++ b/java/core/src/main/java/com/google/protobuf/Utf8.java
@@ -770,7 +770,7 @@
if (out.hasArray()) {
final int offset = out.arrayOffset();
int endIndex = Utf8.encode(in, out.array(), offset + out.position(), out.remaining());
- out.position(endIndex - offset);
+ Java8Compatibility.position(out, endIndex - offset);
} else if (out.isDirect()) {
encodeUtf8Direct(in, out);
} else {
@@ -801,7 +801,7 @@
}
if (inIx == inLength) {
// Successfully encoded the entire string.
- out.position(outIx + inIx);
+ Java8Compatibility.position(out, outIx + inIx);
return;
}
@@ -844,7 +844,7 @@
}
// Successfully encoded the entire string.
- out.position(outIx);
+ Java8Compatibility.position(out, outIx);
} catch (IndexOutOfBoundsException e) {
// TODO(nathanmittler): Consider making the API throw IndexOutOfBoundsException instead.
@@ -1545,7 +1545,7 @@
}
if (inIx == inLimit) {
// We're done, it was ASCII encoded.
- out.position((int) (outIx - address));
+ Java8Compatibility.position(out, (int) (outIx - address));
return;
}
@@ -1585,7 +1585,7 @@
}
// All bytes have been encoded.
- out.position((int) (outIx - address));
+ Java8Compatibility.position(out, (int) (outIx - address));
}
/**
diff --git a/java/lite/pom.xml b/java/lite/pom.xml
index 716ff74..d472b36 100644
--- a/java/lite/pom.xml
+++ b/java/lite/pom.xml
@@ -125,6 +125,7 @@
<include>Internal.java</include>
<include>InvalidProtocolBufferException.java</include>
<include>IterableByteBufferInputStream.java</include>
+ <include>Java8Compatibility.java</include>
<include>JavaType.java</include>
<include>LazyField.java</include>
<include>LazyFieldLite.java</include>
diff --git a/java/lite/src/test/java/com/google/protobuf/LiteTest.java b/java/lite/src/test/java/com/google/protobuf/LiteTest.java
index a4f95a2..a58ce95 100644
--- a/java/lite/src/test/java/com/google/protobuf/LiteTest.java
+++ b/java/lite/src/test/java/com/google/protobuf/LiteTest.java
@@ -191,6 +191,18 @@
}
@Test
+ public void testParsedOneofSubMessageIsImmutable() throws InvalidProtocolBufferException {
+ TestAllTypesLite message =
+ TestAllTypesLite.parseFrom(
+ TestAllTypesLite.newBuilder()
+ .setOneofNestedMessage(NestedMessage.newBuilder().addDd(1234).build())
+ .build()
+ .toByteArray());
+ IntArrayList subList = (IntArrayList) message.getOneofNestedMessage().getDdList();
+ assertThat(subList.isModifiable()).isFalse();
+ }
+
+ @Test
public void testMemoization() throws Exception {
GeneratedMessageLite<?, ?> message = TestUtilLite.getAllLiteExtensionsSet();
@@ -2365,8 +2377,7 @@
Foo fooWithOnlyValue = Foo.newBuilder().setValue(1).build();
Foo fooWithValueAndExtension =
- fooWithOnlyValue
- .toBuilder()
+ fooWithOnlyValue.toBuilder()
.setValue(1)
.setExtension(Bar.fooExt, Bar.newBuilder().setName("name").build())
.build();
@@ -2382,8 +2393,7 @@
Foo fooWithOnlyValue = Foo.newBuilder().setValue(1).build();
Foo fooWithValueAndExtension =
- fooWithOnlyValue
- .toBuilder()
+ fooWithOnlyValue.toBuilder()
.setValue(1)
.setExtension(Bar.fooExt, Bar.newBuilder().setName("name").build())
.build();
@@ -2515,9 +2525,9 @@
assertWithMessage("expected exception").fail();
} catch (InvalidProtocolBufferException expected) {
assertThat(
- TestAllExtensionsLite.newBuilder()
- .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
- .build())
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
+ .build())
.isEqualTo(expected.getUnfinishedMessage());
}
}
diff --git a/src/google/protobuf/unittest_lite.proto b/src/google/protobuf/unittest_lite.proto
index 010d4a9..e2730c6 100644
--- a/src/google/protobuf/unittest_lite.proto
+++ b/src/google/protobuf/unittest_lite.proto
@@ -47,6 +47,7 @@
message NestedMessage {
optional int32 bb = 1;
optional int64 cc = 2;
+ repeated int32 dd = 3 [packed = true];
}
message NestedMessage2 {