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 {