Add Support of NIO ByteBuffer overloading methods with Covariant Return Types.
- In JDK 9, [JDK-4774077](https://bugs.openjdk.java.net/browse/JDK-4774077) added a bunch of covariant overrides to `ByteBuffer`
PiperOrigin-RevId: 294712886
diff --git a/src/test/java/com/google/devtools/build/android/desugar/BUILD b/src/test/java/com/google/devtools/build/android/desugar/BUILD
index 4caf05b..5e835d1 100644
--- a/src/test/java/com/google/devtools/build/android/desugar/BUILD
+++ b/src/test/java/com/google/devtools/build/android/desugar/BUILD
@@ -18,6 +18,7 @@
"//src/test/java/com/google/devtools/build/android/desugar/io:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/langmodel:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/nest:srcs",
+ "//src/test/java/com/google/devtools/build/android/desugar/covariantreturn:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/runtime:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/scan:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/stringconcat:srcs",
diff --git a/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/BUILD b/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/BUILD
new file mode 100644
index 0000000..1d7b0bf
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/BUILD
@@ -0,0 +1,50 @@
+load("@rules_java//java:defs.bzl", "java_test")
+
+# Description:
+# Tests for the Java 8 desugaring tool for Android.
+package(
+ default_testonly = 1,
+ default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"],
+)
+
+licenses(["notice"]) # Apache 2.0
+
+java_test(
+ name = "NioBufferRefConverterTest",
+ size = "medium",
+ srcs = ["NioBufferRefConverterTest.java"],
+ data = [
+ ":nio_buffer_invocations_src",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:android_jar_for_testing",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:jacoco_agent_jar",
+ ],
+ jvm_flags = [
+ "-Dinput_srcs='$(locations :nio_buffer_invocations_src)'",
+ "-Djdk.internal.lambda.dumpProxyClasses=$$(mktemp -d)",
+ "-Dandroid_runtime_jar=$(location //src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:android_jar_for_testing)",
+ "-Djacoco_agent_jar=$(location //src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:jacoco_agent_jar)",
+ ],
+ test_class = "com.google.devtools.build.android.desugar.covariantreturn.NioBufferRefConverterTest",
+ deps = [
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:desugar_rule",
+ "//third_party:asm",
+ "//third_party:asm-tree",
+ "//third_party:guava",
+ "//third_party:guava-testlib",
+ "//third_party:jsr330_inject",
+ "//third_party:junit4",
+ "//third_party:truth",
+ "//third_party:truth8",
+ ],
+)
+
+filegroup(
+ name = "nio_buffer_invocations_src",
+ srcs = ["NioBufferInvocations.java"],
+)
+
+filegroup(
+ name = "srcs",
+ testonly = 0,
+ srcs = glob(["**"]),
+)
diff --git a/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferInvocations.java b/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferInvocations.java
new file mode 100644
index 0000000..029b854
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferInvocations.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.devtools.build.android.desugar.covariantreturn;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
+
+/** Test source for {@link NioBufferRefConverterTest}. */
+public final class NioBufferInvocations {
+
+ public static IntBuffer getIntBufferPosition(IntBuffer buffer, int position) {
+ return buffer.position(position);
+ }
+
+ public static CharBuffer getCharBufferPosition(CharBuffer buffer, int position) {
+ return buffer.position(position);
+ }
+
+ public static FloatBuffer getFloatBufferPosition(FloatBuffer buffer, int position) {
+ return buffer.position(position);
+ }
+
+ public static DoubleBuffer getDoubleBufferPosition(DoubleBuffer buffer, int position) {
+ return buffer.position(position);
+ }
+
+ public static ShortBuffer getShortBufferPosition(ShortBuffer buffer, int position) {
+ return buffer.position(position);
+ }
+
+ public static LongBuffer getLongBufferPosition(LongBuffer buffer, int position) {
+ return buffer.position(position);
+ }
+
+ public static ByteBuffer getByteBufferPosition(ByteBuffer buffer, int position) {
+ return buffer.position(position);
+ }
+
+ private NioBufferInvocations() {}
+}
diff --git a/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferRefConverterTest.java b/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferRefConverterTest.java
new file mode 100644
index 0000000..ecb64b1
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferRefConverterTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.devtools.build.android.desugar.covariantreturn;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static org.objectweb.asm.tree.AbstractInsnNode.METHOD_INSN;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.android.desugar.testing.junit.AsmNode;
+import com.google.devtools.build.android.desugar.testing.junit.DesugarRule;
+import com.google.devtools.build.android.desugar.testing.junit.DesugarRunner;
+import com.google.devtools.build.android.desugar.testing.junit.JdkSuppress;
+import com.google.devtools.build.android.desugar.testing.junit.JdkVersion;
+import com.google.devtools.build.android.desugar.testing.junit.RuntimeMethodHandle;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+
+/** Functional Tests for {@link NioBufferRefConverter}. */
+@RunWith(DesugarRunner.class)
+@JdkSuppress(minJdkVersion = JdkVersion.V11)
+public class NioBufferRefConverterTest {
+
+ @Rule
+ public final DesugarRule desugarRule =
+ DesugarRule.builder(this, MethodHandles.lookup())
+ .addSourceInputsFromJvmFlag("input_srcs")
+ .addJavacOptions("-source 11", "-target 11")
+ .addCommandOptions("desugar_nest_based_private_access", "true")
+ .addCommandOptions("allow_empty_bootclasspath", "true")
+ .addCommandOptions("core_library", "true")
+ .setWorkingJavaPackage("com.google.devtools.build.android.desugar.covariantreturn")
+ .build();
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_beforeDesugar(
+ @AsmNode(className = "NioBufferInvocations", memberName = "getByteBufferPosition", round = 0)
+ MethodNode before) {
+ ImmutableList<AbstractInsnNode> methodInvocations =
+ Arrays.stream(before.instructions.toArray())
+ .filter(insnNode -> insnNode.getType() == METHOD_INSN)
+ .collect(toImmutableList());
+
+ assertThat(methodInvocations).hasSize(1);
+ MethodInsnNode methodInsnNode = (MethodInsnNode) Iterables.getOnlyElement(methodInvocations);
+
+ assertThat(methodInsnNode.owner).isEqualTo("java/nio/ByteBuffer");
+ assertThat(methodInsnNode.name).isEqualTo("position");
+ assertThat(methodInsnNode.desc).isEqualTo("(I)Ljava/nio/ByteBuffer;");
+
+ assertThat(methodInsnNode.getNext().getOpcode()).isEqualTo(Opcodes.ARETURN);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_afterDesugar(
+ @AsmNode(className = "NioBufferInvocations", memberName = "getByteBufferPosition", round = 1)
+ MethodNode after) {
+ ImmutableList<AbstractInsnNode> methodInvocations =
+ Arrays.stream(after.instructions.toArray())
+ .filter(insnNode -> insnNode.getType() == METHOD_INSN)
+ .collect(toImmutableList());
+
+ assertThat(methodInvocations).hasSize(1);
+ MethodInsnNode methodInsnNode = (MethodInsnNode) Iterables.getOnlyElement(methodInvocations);
+
+ assertThat(methodInsnNode.owner).isEqualTo("java/nio/ByteBuffer");
+ assertThat(methodInsnNode.name).isEqualTo("position");
+ assertThat(methodInsnNode.desc).isEqualTo("(I)Ljava/nio/Buffer;");
+
+ TypeInsnNode typeInsnNode = (TypeInsnNode) methodInsnNode.getNext();
+ assertThat(typeInsnNode.getOpcode()).isEqualTo(Opcodes.CHECKCAST);
+ assertThat(typeInsnNode.desc).isEqualTo("java/nio/ByteBuffer");
+
+ assertThat(typeInsnNode.getNext().getOpcode()).isEqualTo(Opcodes.ARETURN);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_beforeDesugarInvocation(
+ @RuntimeMethodHandle(
+ className = "NioBufferInvocations",
+ memberName = "getByteBufferPosition",
+ round = 0)
+ MethodHandle before)
+ throws Throwable {
+ ByteBuffer buffer = ByteBuffer.wrap("random text".getBytes(Charset.defaultCharset()));
+ int expectedPos = 2;
+
+ ByteBuffer result = (ByteBuffer) before.invoke(buffer, expectedPos);
+ assertThat(result.position()).isEqualTo(expectedPos);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_afterDesugarInvocationOfByteBufferMethod(
+ @RuntimeMethodHandle(className = "NioBufferInvocations", memberName = "getByteBufferPosition")
+ MethodHandle after)
+ throws Throwable {
+ ByteBuffer buffer = ByteBuffer.wrap("random text".getBytes(Charset.defaultCharset()));
+ int expectedPos = 2;
+
+ ByteBuffer result = (ByteBuffer) after.invoke(buffer, expectedPos);
+ assertThat(result.position()).isEqualTo(expectedPos);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_afterDesugarInvocationOfCharBufferMethod(
+ @RuntimeMethodHandle(className = "NioBufferInvocations", memberName = "getCharBufferPosition")
+ MethodHandle after)
+ throws Throwable {
+ CharBuffer buffer = CharBuffer.wrap("random text".toCharArray());
+ int expectedPos = 2;
+
+ CharBuffer result = (CharBuffer) after.invoke(buffer, expectedPos);
+ assertThat(result.position()).isEqualTo(expectedPos);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_afterDesugarInvocationOfIntBufferMethod(
+ @RuntimeMethodHandle(className = "NioBufferInvocations", memberName = "getIntBufferPosition")
+ MethodHandle after)
+ throws Throwable {
+ IntBuffer buffer = IntBuffer.wrap(new int[] {10, 20, 30});
+ int expectedPos = 2;
+
+ IntBuffer result = (IntBuffer) after.invoke(buffer, expectedPos);
+ assertThat(result.position()).isEqualTo(expectedPos);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_afterDesugarInvocationOfFloatBufferMethod(
+ @RuntimeMethodHandle(
+ className = "NioBufferInvocations",
+ memberName = "getFloatBufferPosition")
+ MethodHandle after)
+ throws Throwable {
+ FloatBuffer buffer = FloatBuffer.wrap(new float[] {10f, 20f, 30f});
+ int expectedPos = 2;
+
+ FloatBuffer result = (FloatBuffer) after.invoke(buffer, expectedPos);
+ assertThat(result.position()).isEqualTo(expectedPos);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_afterDesugarInvocationOfDoubleBufferMethod(
+ @RuntimeMethodHandle(
+ className = "NioBufferInvocations",
+ memberName = "getDoubleBufferPosition")
+ MethodHandle after)
+ throws Throwable {
+ DoubleBuffer buffer = DoubleBuffer.wrap(new double[] {10.0, 20.0, 30.0});
+ int expectedPos = 2;
+
+ DoubleBuffer result = (DoubleBuffer) after.invoke(buffer, expectedPos);
+ assertThat(result.position()).isEqualTo(expectedPos);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_afterDesugarInvocationOfShortBufferMethod(
+ @RuntimeMethodHandle(
+ className = "NioBufferInvocations",
+ memberName = "getShortBufferPosition")
+ MethodHandle after)
+ throws Throwable {
+ ShortBuffer buffer = ShortBuffer.wrap(new short[] {10, 20, 30});
+ int expectedPos = 2;
+
+ ShortBuffer result = (ShortBuffer) after.invoke(buffer, expectedPos);
+ assertThat(result.position()).isEqualTo(expectedPos);
+ }
+
+ @Test
+ public void methodOfNioBufferWithCovariantTypes_afterDesugarInvocationOfLongBufferMethod(
+ @RuntimeMethodHandle(className = "NioBufferInvocations", memberName = "getLongBufferPosition")
+ MethodHandle after)
+ throws Throwable {
+ LongBuffer buffer = LongBuffer.wrap(new long[] {10L, 20L, 30L});
+ int expectedPos = 2;
+
+ LongBuffer result = (LongBuffer) after.invoke(buffer, expectedPos);
+ assertThat(result.position()).isEqualTo(expectedPos);
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD
index 6e7673f..959a814 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD
@@ -41,6 +41,7 @@
"//src/main/java/com/google/devtools/common/options",
"//src/main/protobuf:worker_protocol_java_proto",
"//src/tools/android/java/com/google/devtools/build/android:android_builder_lib",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn",
"//src/tools/android/java/com/google/devtools/build/android/desugar/io",
"//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel",
"//src/tools/android/java/com/google/devtools/build/android/desugar/nest",
@@ -70,6 +71,7 @@
"//src/tools/android/java/com/google/devtools/build/android/desugar/io:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/nest:srcs",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/strconcat:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/scan:srcs",
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
index fe28295..625eb1c 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
@@ -31,6 +31,7 @@
import com.google.common.io.Resources;
import com.google.devtools.build.android.Converters.ExistingPathConverter;
import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.desugar.covariantreturn.NioBufferRefConverter;
import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter;
import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter.UnprefixingClassWriter;
import com.google.devtools.build.android.desugar.io.FileContentProvider;
@@ -1068,6 +1069,8 @@
visitor = new IndyStringConcatDesugaring(classMemberUseCounter, visitor);
}
+ visitor = NioBufferRefConverter.create(visitor, rewriter.getPrefixer());
+
return visitor;
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn/BUILD
new file mode 100644
index 0000000..addc876
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn/BUILD
@@ -0,0 +1,28 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(
+ default_visibility = [
+ "//src/test/java/com/google/devtools/build/android/desugar:__subpackages__",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar:__subpackages__",
+ ],
+)
+
+java_library(
+ name = "covariantreturn",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel",
+ "//third_party:asm",
+ "//third_party:asm-commons",
+ "//third_party:asm-tree",
+ "//third_party:auto_value",
+ "//third_party:guava",
+ "//third_party:jsr305",
+ ],
+)
+
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//src/tools/android/java/com/google/devtools/build/android/desugar:__pkg__"],
+)
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferRefConverter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferRefConverter.java
new file mode 100644
index 0000000..2696d06
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn/NioBufferRefConverter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.devtools.build.android.desugar.covariantreturn;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.android.desugar.langmodel.ClassName;
+import com.google.devtools.build.android.desugar.langmodel.MethodKey;
+import com.google.devtools.build.android.desugar.langmodel.TypeMapper;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A bytecode converter that supports to use covariant return types in the NIO buffer hierarchy.
+ *
+ * @see https://bugs.openjdk.java.net/browse/JDK-4774077.
+ */
+public final class NioBufferRefConverter extends ClassVisitor {
+
+ /** The inheritance hierarchy root of Java NIO buffer family. */
+ private static final ClassName NIO_BUFFER_BASE = ClassName.create("java/nio/Buffer");
+
+ /** All overloading methods in {@link java.nio.Buffer} with covariant return type. */
+ private static final ImmutableList<MethodKey> BASE_METHODS_WITH_COVARIANT_RETURN_TYPES =
+ ImmutableList.of(
+ MethodKey.create(NIO_BUFFER_BASE, "position", "(I)Ljava/nio/Buffer;"),
+ MethodKey.create(NIO_BUFFER_BASE, "limit", "(I)Ljava/nio/Buffer;"),
+ MethodKey.create(NIO_BUFFER_BASE, "mark", "()Ljava/nio/Buffer;"),
+ MethodKey.create(NIO_BUFFER_BASE, "reset", "()Ljava/nio/Buffer;"),
+ MethodKey.create(NIO_BUFFER_BASE, "clear", "()Ljava/nio/Buffer;"),
+ MethodKey.create(NIO_BUFFER_BASE, "flip", "()Ljava/nio/Buffer;"),
+ MethodKey.create(NIO_BUFFER_BASE, "rewind", "()Ljava/nio/Buffer;"));
+
+ /** All public type-specific NIO buffer classes derived from {@link java.nio.Buffer}. */
+ private static final ImmutableSet<ClassName> TYPE_SPECIFIC_NIO_BUFFERS =
+ ImmutableSet.of(
+ ClassName.create("java/nio/IntBuffer"),
+ ClassName.create("java/nio/CharBuffer"),
+ ClassName.create("java/nio/FloatBuffer"),
+ ClassName.create("java/nio/DoubleBuffer"),
+ ClassName.create("java/nio/ShortBuffer"),
+ ClassName.create("java/nio/LongBuffer"),
+ ClassName.create("java/nio/ByteBuffer"));
+
+ /** Used to find the replacement method from the original method invocation specification. */
+ private final ImmutableMap<MethodKey, MethodKey> methodInvocationMappings;
+
+ /** The public factory API for this class. */
+ public static NioBufferRefConverter create(
+ ClassVisitor classVisitor, TypeMapper corePackagePrefixer) {
+ return new NioBufferRefConverter(
+ classVisitor, corePackagePrefixer.map(getMethodInvocationMappings()));
+ }
+
+ private NioBufferRefConverter(
+ ClassVisitor classVisitor, ImmutableMap<MethodKey, MethodKey> methodInvocationMappings) {
+ super(Opcodes.ASM7, classVisitor);
+ this.methodInvocationMappings = methodInvocationMappings;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
+ return mv == null ? null : new NioBufferMethodVisitor(api, mv, methodInvocationMappings);
+ }
+
+ /** Computes methods in Java NIO buffer family that are subject to invocation conversion. */
+ private static ImmutableMap<MethodKey, MethodKey> getMethodInvocationMappings() {
+ ImmutableMap.Builder<MethodKey, MethodKey> methodMappings = ImmutableMap.builder();
+ for (ClassName typeSpecificNioBuffer : TYPE_SPECIFIC_NIO_BUFFERS) {
+ for (MethodKey baseMethod : BASE_METHODS_WITH_COVARIANT_RETURN_TYPES) {
+ methodMappings.put(
+ MethodKey.create(
+ typeSpecificNioBuffer,
+ baseMethod.name(),
+ Type.getMethodDescriptor(
+ typeSpecificNioBuffer.toAsmObjectType(), baseMethod.getArgumentTypeArray())),
+ MethodKey.create(typeSpecificNioBuffer, baseMethod.name(), baseMethod.descriptor()));
+ }
+ }
+ return methodMappings.build();
+ }
+
+ private static class NioBufferMethodVisitor extends MethodVisitor {
+
+ private final ImmutableMap<MethodKey, MethodKey> methodInvocationMappings;
+
+ NioBufferMethodVisitor(
+ int api,
+ MethodVisitor methodVisitor,
+ ImmutableMap<MethodKey, MethodKey> methodInvocationMappings) {
+ super(api, methodVisitor);
+ this.methodInvocationMappings = methodInvocationMappings;
+ }
+
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface) {
+ MethodKey methodKey = MethodKey.create(ClassName.create(owner), name, descriptor);
+ if (methodInvocationMappings.containsKey(methodKey)) {
+ MethodKey mappedMethodKey = methodInvocationMappings.get(methodKey);
+ super.visitMethodInsn(
+ opcode,
+ mappedMethodKey.ownerName(),
+ mappedMethodKey.name(),
+ mappedMethodKey.descriptor(),
+ isInterface);
+ super.visitTypeInsn(Opcodes.CHECKCAST, owner);
+ return;
+ }
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKey.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKey.java
index b80ca57..b10e6e7 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKey.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKey.java
@@ -16,6 +16,8 @@
package com.google.devtools.build.android.desugar.langmodel;
+import org.objectweb.asm.Type;
+
/** The key that indexes a class member, including fields, constructors and methods. */
public abstract class ClassMemberKey<T extends ClassMemberKey<T>> implements TypeMappable<T> {
@@ -36,6 +38,11 @@
return owner().binaryName();
}
+ /** The asm type name of {@link #owner()} */
+ public final Type ownerAsmObjectType() {
+ return owner().toAsmObjectType();
+ }
+
/** Whether member key represents a constructor. */
public final boolean isConstructor() {
return "<init>".equals(name());
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecord.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecord.java
index d6bd18c..94dfd4b 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecord.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecord.java
@@ -55,12 +55,13 @@
}
/** Find all member keys that represent a constructor. */
- public ImmutableList<ClassMemberKey> findAllConstructorMemberKeys() {
+ public ImmutableList<ClassMemberKey<?>> findAllConstructorMemberKeys() {
return findAllMatchedMemberKeys(ClassMemberKey::isConstructor);
}
/** Find all member keys based on the given member key predicate. */
- ImmutableList<ClassMemberKey> findAllMatchedMemberKeys(Predicate<ClassMemberKey> predicate) {
+ ImmutableList<ClassMemberKey<?>> findAllMatchedMemberKeys(
+ Predicate<ClassMemberKey<?>> predicate) {
return reasons.keySet().stream().filter(predicate).collect(toImmutableList());
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java
index fb47b5f..7cb47c7 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java
@@ -44,7 +44,11 @@
}
public static ClassName create(Class<?> clazz) {
- return create(Type.getInternalName(clazz));
+ return create(Type.getType(clazz));
+ }
+
+ public static ClassName create(Type asmType) {
+ return create(asmType.getInternalName());
}
public final Type toAsmObjectType() {
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodKey.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodKey.java
index 3e153f2..ca868c5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodKey.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodKey.java
@@ -19,7 +19,7 @@
import static com.google.common.base.Preconditions.checkState;
import com.google.auto.value.AutoValue;
-import java.util.Arrays;
+import com.google.common.collect.ImmutableList;
import org.objectweb.asm.Type;
/** The key to index a class or interface method or constructor. */
@@ -43,20 +43,32 @@
return Type.getReturnType(descriptor());
}
+ /** The return type of a method. */
+ public ClassName getReturnTypeName() {
+ return ClassName.create(Type.getReturnType(descriptor()));
+ }
+
/** The formal parameter types of a method. */
- public Type[] getArgumentTypes() {
+ public Type[] getArgumentTypeArray() {
return Type.getArgumentTypes(descriptor());
}
+ /** The formal parameter types of a method. */
+ public ImmutableList<Type> getArgumentTypes() {
+ return ImmutableList.copyOf(getArgumentTypeArray());
+ }
+
/** The synthetic constructor for a private constructor. */
public final MethodKey bridgeOfConstructor(ClassName nestCompanion) {
checkState(isConstructor(), "Expect to use for a constructor but is %s", this);
Type companionClassType = nestCompanion.toAsmObjectType();
- Type[] argumentTypes = getArgumentTypes();
- Type[] bridgeConstructorArgTypes = Arrays.copyOf(argumentTypes, argumentTypes.length + 1);
- bridgeConstructorArgTypes[argumentTypes.length] = companionClassType;
+ ImmutableList<Type> argumentTypes = getArgumentTypes();
+ ImmutableList<Type> bridgeConstructorArgTypes =
+ ImmutableList.<Type>builder().addAll(argumentTypes).add(companionClassType).build();
return create(
- owner(), name(), Type.getMethodDescriptor(getReturnType(), bridgeConstructorArgTypes));
+ owner(),
+ name(),
+ Type.getMethodDescriptor(getReturnType(), bridgeConstructorArgTypes.toArray(new Type[0])));
}
/** The synthetic bridge method for a private static method in a class. */
@@ -84,11 +96,14 @@
/** The descriptor of the static version of a given instance method. */
private static String instanceMethodToStaticDescriptor(MethodKey methodKey) {
checkState(!methodKey.isConstructor(), "Expect a Non-constructor method: %s", methodKey);
- Type[] argumentTypes = methodKey.getArgumentTypes();
- Type[] bridgeMethodArgTypes = new Type[argumentTypes.length + 1];
- bridgeMethodArgTypes[0] = Type.getObjectType(methodKey.ownerName());
- System.arraycopy(argumentTypes, 0, bridgeMethodArgTypes, 1, argumentTypes.length);
- return Type.getMethodDescriptor(methodKey.getReturnType(), bridgeMethodArgTypes);
+ ImmutableList<Type> argumentTypes = methodKey.getArgumentTypes();
+ ImmutableList<Type> bridgeMethodArgTypes =
+ ImmutableList.<Type>builder()
+ .add(methodKey.ownerAsmObjectType())
+ .addAll(argumentTypes)
+ .build();
+ return Type.getMethodDescriptor(
+ methodKey.getReturnType(), bridgeMethodArgTypes.toArray(new Type[0]));
}
@Override
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java
index 4e0d1e9..188c7f3 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java
@@ -16,6 +16,13 @@
package com.google.devtools.build.android.desugar.langmodel;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import java.util.function.Function;
import org.objectweb.asm.commons.Remapper;
@@ -36,4 +43,21 @@
public ClassName map(ClassName internalName) {
return classNameMapper.apply(internalName);
}
+
+ public <E extends TypeMappable<E>> ImmutableList<? extends E> map(
+ ImmutableList<E> mappableTypes) {
+ return mappableTypes.stream().map(e -> e.acceptTypeMapper(this)).collect(toImmutableList());
+ }
+
+ public <E extends TypeMappable<E>> ImmutableSet<? extends E> map(ImmutableSet<E> mappableTypes) {
+ return mappableTypes.stream().map(e -> e.acceptTypeMapper(this)).collect(toImmutableSet());
+ }
+
+ public <K extends TypeMappable<K>, V extends TypeMappable<V>> ImmutableMap<K, V> map(
+ ImmutableMap<K, V> mappableTypes) {
+ return mappableTypes.entrySet().stream()
+ .collect(
+ toImmutableMap(
+ e -> e.getKey().acceptTypeMapper(this), e -> e.getValue().acceptTypeMapper(this)));
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/RuntimeEntityResolver.java b/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/RuntimeEntityResolver.java
index 6dce8d6..06b7888 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/RuntimeEntityResolver.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/RuntimeEntityResolver.java
@@ -381,8 +381,7 @@
if (restoredClassMemberKey == null || restoredClassMemberKey.isEmpty()) {
throw new IllegalStateException(
String.format(
- "Unable to find class member (%s). Please check its presence.",
- restoredClassMemberKey));
+ "Unable to find class member (%s). Please check its presence.", classMemberKey));
} else if (restoredClassMemberKey.size() > 1) {
throw new IllegalStateException(
String.format(