Java fixes for 19.x (#12038) * 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 * Update packaging for autotools Freakin' autotools, we were supposed to be done with you! --------- Co-authored-by: Protobuf Team Bot <protobuf-github-bot@google.com>
diff --git a/Makefile.am b/Makefile.am index 41d4061..33225b8 100644 --- a/Makefile.am +++ b/Makefile.am
@@ -330,6 +330,7 @@ java/core/src/main/java/com/google/protobuf/Internal.java \ java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \ java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java \ + java/core/src/main/java/com/google/protobuf/Java8Compatibility.java \ java/core/src/main/java/com/google/protobuf/JavaType.java \ java/core/src/main/java/com/google/protobuf/LazyField.java \ java/core/src/main/java/com/google/protobuf/LazyFieldLite.java \
diff --git a/java/core/BUILD b/java/core/BUILD index 419eafb..37ef950 100644 --- a/java/core/BUILD +++ b/java/core/BUILD
@@ -45,6 +45,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 a01a6c1..09c5d53 100644 --- a/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java +++ b/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java
@@ -187,7 +187,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 94259ec..3e0721b 100644 --- a/java/core/src/main/java/com/google/protobuf/BinaryWriter.java +++ b/java/core/src/main/java/com/google/protobuf/BinaryWriter.java
@@ -2014,8 +2014,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); @@ -2041,7 +2041,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; @@ -2467,7 +2467,7 @@ } pos -= length; - buffer.position(pos + 1); + Java8Compatibility.position(buffer, pos + 1); buffer.put(value, offset, length); } @@ -2486,7 +2486,7 @@ } pos -= length; - buffer.position(pos + 1); + Java8Compatibility.position(buffer, pos + 1); buffer.put(value, offset, length); } @@ -2498,7 +2498,7 @@ } pos -= length; - buffer.position(pos + 1); + Java8Compatibility.position(buffer, pos + 1); buffer.put(value); } @@ -2518,7 +2518,7 @@ } pos -= length; - buffer.position(pos + 1); + Java8Compatibility.position(buffer, pos + 1); buffer.put(value); } @@ -2568,8 +2568,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); @@ -2594,7 +2594,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; @@ -3006,7 +3006,7 @@ } pos -= length; - buffer.position(bufferPos() + 1); + Java8Compatibility.position(buffer, bufferPos() + 1); buffer.put(value, offset, length); } @@ -3025,7 +3025,7 @@ } pos -= length; - buffer.position(bufferPos() + 1); + Java8Compatibility.position(buffer, bufferPos() + 1); buffer.put(value, offset, length); } @@ -3037,7 +3037,7 @@ } pos -= length; - buffer.position(bufferPos() + 1); + Java8Compatibility.position(buffer, bufferPos() + 1); buffer.put(value); } @@ -3057,7 +3057,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 12f2097..9ec3908 100644 --- a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -1262,7 +1262,7 @@ write(value.array(), value.arrayOffset(), value.capacity()); } else { ByteBuffer duplicated = value.duplicate(); - duplicated.clear(); + Java8Compatibility.clear(duplicated); write(duplicated); } } @@ -1543,7 +1543,7 @@ @Override public void flush() { // Update the position on the buffer. - byteBuffer.position(initialPosition + getTotalBytesWritten()); + Java8Compatibility.position(byteBuffer, initialPosition + getTotalBytesWritten()); } } @@ -1705,7 +1705,7 @@ write(value.array(), value.arrayOffset(), value.capacity()); } else { ByteBuffer duplicated = value.duplicate(); - duplicated.clear(); + Java8Compatibility.clear(duplicated); write(duplicated); } } @@ -1815,18 +1815,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); @@ -1834,7 +1834,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); @@ -1847,7 +1847,7 @@ @Override public void flush() { // Update the position of the original buffer. - originalBuffer.position(buffer.position()); + Java8Compatibility.position(originalBuffer, buffer.position()); } @Override @@ -2035,7 +2035,7 @@ write(value.array(), value.arrayOffset(), value.capacity()); } else { ByteBuffer duplicated = value.duplicate(); - duplicated.clear(); + Java8Compatibility.clear(duplicated); write(duplicated); } } @@ -2171,7 +2171,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); @@ -2208,7 +2208,7 @@ @Override public void flush() { // Update the position of the original buffer. - originalBuffer.position(bufferPos(position)); + Java8Compatibility.position(originalBuffer, bufferPos(position)); } @Override @@ -2222,7 +2222,7 @@ } private void repositionBuffer(long pos) { - buffer.position(bufferPos(pos)); + Java8Compatibility.position(buffer, bufferPos(pos)); } private int bufferPos(long pos) { @@ -2499,7 +2499,7 @@ write(value.array(), value.arrayOffset(), value.capacity()); } else { ByteBuffer duplicated = value.duplicate(); - duplicated.clear(); + Java8Compatibility.clear(duplicated); write(duplicated); } } @@ -2813,7 +2813,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 07e8dd1..1676a26 100644 --- a/java/core/src/main/java/com/google/protobuf/Internal.java +++ b/java/core/src/main/java/com/google/protobuf/Internal.java
@@ -308,7 +308,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. */ @@ -348,7 +352,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 76220cc..35ca407 100644 --- a/java/core/src/main/java/com/google/protobuf/MessageSchema.java +++ b/java/core/src/main/java/com/google/protobuf/MessageSchema.java
@@ -5508,6 +5508,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 b350524..06db1c5 100644 --- a/java/core/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java
@@ -1684,7 +1684,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 7c9133e..c59a050 100644 --- a/java/core/src/main/java/com/google/protobuf/Utf8.java +++ b/java/core/src/main/java/com/google/protobuf/Utf8.java
@@ -769,7 +769,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 { @@ -800,7 +800,7 @@ } if (inIx == inLength) { // Successfully encoded the entire string. - out.position(outIx + inIx); + Java8Compatibility.position(out, outIx + inIx); return; } @@ -843,7 +843,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. @@ -1606,7 +1606,7 @@ } if (inIx == inLimit) { // We're done, it was ASCII encoded. - out.position((int) (outIx - address)); + Java8Compatibility.position(out, (int) (outIx - address)); return; } @@ -1646,7 +1646,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 e4edb4d..a29f5e1 100644 --- a/java/lite/pom.xml +++ b/java/lite/pom.xml
@@ -123,6 +123,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 6535a1b..23654f8 100644 --- a/java/lite/src/test/java/com/google/protobuf/LiteTest.java +++ b/java/lite/src/test/java/com/google/protobuf/LiteTest.java
@@ -189,6 +189,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(); @@ -2346,8 +2358,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(); @@ -2363,8 +2374,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();
diff --git a/src/google/protobuf/unittest_lite.proto b/src/google/protobuf/unittest_lite.proto index 92282a6..9ee290b 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 {