diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index f33e45d8bf067a..7485e3ec7d1e05 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -265,6 +265,12 @@ tasks: - "//tools/test/..." # Re-enable the following tests on Windows: # https://github.com/bazelbuild/bazel/issues/4292 + - "-//src/test/java/com/google/devtools/build/android/desugar/nest/..." + - "-//src/test/java/com/google/devtools/build/android/desugar/stringconcat/..." + - "-//src/test/java/com/google/devtools/build/android/desugar/testing/junit/..." + - "-//src/test/java/com/google/devtools/build/android/desugar/covariantreturn/..." + - "-//src/test/java/com/google/devtools/build/android/desugar/scan/..." + - "-//src/test/java/com/google/devtools/build/android/desugar/typeannotation/..." - "-//src/test/java/com/google/devtools/build/android/r8/..." - "-//src/test/java/com/google/devtools/build/lib/query2/cquery/..." - "-//src/test/java/com/google/devtools/build/lib/query2/engine/..." 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 96480813e94a69..97f9e155ef4ab8 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/BUILD +++ b/src/test/java/com/google/devtools/build/android/desugar/BUILD @@ -17,7 +17,13 @@ filegroup( "//src/test/java/com/google/devtools/build/android/desugar/dependencies:srcs", "//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/typeannotation: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", + "//src/test/java/com/google/devtools/build/android/desugar/testing/junit:srcs", ], visibility = ["//src/test/java/com/google/devtools/build/android:__pkg__"], ) 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 00000000000000..4308ccb07845d8 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/covariantreturn/BUILD @@ -0,0 +1,49 @@ +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", + ], +) + +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 00000000000000..029b854e4b6f25 --- /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 00000000000000..53054039cf9386 --- /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("--release 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 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 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/test/java/com/google/devtools/build/android/desugar/nest/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/BUILD new file mode 100644 index 00000000000000..a92d62fc6c989c --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/BUILD @@ -0,0 +1,234 @@ +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 = "ClassFileFormatTest", + size = "medium", + srcs = ["ClassFileFormatTest.java"], + data = [ + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat", + "//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 //src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat)'", + "-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.nest.ClassFileFormatTest", + 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", + ], +) + +java_test( + name = "NestAnalyzerTest", + size = "medium", + srcs = ["NestAnalyzerTest.java"], + data = [ + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer:analyzed_target", + ], + jvm_flags = [ + "-Dinput_srcs=$(location //src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer:analyzed_target)", + ], + test_class = "com.google.devtools.build.android.desugar.nest.NestAnalyzerTest", + deps = [ + "//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", + "//src/tools/android/java/com/google/devtools/build/android/desugar/preanalysis", + "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:desugar_rule", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "NestDigestTest", + size = "small", + srcs = ["NestDigestTest.java"], + test_class = "com.google.devtools.build.android.desugar.nest.NestDigestTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel", + "//src/tools/android/java/com/google/devtools/build/android/desugar/nest", + "//third_party:asm", + "//third_party:asm-tree", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "NestDesugaringConstructorAccessTest", + size = "medium", + srcs = ["NestDesugaringConstructorAccessTest.java"], + data = [ + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor", + "//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 //src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor)'", + # Required by Desugar#verifyLambdaDumpDirectoryRegistered + "-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.nest.NestDesugaringConstructorAccessTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:desugar_rule", + "//third_party:asm-tree", + "//third_party:guava", + "//third_party:jsr330_inject", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "NestDesugaringFieldAccessTest", + size = "medium", + srcs = ["NestDesugaringFieldAccessTest.java"], + data = [ + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field", + "//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=$(location //src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field)", + # Required by Desugar#verifyLambdaDumpDirectoryRegistered + "-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.nest.NestDesugaringFieldAccessTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:desugar_rule", + "//third_party:asm", + "//third_party:asm-tree", + "//third_party:jsr330_inject", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "NestDesugaringInterfaceMethodAccessTest", + size = "medium", + srcs = ["NestDesugaringInterfaceMethodAccessTest.java"], + data = [ + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod", + "//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=$(location //src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod)", + # Required by Desugar#verifyLambdaDumpDirectoryRegistered + "-Djdk.internal.lambda.dumpProxyClasses=$$(mktemp -d)", + "-Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT", + "-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.nest.NestDesugaringInterfaceMethodAccessTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:desugar_rule", + "//third_party:asm-tree", + "//third_party:guava", + "//third_party:jsr330_inject", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "NestDesugaringMethodAccessTest", + size = "medium", + srcs = ["NestDesugaringMethodAccessTest.java"], + data = [ + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method", + "//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 //src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method)'", + # Required by Desugar#verifyLambdaDumpDirectoryRegistered + "-Djdk.internal.lambda.dumpProxyClasses=$$(mktemp -d)", + "-Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT", + "-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.nest.NestDesugaringMethodAccessTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel", + "//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:jsr330_inject", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "NestDesugaringComplexCasesTest", + size = "medium", + srcs = ["NestDesugaringComplexCasesTest.java"], + data = [ + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase", + "//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 //src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase)'", + # Required by Desugar#verifyLambdaDumpDirectoryRegistered + "-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.nest.NestDesugaringComplexCasesTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:desugar_rule", + "//third_party:asm-tree", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]) + [ + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase:srcs", + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer:srcs", + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor:srcs", + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing:srcs", + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat:srcs", + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field:srcs", + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod:srcs", + "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method:srcs", + ], +) + +test_suite( + name = "AllTests", +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/ClassFileFormatTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/ClassFileFormatTest.java new file mode 100644 index 00000000000000..6a89d951eff6b5 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/ClassFileFormatTest.java @@ -0,0 +1,100 @@ +/* + * 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.nest; + +import static com.google.common.truth.Truth.assertThat; + +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 java.lang.invoke.MethodHandles; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.tree.ClassNode; + +/** + * Tests for accessing a series of private fields, constructors and methods from another class + * within a nest. + */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public final class ClassFileFormatTest { + + private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + + @Rule + @SuppressWarnings("SplitterToStream") // Pending bazel guava update. + public final DesugarRule desugarRule = + DesugarRule.builder(this, lookup) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("--release 11") + .setWorkingJavaPackage( + "com.google.devtools.build.android.desugar.nest.testsrc.simpleunit.classfileformat") + .addCommandOptions("desugar_nest_based_private_access", "true") + .build(); + + @Test + public void classFileMajorVersions( + @AsmNode(className = "NestOuterInterfaceA", round = 0) ClassNode beforeDesugarClassNode, + @AsmNode(className = "NestOuterInterfaceA", round = 1) ClassNode afterDesugarClassNode) { + assertThat(beforeDesugarClassNode.version).isEqualTo(JdkVersion.V11); + assertThat(afterDesugarClassNode.version).isEqualTo(JdkVersion.V1_7); + } + + @Test + public void nestMembersAttribute_strippedOutAfterDesugaring( + @AsmNode(className = "NestOuterInterfaceA", round = 0) ClassNode before, + @AsmNode(className = "NestOuterInterfaceA", round = 1) ClassNode after) { + assertThat(before.nestMembers).isNotEmpty(); + assertThat(after.nestMembers).isNull(); + } + + @Test + public void nestHostAttribute_strippedOutAfterDesugaringForNestedClass( + @AsmNode(className = "NestOuterInterfaceA$NestedClassB", round = 0) ClassNode before, + @AsmNode(className = "NestOuterInterfaceA$NestedClassB", round = 1) ClassNode after) { + assertThat(before.nestHostClass).isNotEmpty(); + assertThat(after.nestHostClass).isNull(); + } + + @Test + public void nestHostAttribute_strippedOutAfterDesugaringForNestedInterface( + @AsmNode(className = "NestOuterInterfaceA$NestedInterfaceC", round = 0) ClassNode before, + @AsmNode(className = "NestOuterInterfaceA$NestedInterfaceC", round = 1) ClassNode after) { + assertThat(before.nestHostClass).isNotEmpty(); + assertThat(after.nestHostClass).isNull(); + } + + @Test + public void nestHostAttribute_strippedOutAfterDesugaringForNestedAnnotation( + @AsmNode(className = "NestOuterInterfaceA$NestedAnnotationD", round = 0) ClassNode before, + @AsmNode(className = "NestOuterInterfaceA$NestedAnnotationD", round = 1) ClassNode after) { + assertThat(before.nestHostClass).isNotEmpty(); + assertThat(after.nestHostClass).isNull(); + } + + @Test + public void nestHostAttribute_strippedOutAfterDesugaringForNestedEnum( + @AsmNode(className = "NestOuterInterfaceA$NestedEnumE", round = 0) ClassNode before, + @AsmNode(className = "NestOuterInterfaceA$NestedEnumE", round = 1) ClassNode after) { + assertThat(before.nestHostClass).isNotEmpty(); + assertThat(after.nestHostClass).isNull(); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestAnalyzerTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestAnalyzerTest.java new file mode 100644 index 00000000000000..1ef8e5f5f01192 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestAnalyzerTest.java @@ -0,0 +1,101 @@ +// Copyright 2019 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.nest; + +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.android.desugar.io.FileContentProvider; +import com.google.devtools.build.android.desugar.io.JarItem; +import com.google.devtools.build.android.desugar.preanalysis.InputPreAnalyzer; +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.RuntimeJarEntry; +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.Attribute; + +/** The tests for {@link NestAnalyzer}. */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public final class NestAnalyzerTest { + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, MethodHandles.lookup()) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("--release 11") + .setWorkingJavaPackage( + "com.google.devtools.build.android.desugar.nest.testsrc.nestanalyzer") + .enableIterativeTransformation(0) + .build(); + + @Test + public void emptyInputFiles() throws IOException { + InputPreAnalyzer inputPreAnalyzer = + new InputPreAnalyzer(/* inputFileContents= */ ImmutableList.of(), new Attribute[] {}); + inputPreAnalyzer.process(); + NestDigest nestDigest = + NestAnalyzer.digest( + inputPreAnalyzer.getClassAttributeRecord(), inputPreAnalyzer.getClassMemberRecord()); + + assertThat(nestDigest.getAllCompanionClassNames()).isEmpty(); + } + + @Test + public void companionClassGeneration( + @RuntimeJarEntry( + value = "AnalyzedTarget.class", + round = 0) // Without desugaring at zero-th round. + JarItem analyzedTarget) + throws IOException { + JarFile jarFile = analyzedTarget.jarFile(); + + InputPreAnalyzer inputPreAnalyzer = + new InputPreAnalyzer( + jarFile.stream() + .map( + entry -> + new FileContentProvider<>( + entry.getName(), () -> getJarEntryInputStream(jarFile, entry))) + .collect(toImmutableList()), + new Attribute[] {}); + inputPreAnalyzer.process(); + NestDigest nestDigest = + NestAnalyzer.digest( + inputPreAnalyzer.getClassAttributeRecord(), inputPreAnalyzer.getClassMemberRecord()); + + assertThat(nestDigest.getAllCompanionClassNames()) + .containsExactly( + "com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer/AnalyzedTarget$NestCC"); + } + + private static InputStream getJarEntryInputStream(JarFile jarFile, JarEntry entry) { + try { + return jarFile.getInputStream(entry); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringComplexCasesTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringComplexCasesTest.java new file mode 100644 index 00000000000000..97e1408884bf5f --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringComplexCasesTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2019 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.nest; + +import static com.google.common.truth.Truth.assertThat; + +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 org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.tree.ClassNode; + +/** + * Tests for accessing a series of private fields, constructors and methods from another class + * within a nest. + */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public final class NestDesugaringComplexCasesTest { + + private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, lookup) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("--release 11") + .setWorkingJavaPackage( + "com.google.devtools.build.android.desugar.nest.testsrc.complexcase") + .addCommandOptions("desugar_nest_based_private_access", "true") + .build(); + + @Test + public void inputClassFileMajorVersions( + @AsmNode(className = "Xylem", round = 0) ClassNode beforeDesugarClassNode, + @AsmNode(className = "Xylem", round = 1) ClassNode afterDesugarClassNode) { + assertThat(beforeDesugarClassNode.version).isEqualTo(JdkVersion.V11); + assertThat(afterDesugarClassNode.version).isEqualTo(JdkVersion.V1_7); + } + + @Test + public void comprehensiveTest( + @RuntimeMethodHandle(className = "Xylem", memberName = "execute") MethodHandle xylemExecute) + throws Throwable { + long result = (long) xylemExecute.invoke((long) 2L, (int) 3); + assertThat(result).isEqualTo(14004004171L); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringConstructorAccessTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringConstructorAccessTest.java new file mode 100644 index 00000000000000..33a258b1640068 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringConstructorAccessTest.java @@ -0,0 +1,173 @@ +/* + * Copyright 2019 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.nest; + +import static com.google.common.truth.Truth.assertThat; + +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.DynamicClassLiteral; +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.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import javax.inject.Inject; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +/** Tests for accessing private constructors from another class within a nest. */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public final class NestDesugaringConstructorAccessTest { + + private static final Lookup lookup = MethodHandles.lookup(); + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, lookup) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("--release 11") + .setWorkingJavaPackage( + "com.google.devtools.build.android.desugar.nest.testsrc.simpleunit.constructor") + .addCommandOptions("desugar_nest_based_private_access", "true") + .build(); + + @Inject + @DynamicClassLiteral("ConstructorNest$ConstructorServiceMate") + private Class mate; + + @Inject + @DynamicClassLiteral("ConstructorNest") + private Class invoker; + + @Test + public void inputClassFileMajorVersions( + @AsmNode(className = "ConstructorNest", round = 0) ClassNode beforeDesugarClassNode, + @AsmNode(className = "ConstructorNest", round = 1) ClassNode afterDesugarClassNode) { + assertThat(beforeDesugarClassNode.version).isEqualTo(JdkVersion.V11); + assertThat(afterDesugarClassNode.version).isEqualTo(JdkVersion.V1_7); + } + + @Test + public void companionClassIsPresent( + @DynamicClassLiteral("ConstructorNest$NestCC") Class companion) { + assertThat(companion).isNotNull(); + } + + @Test + public void companionClassHierarchy( + @DynamicClassLiteral("ConstructorNest$NestCC") Class companion) { + assertThat(companion.getEnclosingClass()).isEqualTo(invoker); + assertThat(companion.getEnclosingConstructor()).isNull(); + assertThat(companion.getEnclosingMethod()).isNull(); + } + + @Test + public void companionClassModifiers( + @DynamicClassLiteral("ConstructorNest$NestCC") Class companion) { + assertThat(companion.isSynthetic()).isTrue(); + assertThat(companion.isMemberClass()).isTrue(); + assertThat(Modifier.isAbstract(companion.getModifiers())).isTrue(); + assertThat(Modifier.isStatic(companion.getModifiers())).isTrue(); + } + + @Test + public void constructorBridgeGeneration() { + assertThat(mate.getDeclaredConstructors()).hasLength(4); + } + + @Test + public void zeroArgConstructorBridge( + @DynamicClassLiteral("ConstructorNest$NestCC") Class companion) throws Exception { + Constructor constructor = mate.getDeclaredConstructor(companion); + assertThat(constructor.getModifiers() & 0x7).isEqualTo(0); + } + + @Test + public void multiArgConstructorBridge( + @DynamicClassLiteral("ConstructorNest$NestCC") Class companion) throws Exception { + Constructor constructor = mate.getDeclaredConstructor(long.class, int.class, companion); + + assertThat(Modifier.isPublic(constructor.getModifiers())).isFalse(); + assertThat(Modifier.isPrivate(constructor.getModifiers())).isFalse(); + assertThat(Modifier.isProtected(constructor.getModifiers())).isFalse(); + } + + @Test + public void createFromEmptyArgConstructor( + @RuntimeMethodHandle( + className = "ConstructorNest", + memberName = "createFromZeroArgConstructor") + MethodHandle createFromZeroArgConstructor) + throws Throwable { + long result = (long) createFromZeroArgConstructor.invoke(); + assertThat(result).isEqualTo(30L); + } + + @Test + public void createFromMultiArgConstructor( + @RuntimeMethodHandle( + className = "ConstructorNest", + memberName = "createFromMultiArgConstructor") + MethodHandle createFromMultiArgConstructor) + throws Throwable { + long result = (long) createFromMultiArgConstructor.invoke((long) 20L, (int) 30); + assertThat(result).isEqualTo(50L); + } + + @Test + public void nestWithDollarSignNamedClasses_nestHostSyntheticConstructor( + @AsmNode( + className = "$Dollar$Sign$Named$Nest$", + memberName = "", + memberDescriptor = + "(JLcom/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/$Dollar$Sign$Named$Nest$$NestCC;)V") + MethodNode constructor) + throws Throwable { + assertThat(constructor).isNotNull(); + } + + @Test + public void nestWithDollarSignNamedClasses_nestMemberSyntheticConstructor( + @AsmNode( + className = "$Dollar$Sign$Named$Nest$$$Dollar$Sign$Named$Member$", + memberName = "", + memberDescriptor = + "(Lcom/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/$Dollar$Sign$Named$Nest$;Lcom/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/$Dollar$Sign$Named$Nest$$NestCC;)V") + MethodNode constructor) + throws Throwable { + assertThat(constructor).isNotNull(); + } + + @Test + public void nestWithDollarSignNamedClasses_execute( + @RuntimeMethodHandle(className = "DollarSignNamedNest", memberName = "execute") + MethodHandle execute) + throws Throwable { + + long result = (long) execute.invoke((long) 10L); + assertThat(result).isEqualTo(14L); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringCoreLibTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringCoreLibTest.java new file mode 100644 index 00000000000000..1e5932b7495d79 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringCoreLibTest.java @@ -0,0 +1,80 @@ +/* + * 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.nest; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.android.desugar.testing.junit.DesugarTestHelpers.getRuntimePathsFromJvmFlag; + +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.lang.invoke.MethodHandles.Lookup; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.tree.ClassNode; + +/** Tests for accessing private constructors from another class within a nest. */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public class NestDesugaringCoreLibTest { + + private static final Lookup lookup = MethodHandles.lookup(); + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, lookup) + .addSourceInputs(getRuntimePathsFromJvmFlag("input_srcs")) + .addJavacOptions("--release 11") + .addCommandOptions("desugar_nest_based_private_access", "true") + .addCommandOptions("allow_empty_bootclasspath", "true") + .addCommandOptions("core_library", "true") + .addCommandOptions("desugar_supported_core_libs", "true") + .addCommandOptions("rewrite_core_library_prefix", "javadesugar/testing/") + .build(); + + @Test + public void inputClassFileMajorVersions( + @AsmNode(className = "javadesugar.testing.TestCoreType$MateA", round = 0) ClassNode before, + @AsmNode(className = "jd$.testing.TestCoreType$MateA", round = 1) ClassNode after) { + assertThat(before.version).isEqualTo(JdkVersion.V11); + assertThat(after.version).isEqualTo(JdkVersion.V1_7); + } + + @Test + public void invokeInterMatePrivateStaticMethodOfCoreLibType( + @RuntimeMethodHandle(className = "jd$.testing.TestCoreType", memberName = "twoSum") + MethodHandle twoSum) + throws Throwable { + long result = (long) twoSum.invoke(1L, 2L); + assertThat(result).isEqualTo(3L); + } + + @Test + public void invokeInterMatePrivateInstanceMethodOfCoreLibType( + @RuntimeMethodHandle(className = "jd$.testing.TestCoreType", memberName = "twoSumWithBase") + MethodHandle twoSum) + throws Throwable { + long result = (long) twoSum.invoke(1000L, 1L, 2L); + assertThat(result).isEqualTo(1003L); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringFieldAccessTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringFieldAccessTest.java new file mode 100644 index 00000000000000..dab87aebe60c0c --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringFieldAccessTest.java @@ -0,0 +1,423 @@ +/* + * Copyright 2019 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.nest; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; + +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.DynamicClassLiteral; +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.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import javax.inject.Inject; +import org.junit.Before; +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.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +/** Tests for accessing private fields from another class within a nest. */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public final class NestDesugaringFieldAccessTest { + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, MethodHandles.lookup()) + .addSourceInputsFromJvmFlag("input_srcs") + .setWorkingJavaPackage( + "com.google.devtools.build.android.desugar.nest.testsrc.simpleunit.field") + .addJavacOptions("--release 11") + .addCommandOptions("desugar_nest_based_private_access", "true") + .build(); + + @Inject + @DynamicClassLiteral("FieldNest$FieldOwnerMate") + private Class mate; + + private Object mateInstance; + + @Before + public void loadClasses() throws Exception { + mateInstance = mate.getConstructor().newInstance(); + } + + @Test + public void inputClassFileMajorVersions( + @AsmNode(className = "FieldNest", round = 0) ClassNode beforeDesugarClassNode, + @AsmNode(className = "FieldNest", round = 1) ClassNode afterDesugarClassNode) { + assertThat(beforeDesugarClassNode.version).isEqualTo(JdkVersion.V11); + assertThat(afterDesugarClassNode.version).isEqualTo(JdkVersion.V1_7); + } + + @Test + public void bridgeMethodGeneration() { + List bridgeMethodNames = + Arrays.stream(mate.getDeclaredMethods()) + .map(Method::getName) + .filter(name -> !name.startsWith("$jacoco")) + .collect(Collectors.toList()); + assertThat(bridgeMethodNames) + .containsExactly( + "privateStaticField$bridge_getter", + "privateStaticField$bridge_setter", + "privateInstanceField$bridge_getter", + "privateInstanceField$bridge_setter", + "privateInstanceWideField$bridge_setter", + "getPrivateStaticFieldInBoundary", + "getPrivateInstanceFieldInBoundary", + "privateStaticFieldReadOnly$bridge_getter", + "privateInstanceFieldReadOnly$bridge_getter", + "privateStaticArrayField$bridge_getter", + "privateInstanceArrayField$bridge_getter"); + } + + @Test + public void getStaticField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "getStaticField") + MethodHandle getStaticField) + throws Throwable { + long result = (long) getStaticField.invoke(); + assertThat(result).isEqualTo(10L); + } + + @Test + public void getInstanceField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "getInstanceField") + MethodHandle getInstanceField) + throws Throwable { + long result = (long) getInstanceField.invoke(mateInstance); + assertThat(result).isEqualTo(20); + } + + @Test + public void getPrivateStaticField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "getPrivateStaticField") + MethodHandle getPrivateStaticField) + throws Throwable { + long result = (long) getPrivateStaticField.invoke(); + assertThat(result).isEqualTo(30L); + } + + @Test + public void setPrivateStaticField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "setPrivateStaticField") + MethodHandle setPrivateStaticField) + throws Throwable { + long result = (long) setPrivateStaticField.invoke((long) 35L); + assertThat(result).isEqualTo(35L); + + Field privateStaticField = mate.getDeclaredField("privateStaticField"); + privateStaticField.setAccessible(true); + assertThat(privateStaticField.get(null)).isEqualTo(35L); + } + + @Test + public void getPrivateInstanceField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "getPrivateInstanceField") + MethodHandle getPrivateInstanceField) + throws Throwable { + int result = (int) getPrivateInstanceField.invoke(mateInstance); + assertThat(result).isEqualTo(40); + } + + @Test + public void setPrivateInstanceField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "setPrivateInstanceField") + MethodHandle setPrivateInstanceField) + throws Throwable { + int result = (int) setPrivateInstanceField.invoke(mateInstance, (int) 45); + assertThat(result).isEqualTo(45); + + Field privateInstanceField = mate.getDeclaredField("privateInstanceField"); + privateInstanceField.setAccessible(true); + assertThat(privateInstanceField.get(mateInstance)).isEqualTo(45); + } + + @Test + public void setPrivateInstanceWideField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "setPrivateInstanceWideField") + MethodHandle setPrivateInstanceWideField) + throws Throwable { + long result = (long) setPrivateInstanceWideField.invoke(mateInstance, 47L); + assertThat(result).isEqualTo(47L); + + Field privateInstanceField = mate.getDeclaredField("privateInstanceWideField"); + privateInstanceField.setAccessible(true); + assertThat(privateInstanceField.get(mateInstance)).isEqualTo(47L); + } + + @Test + public void setPrivateInstanceWideField_opcodes( + @AsmNode(className = "FieldNest", memberName = "setPrivateInstanceWideField") + MethodNode setPrivateInstanceWideField) + throws Throwable { + AbstractInsnNode[] instructions = setPrivateInstanceWideField.instructions.toArray(); + assertThat(Arrays.stream(instructions).map(AbstractInsnNode::getOpcode)) + .containsAtLeast( + Opcodes.ALOAD, + Opcodes.LLOAD, + Opcodes.DUP2_X1, + Opcodes.INVOKESTATIC, + Opcodes.POP2, + Opcodes.LRETURN) + .inOrder(); + } + + @Test + public void getPrivateStaticFieldReadOnly( + @RuntimeMethodHandle(className = "FieldNest", memberName = "getPrivateStaticFieldReadOnly") + MethodHandle getPrivateStaticFieldReadOnly) + throws Throwable { + long result = (long) getPrivateStaticFieldReadOnly.invoke(); + assertThat(result).isEqualTo(50L); + } + + @Test + public void getPrivateInstanceFieldReadOnly( + @RuntimeMethodHandle(className = "FieldNest", memberName = "getPrivateInstanceFieldReadOnly") + MethodHandle getPrivateInstanceFieldReadOnly) + throws Throwable { + long result = (long) getPrivateInstanceFieldReadOnly.invoke(mateInstance); + assertThat(result).isEqualTo(60L); + } + + @Test + public void getPrivateInstanceFieldReadOnly_trace( + @RuntimeMethodHandle(className = "FieldNest", memberName = "getPrivateInstanceFieldReadOnly") + MethodHandle getPrivateInstanceFieldReadOnly) + throws Throwable { + long result = (long) getPrivateInstanceFieldReadOnly.invoke(mateInstance); + assertThat(result).isEqualTo(60L); + } + + @Test + public void getPrivateStaticFieldInBoundary( + @RuntimeMethodHandle(className = "FieldNest", memberName = "getPrivateStaticFieldInBoundary") + MethodHandle getPrivateStaticFieldInBoundary) + throws Throwable { + long result = (long) getPrivateStaticFieldInBoundary.invoke(); + assertThat(result).isEqualTo(70L); + } + + @Test + public void getPrivateInstanceFieldInBoundary( + @RuntimeMethodHandle( + className = "FieldNest", + memberName = "getPrivateInstanceFieldInBoundary") + MethodHandle getPrivateInstanceFieldInBoundary) + throws Throwable { + int result = (int) getPrivateInstanceFieldInBoundary.invoke(mateInstance); + assertThat(result).isEqualTo(80); + } + + @Test + public void getPrivateStaticArrayFieldElement( + @RuntimeMethodHandle( + className = "FieldNest", + memberName = "getPrivateStaticArrayFieldElement") + MethodHandle getPrivateStaticArrayFieldElement) + throws Throwable { + long[] arrayFieldInitialValue = {100L, 200L, 300L}; + + Field field = mate.getDeclaredField("privateStaticArrayField"); + field.setAccessible(true); + field.set(null, arrayFieldInitialValue); + + long result = (long) getPrivateStaticArrayFieldElement.invoke(1); // 1 is for index. + assertThat(result).isEqualTo(200L); + } + + @Test + public void setPrivateStaticArrayFieldElement( + @RuntimeMethodHandle( + className = "FieldNest", + memberName = "setPrivateStaticArrayFieldElement") + MethodHandle setPrivateStaticArrayFieldElement) + throws Throwable { + long[] arrayFieldInitialValue = {200L, 300L, 400L}; + long overriddenValue = 3000L; + + Field field = mate.getDeclaredField("privateStaticArrayField"); + field.setAccessible(true); + field.set(null, arrayFieldInitialValue); + + long result = (long) setPrivateStaticArrayFieldElement.invoke(1, overriddenValue); + assertThat(result).isEqualTo(overriddenValue); + + long[] actual = (long[]) field.get(null); + assertThat(actual).asList().containsExactly(200L, 3000L, 400L).inOrder(); + } + + @Test + public void getPrivateInstanceArrayFieldElement( + @RuntimeMethodHandle( + className = "FieldNest", + memberName = "getPrivateInstanceArrayFieldElement") + MethodHandle getPrivateInstanceArrayFieldElement) + throws Throwable { + int[] arrayFieldInitialValue = {300, 400, 500}; + + Field field = mate.getDeclaredField("privateInstanceArrayField"); + field.setAccessible(true); + field.set(mateInstance, arrayFieldInitialValue); + + int result = (int) getPrivateInstanceArrayFieldElement.invoke(mateInstance, 1); + assertThat(result).isEqualTo(400); + } + + @Test + public void setPrivateInstanceArrayFieldElement( + @RuntimeMethodHandle( + className = "FieldNest", + memberName = "setPrivateInstanceArrayFieldElement") + MethodHandle setPrivateInstanceArrayFieldElement) + throws Throwable { + int[] arrayFieldInitialValue = {400, 500, 600}; + int overriddenValue = 5000; + + Field field = mate.getDeclaredField("privateInstanceArrayField"); + field.setAccessible(true); + field.set(mateInstance, arrayFieldInitialValue); + + long result = + (long) setPrivateInstanceArrayFieldElement.invoke(mateInstance, 1, overriddenValue); + + assertThat(result).isEqualTo(overriddenValue); + + int[] actual = (int[]) field.get(mateInstance); + assertThat(actual).asList().containsExactly(400, 5000, 600).inOrder(); + } + + @Test + public void compoundSetPrivateStaticField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "compoundSetPrivateStaticField") + MethodHandle compoundSetPrivateStaticField) + throws Throwable { + long fieldInitialValue = 100; + long fieldValueDelta = 20; + + Field field = mate.getDeclaredField("privateStaticField"); + field.setAccessible(true); + field.set(null, fieldInitialValue); + + long result = (long) compoundSetPrivateStaticField.invoke(fieldValueDelta); + assertThat(result).isEqualTo(fieldInitialValue + fieldValueDelta); + + assertThat(field.get(null)).isEqualTo(fieldInitialValue + fieldValueDelta); + } + + @Test + public void preIncrementPrivateStaticField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "preIncrementPrivateStaticField") + MethodHandle preIncrementPrivateStaticField) + throws Throwable { + long fieldInitialValue = 200; + + Field field = mate.getDeclaredField("privateStaticField"); + field.setAccessible(true); + field.set(null, fieldInitialValue); + + long result = (long) preIncrementPrivateStaticField.invoke(); + assertThat(result).isEqualTo(fieldInitialValue + 1); + + assertThat(field.get(null)).isEqualTo(fieldInitialValue + 1); + } + + @Test + public void postIncrementPrivateStaticField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "postIncrementPrivateStaticField") + MethodHandle postIncrementPrivateStaticField) + throws Throwable { + long fieldInitialValue = 300; + + Field field = mate.getDeclaredField("privateStaticField"); + field.setAccessible(true); + field.set(null, fieldInitialValue); + + long result = (long) postIncrementPrivateStaticField.invoke(); + assertThat(result).isEqualTo(fieldInitialValue); + + assertThat(field.get(null)).isEqualTo(fieldInitialValue + 1); + } + + @Test + public void compoundSetPrivateInstanceField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "compoundSetPrivateInstanceField") + MethodHandle compoundSetPrivateInstanceField) + throws Throwable { + int fieldInitialValue = 400; + int fieldDelta = 10; + + Field field = mate.getDeclaredField("privateInstanceField"); + field.setAccessible(true); + field.set(mateInstance, fieldInitialValue); + + int result = (int) compoundSetPrivateInstanceField.invoke(mateInstance, fieldDelta); + assertThat(result).isEqualTo(fieldInitialValue + fieldDelta); + + assertThat(field.get(mateInstance)).isEqualTo(fieldInitialValue + fieldDelta); + } + + @Test + public void preIncrementPrivateInstanceField( + @RuntimeMethodHandle(className = "FieldNest", memberName = "preIncrementPrivateInstanceField") + MethodHandle preIncrementPrivateInstanceField) + throws Throwable { + int fieldInitialValue = 500; + + Field field = mate.getDeclaredField("privateInstanceField"); + field.setAccessible(true); + field.set(mateInstance, fieldInitialValue); + + int result = (int) preIncrementPrivateInstanceField.invoke(mateInstance); + assertThat(result).isEqualTo(fieldInitialValue + 1); + + assertThat(field.get(mateInstance)).isEqualTo(fieldInitialValue + 1); + } + + @Test + public void postIncrementPrivateInstanceField( + @RuntimeMethodHandle( + className = "FieldNest", + memberName = "postIncrementPrivateInstanceField") + MethodHandle postIncrementPrivateInstanceField) + throws Throwable { + int fieldInitialValue = 600; + + Field field = mate.getDeclaredField("privateInstanceField"); + field.setAccessible(true); + field.set(mateInstance, fieldInitialValue); + + int result = (int) postIncrementPrivateInstanceField.invoke(mateInstance); + assertThat(result).isEqualTo(fieldInitialValue); + + assertThat(field.get(mateInstance)).isEqualTo(fieldInitialValue + 1); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringInterfaceMethodAccessTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringInterfaceMethodAccessTest.java new file mode 100644 index 00000000000000..17467659fdb21f --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringInterfaceMethodAccessTest.java @@ -0,0 +1,152 @@ +/* + * Copyright 2019 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.nest; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +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.DynamicClassLiteral; +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.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import javax.inject.Inject; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.tree.ClassNode; + +/** Tests for accessing private interface methods from another class within a nest. */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public final class NestDesugaringInterfaceMethodAccessTest { + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, MethodHandles.lookup()) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("--release 11") + .setWorkingJavaPackage( + "com.google.devtools.build.android.desugar.nest.testsrc.simpleunit.interfacemethod") + .addCommandOptions("desugar_nest_based_private_access", "true") + .build(); + + @Inject + @DynamicClassLiteral(value = "InterfaceNest$InterfaceMate") + private Class mate; + + private Object mateInstance; + + @Inject + @DynamicClassLiteral(value = "InterfaceNest$ConcreteMate") + private Class concreteMate; + + @Before + public void loadClassesInNest() throws Exception { + mateInstance = concreteMate.getDeclaredConstructor().newInstance(); + } + + @Test + public void inputClassFileMajorVersions( + @AsmNode(className = "InterfaceNest", round = 0) ClassNode beforeDesugarClassNode, + @AsmNode(className = "InterfaceNest", round = 1) ClassNode afterDesugarClassNode) { + assertThat(beforeDesugarClassNode.version).isEqualTo(JdkVersion.V11); + assertThat(afterDesugarClassNode.version).isEqualTo(JdkVersion.V1_7); + } + + @Test + public void interfaceDeclaredMethods() { + List interfaceMethodNames = + Arrays.stream(mate.getDeclaredMethods()).map(Method::getName).collect(Collectors.toList()); + assertThat(interfaceMethodNames) + .containsExactly("invokeInstanceMethodInClassBoundary", "mateValues"); + } + + @Test + public void invokePrivateStaticMethod( + @RuntimeMethodHandle(className = "InterfaceNest", memberName = "invokePrivateStaticMethod") + MethodHandle invokePrivateStaticMethod) + throws Throwable { + long result = (long) invokePrivateStaticMethod.invoke(10L, 20); + assertThat(result).isEqualTo(30L); + } + + @Test + public void invokePrivateInstanceMethod( + @RuntimeMethodHandle(className = "InterfaceNest", memberName = "invokePrivateInstanceMethod") + MethodHandle invokePrivateInstanceMethod) + throws Throwable { + long result = (long) invokePrivateInstanceMethod.invoke(mateInstance, 20L, 30); + assertThat(result).isEqualTo(50L); + } + + @Test + public void invokeInClassBoundaryStaticMethod( + @RuntimeMethodHandle( + className = "InterfaceNest", + memberName = "invokeInClassBoundaryStaticMethod") + MethodHandle invokeInClassBoundaryStaticMethod) + throws Throwable { + long result = (long) invokeInClassBoundaryStaticMethod.invoke(2L); + assertThat(result).isEqualTo(3L); // 0 + 1 + 2 + } + + @Test + public void invokeInClassBoundaryInstanceMethod( + @RuntimeMethodHandle( + className = "InterfaceNest", + memberName = "invokeInClassBoundaryInstanceMethod") + MethodHandle invokeInClassBoundaryInstanceMethod) + throws Throwable { + long result = (long) invokeInClassBoundaryInstanceMethod.invoke(mateInstance, 3L); + assertThat(result).isEqualTo(6L); // 0 + 1 + 2 + 3 + } + + @Test + public void invokePrivateStaticMethodWithLambda( + @RuntimeMethodHandle( + className = "InterfaceNest", + memberName = "invokePrivateStaticMethodWithLambda") + MethodHandle invokePrivateStaticMethodWithLambda) + throws Throwable { + long result = (long) invokePrivateStaticMethodWithLambda.invoke(2L, 3L, 4L, 5L); + assertThat(result).isEqualTo(17L); + } + + @Test + public void invokePrivateInstanceMethodWithLambda( + @RuntimeMethodHandle( + className = "InterfaceNest", + memberName = "invokePrivateInstanceMethodWithLambda") + MethodHandle invokePrivateInstanceMethodWithLambda) + throws Throwable { + long result = + (long) + invokePrivateInstanceMethodWithLambda.invoke( + mateInstance, ImmutableList.of(2L, 3L), 4L, 5L); + assertThat(result).isEqualTo(64850L); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringMethodAccessTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringMethodAccessTest.java new file mode 100644 index 00000000000000..889a3dd6bb84e8 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringMethodAccessTest.java @@ -0,0 +1,240 @@ +/* + * Copyright 2019 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.nest; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Iterables; +import com.google.devtools.build.android.desugar.langmodel.MemberUseKind; +import com.google.devtools.build.android.desugar.langmodel.MethodInvocationSite; +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.DesugarTestHelpers; +import com.google.devtools.build.android.desugar.testing.junit.DynamicClassLiteral; +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 com.google.devtools.build.android.desugar.testing.junit.RuntimeMethodHandle.MemberUseContext; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import javax.inject.Inject; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +/** Tests for accessing private methods from another class within a nest. */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public final class NestDesugaringMethodAccessTest { + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, MethodHandles.lookup()) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("--release 11") + .setWorkingJavaPackage( + "com.google.devtools.build.android.desugar.nest.testsrc.simpleunit.method") + .addCommandOptions("desugar_nest_based_private_access", "true") + .build(); + + @Inject + @DynamicClassLiteral(value = "MethodNest$MethodOwnerMate") + private Class mate; + + @Inject + @DynamicClassLiteral("MethodNest$SubMate") + private Class subClassMate; + + @Inject + @DynamicClassLiteral("MethodNest") + private Class invoker; + + private Object mateInstance; + private Object invokerInstance; + + @Before + public void loadClassesInNest() throws Exception { + mateInstance = mate.getConstructor().newInstance(); + invokerInstance = invoker.getDeclaredConstructor().newInstance(); + } + + @Test + public void inputClassFileMajorVersions( + @AsmNode(className = "MethodNest", round = 0) ClassNode beforeDesugarClassNode, + @AsmNode(className = "MethodNest", round = 1) ClassNode afterDesugarClassNode) { + assertThat(beforeDesugarClassNode.version).isEqualTo(JdkVersion.V11); + assertThat(afterDesugarClassNode.version).isEqualTo(JdkVersion.V1_7); + } + + @Test + public void methodBridgeGeneration() throws Exception { + List bridgeMethodNames = + Arrays.stream(mate.getDeclaredMethods()) + .map(Method::getName) + .filter(name -> !name.startsWith("$jacoco")) + .collect(Collectors.toList()); + assertThat(bridgeMethodNames) + .containsExactly( + "staticMethod", + "instanceMethod", + "privateStaticMethod", + "privateInstanceMethod", + "inClassBoundStaticMethod", + "inClassBoundInstanceMethod", + "privateStaticMethod$bridge", + "privateInstanceMethod$bridge"); + } + + @Test + public void invokePrivateStaticMethod_staticInitializer( + @RuntimeMethodHandle( + className = "MethodNest", + memberName = "populatedFromInvokePrivateStaticMethod", + usage = MemberUseContext.FIELD_GETTER) + MethodHandle populatedFromInvokePrivateStaticMethod) + throws Throwable { + long result = (long) populatedFromInvokePrivateStaticMethod.invoke(); + assertThat(result).isEqualTo(385L); // 128L + 256 + 1 + } + + @Test + public void invokePrivateInstanceMethod_instanceInitializer( + @RuntimeMethodHandle( + className = "MethodNest", + memberName = "populatedFromInvokePrivateInstanceMethod", + usage = MemberUseContext.FIELD_GETTER) + MethodHandle populatedFromInvokePrivateInstanceMethod) + throws Throwable { + long result = (long) populatedFromInvokePrivateInstanceMethod.invoke(invokerInstance); + assertThat(result).isEqualTo(768L); // 128L + 256 + 1 + } + + @Test + public void invokePrivateStaticMethod( + @RuntimeMethodHandle(className = "MethodNest", memberName = "invokePrivateStaticMethod") + MethodHandle invokePrivateStaticMethod) + throws Throwable { + long result = (long) invokePrivateStaticMethod.invokeExact((long) 1L, (int) 2); + assertThat(result).isEqualTo(1L + 2); + } + + @Test + public void invokePrivateInstanceMethod( + @RuntimeMethodHandle(className = "MethodNest", memberName = "invokePrivateInstanceMethod") + MethodHandle invokePrivateInstanceMethod) + throws Throwable { + long result = (long) invokePrivateInstanceMethod.invoke(mateInstance, 2L, 3); + assertThat(result).isEqualTo(2L + 3); + } + + @Test + public void invokeStaticMethod( + @RuntimeMethodHandle(className = "MethodNest", memberName = "invokeStaticMethod") + MethodHandle invokeStaticMethod) + throws Throwable { + long x = 3L; + int y = 4; + + long result = (long) invokeStaticMethod.invoke(x, y); + assertThat(result).isEqualTo(x + y); + } + + @Test + public void invokeInstanceMethod( + @RuntimeMethodHandle(className = "MethodNest", memberName = "invokeInstanceMethod") + MethodHandle invokeInstanceMethod) + throws Throwable { + long x = 4L; + int y = 5; + + long result = (long) invokeInstanceMethod.invoke(mateInstance, x, y); + assertThat(result).isEqualTo(x + y); + } + + @Test + public void invokeSuperAccessPrivateInstanceMethod( + @RuntimeMethodHandle( + className = "MethodNest", + memberName = "invokeSuperAccessPrivateInstanceMethod") + MethodHandle invokeSuperAccessPrivateInstanceMethod) + throws Throwable { + assertThat( + invokeSuperAccessPrivateInstanceMethod.invoke( + subClassMate.getConstructor().newInstance(), 7L, 8)) + .isEqualTo(16L); // 15 + 1 + } + + @Test + public void invokeCastAccessPrivateInstanceMethod( + @RuntimeMethodHandle( + className = "MethodNest", + memberName = "invokeCastAccessPrivateInstanceMethod") + MethodHandle invokeCastAccessPrivateInstanceMethod) + throws Throwable { + long result = + (long) + invokeCastAccessPrivateInstanceMethod.invoke( + subClassMate.getConstructor().newInstance(), 9L, 10); + assertThat(result).isEqualTo(21L); // 19 + 2 + } + + @Test + public void nonNestInvocationInstructions( + @AsmNode(className = "NonNest", round = 0) ClassNode before, + @AsmNode(className = "NonNest", round = 1) ClassNode after) { + assertThat(before.version).isEqualTo(JdkVersion.V11); + assertThat(after.version).isEqualTo(JdkVersion.V1_7); + } + + @Test + public void invokeVirtualOnPrivateMethod_beforeDesugaring( + @AsmNode(className = "NonNest", memberName = "invokeTwoSum", round = 0) + MethodNode invokeTwoSum) { + MethodInvocationSite twoSumInvocation = + Iterables.getOnlyElement( + DesugarTestHelpers.findMethodInvocationSites(invokeTwoSum, "NonNest", "twoSum", ".*")); + assertThat(twoSumInvocation.invocationKind()).isEqualTo(MemberUseKind.INVOKEVIRTUAL); + } + + @Test + public void invokeSpecialOnPrivateMethod_afterDesugaring( + @AsmNode(className = "NonNest", memberName = "invokeTwoSum", round = 1) + MethodNode invokeTwoSum) { + MethodInvocationSite twoSumInvocation = + Iterables.getOnlyElement( + DesugarTestHelpers.findMethodInvocationSites(invokeTwoSum, "NonNest", "twoSum", ".*")); + assertThat(twoSumInvocation.invocationKind()).isEqualTo(MemberUseKind.INVOKESPECIAL); + } + + @Test + public void pnvokeSpecialOnPrivateMethod_afterDesugaringExecution( + @RuntimeMethodHandle(className = "NonNest", memberName = "invokeTwoSum", round = 1) + MethodHandle invokeTwoSum) + throws Throwable { + long result = (long) invokeTwoSum.invoke(1000L, 2L, 3L); + assertThat(result).isEqualTo(1005L); + } + +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDigestTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDigestTest.java new file mode 100644 index 00000000000000..0f951328f79ce1 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDigestTest.java @@ -0,0 +1,168 @@ +// Copyright 2019 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.nest; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord; +import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord.ClassAttributeRecordBuilder; +import com.google.devtools.build.android.desugar.langmodel.ClassAttributes; +import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord; +import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord.ClassMemberRecordBuilder; +import com.google.devtools.build.android.desugar.langmodel.ClassName; +import com.google.devtools.build.android.desugar.langmodel.MethodKey; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.objectweb.asm.Opcodes; + +/** The tests for {@link NestDigest}. */ +@RunWith(JUnit4.class) +public final class NestDigestTest { + + private final ClassMemberRecordBuilder classMemberRecord = ClassMemberRecord.builder(); + private final ClassAttributeRecordBuilder classAttributeRecord = ClassAttributeRecord.builder(); + + @Test + public void prepareCompanionClassWriters_noCompanionClassesGenerated() { + classMemberRecord.logMemberDecl( + MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I"), + /* ownerAccess= */ Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE, + /* memberDeclAccess= */ Opcodes.ACC_PRIVATE); + + NestDigest nestDigest = + NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); + + assertThat(nestDigest.getAllCompanionClassNames()).isEmpty(); + } + + @Test + public void prepareCompanionClassWriters_companionClassesGenerated() { + MethodKey constructor = + MethodKey.create(ClassName.create("package/path/OwnerClass"), "", "()V"); + classMemberRecord.logMemberDecl( + constructor, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE); + classMemberRecord.logMemberUse(constructor, Opcodes.INVOKESPECIAL); + classAttributeRecord.addClassAttributes( + ClassAttributes.builder() + .setClassBinaryName(ClassName.create("package/path/OwnerClass$NestedClass")) + .setNestHost(ClassName.create("package/path/OwnerClass")) + .build()); + classAttributeRecord.addClassAttributes( + ClassAttributes.builder() + .setClassBinaryName(ClassName.create("package/path/OwnerClass")) + .addNestMember(ClassName.create("package/path/OwnerClass$NestedClass")) + .build()); + + NestDigest nestDigest = + NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); + + assertThat(nestDigest.getAllCompanionClassNames()) + .containsExactly("package/path/OwnerClass$NestCC"); + } + + @Test + public void preparCompanionClassWriters_multipleCompanionClassesGenerated() { + classMemberRecord.logMemberDecl( + MethodKey.create(ClassName.create("package/path/OwnerClassA"), "", "()V"), + Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, + Opcodes.ACC_PRIVATE); + classMemberRecord.logMemberDecl( + MethodKey.create(ClassName.create("package/path/OwnerClassA"), "method", "(II)I"), + Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, + Opcodes.ACC_PRIVATE); + classMemberRecord.logMemberDecl( + MethodKey.create(ClassName.create("package/path/OwnerClassB"), "", "()V"), + Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, + Opcodes.ACC_PRIVATE); + + classMemberRecord.logMemberUse( + MethodKey.create(ClassName.create("package/path/OwnerClassA"), "", "()V"), + Opcodes.INVOKESPECIAL); + classMemberRecord.logMemberUse( + MethodKey.create(ClassName.create("package/path/OwnerClassA"), "method", "(II)I"), + Opcodes.INVOKESPECIAL); + classMemberRecord.logMemberUse( + MethodKey.create(ClassName.create("package/path/OwnerClassB"), "", "()V"), + Opcodes.INVOKESPECIAL); + + classAttributeRecord.addClassAttributes( + ClassAttributes.builder() + .setClassBinaryName(ClassName.create("package/path/OwnerClassA$NestedClass")) + .setNestHost(ClassName.create("package/path/OwnerClassA")) + .build()); + classAttributeRecord.addClassAttributes( + ClassAttributes.builder() + .setClassBinaryName(ClassName.create("package/path/OwnerClassB$NestedClass")) + .setNestHost(ClassName.create("package/path/OwnerClassB")) + .build()); + + classAttributeRecord.addClassAttributes( + ClassAttributes.builder() + .setClassBinaryName(ClassName.create("package/path/OwnerClassA")) + .addNestMember(ClassName.create("package/path/OwnerClassA$NestedClass")) + .build()); + classAttributeRecord.addClassAttributes( + ClassAttributes.builder() + .setClassBinaryName(ClassName.create("package/path/OwnerClassB")) + .addNestMember(ClassName.create("package/path/OwnerClassB$NestedClass")) + .build()); + + NestDigest nestDigest = + NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); + + assertThat(nestDigest.getAllCompanionClassNames()) + .containsExactly("package/path/OwnerClassA$NestCC", "package/path/OwnerClassB$NestCC"); + } + + @Test + public void prepareCompanionClassWriters_classNameWithDollarSign() { + MethodKey constructor = + MethodKey.create(ClassName.create("package/path/$Owner$Class$"), "", "()V"); + + classMemberRecord.logMemberDecl( + constructor, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE); + classMemberRecord.logMemberUse(constructor, Opcodes.INVOKESPECIAL); + + classAttributeRecord.addClassAttributes( + ClassAttributes.builder() + .setClassBinaryName(ClassName.create(constructor.ownerName() + "$NestClass")) + .setNestHost(ClassName.create(constructor.ownerName())) + .build()); + classAttributeRecord.addClassAttributes( + ClassAttributes.builder() + .setClassBinaryName(ClassName.create(constructor.ownerName())) + .addNestMember(ClassName.create(constructor.ownerName() + "$NestClass")) + .build()); + + NestDigest nestDigest = + NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); + + assertThat(nestDigest.getAllCompanionClassNames()) + .containsExactly("package/path/$Owner$Class$$NestCC"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Alpha.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Alpha.java new file mode 100644 index 00000000000000..ae45c733e4ac64 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Alpha.java @@ -0,0 +1,39 @@ +// Copyright 2019 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.nest.testsrc.complexcase; + +/** For testing private interface methods desugaring. */ +public interface Alpha { + + long VAL = 1000L; + + static long publicStaticMethod(Alpha alpha, Bravo bravo, long x, int y) { + return Alpha.VAL + bravo.abstractMethod(x, y) + privateStaticMethod(alpha, bravo, x, y); + } + + static long privateStaticMethod(Alpha alpha, Bravo bravo, long x, int y) { + return Bravo.VAL + alpha.abstractMethod(x, y) + Bravo.publicStaticMethod(alpha, bravo, x, y); + } + + default long defaultMethod(Bravo bravo, long x, int y) { + return Alpha.VAL + bravo.abstractMethod(x, y) + privateInstanceMethod(bravo, x, y); + } + + private long privateInstanceMethod(Bravo bravo, long x, int y) { + return Bravo.VAL + abstractMethod(x, y) + bravo.defaultMethod(this, x, y); + } + + long abstractMethod(long x, int y); +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/BUILD new file mode 100644 index 00000000000000..9bfe7e05b9f8cf --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/BUILD @@ -0,0 +1,17 @@ +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "complexcase", + srcs = glob(["*.java"]), +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Bravo.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Bravo.java new file mode 100644 index 00000000000000..bfd14e2cede8a0 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Bravo.java @@ -0,0 +1,51 @@ +// Copyright 2019 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.nest.testsrc.complexcase; + +/** For testing private interface methods desugaring. */ +public interface Bravo { + + long VAL = 1_000_000L; + + static long publicStaticMethod(Alpha alpha, Bravo bravo, long x, int y) { + return Alpha.VAL + bravo.abstractMethod(x, y) + privateStaticMethod(alpha, bravo, x, y); + } + + static long privateStaticMethod(Alpha alpha, Bravo bravo, long x, int y) { + return Alpha.VAL + bravo.abstractMethod(x, y) + bravo.privateInstanceMethod(alpha, x, y); + } + + private long privateInstanceMethod(Alpha alpha, long x, int y) { + return Bravo.VAL + alpha.abstractMethod(x, y) + alpha.defaultMethod(this, x, y); + } + + default long defaultMethod(Alpha alpha, long x, int y) { + return Bravo.VAL + alpha.abstractMethod(x, y) + abstractMethod(x, y); + } + + private long crossMatePrivateInstanceMethod() { + return 123L; + } + + long abstractMethod(long x, int y); + + /** For testing private interface methods desugaring. */ + interface Charlie extends Bravo { + default long invokeCrossMatePrivateInstanceMethod() { + // Emits invokespecial instruction. + return Bravo.super.crossMatePrivateInstanceMethod(); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Phloem.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Phloem.java new file mode 100644 index 00000000000000..9a269fa054b3ea --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Phloem.java @@ -0,0 +1,18 @@ +// Copyright 2019 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.nest.testsrc.complexcase; + +/** A test class for desugaring operations. */ +public class Phloem {} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Xylem.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Xylem.java new file mode 100644 index 00000000000000..8c82018e3d2af6 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase/Xylem.java @@ -0,0 +1,72 @@ +// Copyright 2019 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.nest.testsrc.complexcase; + +import com.google.devtools.build.android.desugar.nest.testsrc.complexcase.Bravo.Charlie; + +/** Test class as source data. */ +@SuppressWarnings({"PrivateConstructorForUtilityClass", "FieldCanBeFinal"}) // For testing. +public class Xylem { + static class ConcreteAlpha implements Alpha { + private ConcreteAlpha() {} + + private static long privateStaticField = ConcreteBravo.privateStaticMethod(); + private int privateInstanceField = 2; + + @Override + public long abstractMethod(long x, int y) { + return x + y + 1_000_000_000L; + } + } + + static class ConcreteBravo extends ConcreteAlpha implements Bravo { + private ConcreteBravo() {} + + private static long privateStaticMethod() { + return 1L; + } + + private int privateInstanceMethod() { + return super.privateInstanceField; + } + + @Override + public long abstractMethod(long x, int y) { + return x + y + 2_000_000_000L; + } + } + + private static class XylemInvoker { + private static long execute(long x, int y) { + ConcreteBravo bravo = new ConcreteBravo(); + long localSum = x + y; + Charlie charlie = (a, b) -> a + b + localSum; + return ConcreteAlpha.privateStaticField + + bravo.privateInstanceMethod() + + Alpha.publicStaticMethod(new ConcreteAlpha(), bravo, x, y) + + charlie.invokeCrossMatePrivateInstanceMethod(); + } + } + + public static long execute(long x, int y) { + return XylemInvoker.execute(x, y); + } + + public static void main(String[] args) { + int x = Integer.parseInt(args[0]); + int y = Integer.parseInt(args[1]); + System.out.println(execute(x, y)); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer/AnalyzedTarget.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer/AnalyzedTarget.java new file mode 100644 index 00000000000000..9462db3e4d478a --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer/AnalyzedTarget.java @@ -0,0 +1,29 @@ +// Copyright 2019 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.nest.testsrc.nestanalyzer; + +@SuppressWarnings("PrivateConstructorForUtilityClass") // As testing source. +class AnalyzedTarget { + + private static class EnclosedTargetAlpha { + private EnclosedTargetAlpha() {} + } + + static class EnclosedTargetBravo { + public static void main(String[] args) { + System.out.println(new EnclosedTargetAlpha()); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer/BUILD new file mode 100644 index 00000000000000..266d59e493a431 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer/BUILD @@ -0,0 +1,17 @@ +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "analyzed_target", + srcs = glob(["*.java"]), +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat/BUILD new file mode 100644 index 00000000000000..5376b5b2bf9fbe --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat/BUILD @@ -0,0 +1,17 @@ +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "classfileformat", + srcs = glob(["*.java"]), +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat/NestOuterInterfaceA.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat/NestOuterInterfaceA.java new file mode 100644 index 00000000000000..6c651692b39f4a --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat/NestOuterInterfaceA.java @@ -0,0 +1,34 @@ +/* + * 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.nest.testsrc.simpleunit.classfileformat; + +/** A nest for testing the attributes in class file format. */ +@SuppressWarnings("InterfaceWithOnlyStatics") // Contrived class for nest-access based testing. +public interface NestOuterInterfaceA { + + /** A nested class. */ + class NestedClassB {} + + /** A nested interface. */ + interface NestedInterfaceC {} + + /** A nested annotation. */ + @interface NestedAnnotationD {} + + /** A nested enum. */ + enum NestedEnumE {} +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/BUILD new file mode 100644 index 00000000000000..b61987f6b21e99 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/BUILD @@ -0,0 +1,17 @@ +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "constructor", + srcs = glob(["*.java"]), +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/ConstructorNest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/ConstructorNest.java new file mode 100644 index 00000000000000..c2c2c47b25632a --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/ConstructorNest.java @@ -0,0 +1,46 @@ +// Copyright 2019 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.nest.testsrc.simpleunit.constructor; + +/** A nest for testing private constructor desugaring. */ +public class ConstructorNest { + + /** A nest member class that encloses private constructors with cross-mate invocations. */ + public static class ConstructorServiceMate { + private final long x; + private final int y; + + private ConstructorServiceMate() throws Exception { + this(10L, 20); + } + + private ConstructorServiceMate(long x, int y) throws Exception { + this.x = x; + this.y = y; + } + + long getSum() { + return x + y; + } + } + + public static long createFromZeroArgConstructor() throws Exception { + return new ConstructorServiceMate().getSum(); + } + + public static long createFromMultiArgConstructor(long x, int y) throws Exception { + return new ConstructorServiceMate(x, y).getSum(); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/DollarSignNamedNest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/DollarSignNamedNest.java new file mode 100644 index 00000000000000..9a5ff07f81634e --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor/DollarSignNamedNest.java @@ -0,0 +1,54 @@ +/* + * 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.nest.testsrc.simpleunit.constructor; + +/** The entry-point point for testing to desugar classes with '$' in class names. */ +public class DollarSignNamedNest { + public static long execute(long initialValue) { + return $Dollar$Sign$Named$Nest$.execute(initialValue); + } +} + +class $Dollar$Sign$Named$Nest$ { + + private final long value; + + static long execute(long value) { + return new $Dollar$Sign$Named$Nest$(value).toMember().toHost().value; + } + + private $Dollar$Sign$Named$Nest$(long value) { + this.value = value + 1; + } + + $Dollar$Sign$Named$Member$ toMember() { + return new $Dollar$Sign$Named$Member$(this); + } + + static class $Dollar$Sign$Named$Member$ { + + private final long value; + + private $Dollar$Sign$Named$Member$($Dollar$Sign$Named$Nest$ host) { + value = host.value + 2; + } + + $Dollar$Sign$Named$Nest$ toHost() { + return new $Dollar$Sign$Named$Nest$(value); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/BUILD new file mode 100644 index 00000000000000..300fa54d7b7881 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/BUILD @@ -0,0 +1,17 @@ +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "testing", + srcs = glob(["*.java"]), +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/TestCoreType.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/TestCoreType.java new file mode 100644 index 00000000000000..6f42dd1b4f6d66 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/TestCoreType.java @@ -0,0 +1,54 @@ +/* + * 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 javadesugar.testing; + +/** + * A fake core library class for testing core type desugaring. Related flags include, + * + *

--core_library, --desugar_supported_core_libs, --rewrite_core_library_prefix + */ +public class TestCoreType { + + /** Invocation entry point for testing to invoke private static methods in anther mate. */ + public static long twoSum(long x, long y) { + return MateA.twoSum(x, y); + } + + /** Invocation entry point for testing to invoke private instance methods in anther mate. */ + public static long twoSumWithBase(long base, long x, long y) { + return new MateA(base).twoSumWithBase(x, y); + } + + private TestCoreType() {} + + private static class MateA { + + private final long base; + + private MateA(long base) { + this.base = base; + } + + private static long twoSum(long x, long y) { + return x + y; + } + + private long twoSumWithBase(long x, long y) { + return base + x + y; + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field/BUILD new file mode 100644 index 00000000000000..4ccfdf6db8b549 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field/BUILD @@ -0,0 +1,17 @@ +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "field", + srcs = glob(["*.java"]), +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field/FieldNest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field/FieldNest.java new file mode 100644 index 00000000000000..012aaac0a4791e --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field/FieldNest.java @@ -0,0 +1,139 @@ +// Copyright 2019 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.nest.testsrc.simpleunit.field; + +/** A nest for testing private field desugaring. */ +public class FieldNest { + + /** A nest member that encloses private fields with cross-mate reads and writes. */ + public static class FieldOwnerMate { + + static long staticField = 10L; + + int instanceField = 20; + + private static long privateStaticField = 30L; + + private int privateInstanceField = 40; + + private long privateInstanceWideField = 45L; + + private static long privateStaticFieldReadOnly = 50L; + + private int privateInstanceFieldReadOnly = 60; + + private static long privateStaticFieldInBoundary = 70L; + + private int privateInstanceFieldInBoundary = 80; + + private static long[] privateStaticArrayField = {90L, 100L}; + + private int[] privateInstanceArrayField = {110, 120}; + + public static long getPrivateStaticFieldInBoundary() { + return privateStaticFieldInBoundary; + } + + public int getPrivateInstanceFieldInBoundary() { + return privateInstanceFieldInBoundary; + } + } + + public static synchronized long getStaticField() { + return FieldOwnerMate.staticField; + } + + public static synchronized int getInstanceField(FieldOwnerMate mate) { + return mate.instanceField; + } + + public static synchronized long getPrivateStaticField() { + return FieldOwnerMate.privateStaticField; + } + + public static synchronized long setPrivateStaticField(long x) { + return FieldOwnerMate.privateStaticField = x; + } + + public static synchronized int getPrivateInstanceField(FieldOwnerMate mate) { + return mate.privateInstanceField; + } + + public static synchronized int setPrivateInstanceField(FieldOwnerMate mate, int x) { + return mate.privateInstanceField = x; + } + + public static synchronized long setPrivateInstanceWideField(FieldOwnerMate mate, long x) { + return mate.privateInstanceWideField = x; + } + + public static synchronized long getPrivateStaticArrayFieldElement(int index) { + return FieldOwnerMate.privateStaticArrayField[index]; + } + + public static synchronized long setPrivateStaticArrayFieldElement(int index, long value) { + return FieldOwnerMate.privateStaticArrayField[index] = value; + } + + public static synchronized int getPrivateInstanceArrayFieldElement( + FieldOwnerMate mate, int index) { + return mate.privateInstanceArrayField[index]; + } + + public static synchronized int setPrivateInstanceArrayFieldElement( + FieldOwnerMate mate, int index, int value) { + return mate.privateInstanceArrayField[index] = value; + } + + public static synchronized long getPrivateStaticFieldReadOnly() { + return FieldOwnerMate.privateStaticFieldReadOnly; + } + + public static synchronized int getPrivateInstanceFieldReadOnly(FieldOwnerMate mate) { + return mate.privateInstanceFieldReadOnly; + } + + public static synchronized long getPrivateStaticFieldInBoundary() { + return FieldOwnerMate.getPrivateStaticFieldInBoundary(); + } + + public static synchronized int getPrivateInstanceFieldInBoundary(FieldOwnerMate mate) { + return mate.getPrivateInstanceFieldInBoundary(); + } + + public static synchronized long compoundSetPrivateStaticField(long x) { + return FieldOwnerMate.privateStaticField += x; + } + + public static synchronized long preIncrementPrivateStaticField() { + return ++FieldOwnerMate.privateStaticField; + } + + public static synchronized long postIncrementPrivateStaticField() { + return FieldOwnerMate.privateStaticField++; + } + + public static synchronized int compoundSetPrivateInstanceField(FieldOwnerMate mate, int x) { + return mate.privateInstanceField += x; + } + + public static synchronized int preIncrementPrivateInstanceField(FieldOwnerMate mate) { + return ++mate.privateInstanceField; + } + + public static synchronized int postIncrementPrivateInstanceField(FieldOwnerMate mate) { + return mate.privateInstanceField++; + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod/BUILD new file mode 100644 index 00000000000000..c842df06e07bf9 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod/BUILD @@ -0,0 +1,17 @@ +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "interfacemethod", + srcs = glob(["*.java"]), +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod/InterfaceNest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod/InterfaceNest.java new file mode 100644 index 00000000000000..b223d25e3d00ac --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod/InterfaceNest.java @@ -0,0 +1,129 @@ +// Copyright 2019 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.nest.testsrc.simpleunit.interfacemethod; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.LongStream; + +/** A nest for testing private interface methods desugaring. */ +public class InterfaceNest { + + /** A class that implements the interface mate. */ + public static class ConcreteMate implements InterfaceMate { + @Override + public List mateValues() { + return LongStream.range(0L, 100L).boxed().collect(Collectors.toList()); + } + } + /** A nest member that encloses private interface methods with cross-mate invocations. */ + interface SubInterfaceMateMate { + private static long privateStaticMethod(long x, int y) { + return x + y; + } + + private long privateInstanceMethod(long x, int y) { + return x + y; + } + } + + interface InterfaceMate { + + static long publicStaticMethod(long x, long y) { + return x + y; + } + + private static long privateStaticMethod(long x, int y) { + return x + y; + } + + private long privateInstanceMethod(long x, int y) { + return x + y; + } + + private static long privateStaticMethodWithLambdaEvaluation( + Function> hf, long a, long b) { + return hf.apply(a).apply(b); + } + + private static Function> privateStaticMethodWithLambdaGeneration( + long a, long b) { + return p -> (q -> a + b * q); + } + + private long privateInstanceMethodWithLambda(Collection factors, long a, long b) { + Long sum = factors.stream().map(v -> a + v).reduce(0L, Long::sum); + return mateValues().stream().mapToLong(v -> b + sum * v).sum(); + } + + List mateValues(); + + private static long inClassBoundaryStaticMethod(long x) { + return x == 0 ? 0 : x + inClassBoundaryStaticMethod(x - 1); + } + + private long inClassBoundaryInstanceMethod(long x) { + return x == 0 ? 0 : x + inClassBoundaryInstanceMethod(x - 1); + } + + static long invokeStaticMethodInClassBoundary(long x) { + return inClassBoundaryStaticMethod(x); + } + + default long invokeInstanceMethodInClassBoundary(long x) { + return inClassBoundaryInstanceMethod(x); + } + } + + public static long invokePublicStaticMethod(long x, int y) { + return InterfaceMate.publicStaticMethod(x, y); + } + + public static long invokePrivateStaticMethod(long x, int y) { + return InterfaceMate.privateStaticMethod(x, y); + } + + public static long invokePrivateInstanceMethod(InterfaceMate mate, long x, int y) { + return mate.privateInstanceMethod(x, y); + } + + public static long invokeSubMatePrivateStaticMethod(long x, int y) { + return SubInterfaceMateMate.privateStaticMethod(x, y); + } + + public static long invokeSubMatePrivateInstanceMethod(SubInterfaceMateMate mate, long x, int y) { + return mate.privateInstanceMethod(x, y); + } + + public static long invokePrivateStaticMethodWithLambda(long a0, long b0, long a1, long b1) { + return InterfaceMate.privateStaticMethodWithLambdaEvaluation( + InterfaceMate.privateStaticMethodWithLambdaGeneration(a0, b0), a1, b1); + } + + public static long invokePrivateInstanceMethodWithLambda( + InterfaceMate mate, Collection vals, long a, long b) { + return mate.privateInstanceMethodWithLambda(vals, a, b); + } + + public static long invokeInClassBoundaryStaticMethod(long x) { + return InterfaceMate.invokeStaticMethodInClassBoundary(x); + } + + public static long invokeInClassBoundaryInstanceMethod(InterfaceMate mate, long x) { + return mate.invokeInstanceMethodInClassBoundary(x); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/BUILD new file mode 100644 index 00000000000000..01a403bd29adb9 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/BUILD @@ -0,0 +1,17 @@ +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "method", + srcs = glob(["*.java"]), +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/MethodNest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/MethodNest.java new file mode 100644 index 00000000000000..e2e3c845379ee6 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/MethodNest.java @@ -0,0 +1,118 @@ +// Copyright 2019 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.nest.testsrc.simpleunit.method; + +/** A nest for testing private method desugaring. */ +@SuppressWarnings("MethodCanBeStatic") // Intentional for testing. +public class MethodNest { + + /** A nest member that encloses private methods with cross-mate invocations. */ + public static class MethodOwnerMate { + + static long staticMethod(long x, int y) { + return x + y; + } + + long instanceMethod(long x, int y) { + return x + y; + } + + private static long privateStaticMethod(long x, int y) { + return x + y; + } + + private long privateInstanceMethod(long x, int y) throws Exception { + return x + y; + } + + // No generation of bridge methods. + private long inClassBoundInstanceMethod(long x) { + return x == 0 ? 0 : x + inClassBoundInstanceMethod(x - 1); + } + + // No generation of bridge methods. + private static long inClassBoundStaticMethod(long x) { + return x == 0 ? 0 : x + inClassBoundStaticMethod(x - 1); + } + } + + /** A nest member that has access to cross-mate private methods through inheritance. */ + public static class SubMate extends MethodOwnerMate { + + public static long invokePrivateStaticMethod(long x, int y) { + return MethodOwnerMate.privateStaticMethod(x, y); + } + + public long superAccessPrivateInstanceMethod(long x, int y) throws Exception { + return 1 + super.privateInstanceMethod(x, y); + } + + public long castAccessPrivateInstanceMethod(long x, int y) throws Exception { + return 2 + ((MethodOwnerMate) this).privateInstanceMethod(x, y); + } + } + + public static long populatedFromInvokePrivateStaticMethod; + public long populatedFromInvokePrivateInstanceMethod; + + // For testing a static initializer block. + static { + long t = MethodOwnerMate.privateStaticMethod(128L, 256); + populatedFromInvokePrivateStaticMethod = 1 + t; + } + + // For testing a instance initializer block. + { + try { + populatedFromInvokePrivateInstanceMethod = + new MethodOwnerMate().privateInstanceMethod(256L, 512); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("StaticQualifiedUsingExpression") // Intentionally for testing. + public static long invokeStaticMethod(long x, int y) { + MethodOwnerMate methodOwnerMate = null; + return methodOwnerMate.staticMethod(x, y); + } + + public static long invokeInstanceMethod(MethodOwnerMate mateInstance, long x, int y) { + return mateInstance.instanceMethod(x, y); + } + + public static long invokePrivateStaticMethod(long x, int y) { + return MethodOwnerMate.privateStaticMethod(x, y); + } + + public static long invokePrivateInstanceMethod(MethodOwnerMate mateInstance, long x, int y) + throws Exception { + return mateInstance.privateInstanceMethod(x, y); + } + + public static long invokeSuperAccessPrivateInstanceMethod(SubMate subMate, long x, int y) + throws Exception { + return subMate.superAccessPrivateInstanceMethod(x, y); + } + + public static long invokeCastAccessPrivateInstanceMethod(SubMate subMate, long x, int y) + throws Exception { + return subMate.castAccessPrivateInstanceMethod(x, y); + } + + public static void main(String[] args) { + System.out.println("hello2"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/NonNest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/NonNest.java new file mode 100644 index 00000000000000..0195690cc3b68f --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/method/NonNest.java @@ -0,0 +1,39 @@ +/* + * 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.nest.testsrc.simpleunit.method; + +/** + * Source code for testing the desugaring of invokevirtual at call the site of private instance + * method within the same class. + */ +public final class NonNest { + + private final long base; + + private NonNest(long base) { + this.base = base; + } + + private long twoSum(long x, long y) { + return base + x + y; + } + + public static long invokeTwoSum(long base, long x, long y) { + return new NonNest(base) // Expected invokespecial + .twoSum(x, y); // Expected invokevirtual under javac 11. + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/scan/BUILD b/src/test/java/com/google/devtools/build/android/desugar/scan/BUILD new file mode 100644 index 00000000000000..d2203d8789a132 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/scan/BUILD @@ -0,0 +1,36 @@ +load("@rules_java//java:defs.bzl", "java_library") + +# Description: +# Tests for the Java 8 desugaring tool for Android. +package( + default_testonly = 1, +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["**"]), + visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__pkg__"], +) + +sh_test( + name = "test_keep_scanner", + srcs = ["test_keep_scanner.sh"], + args = [ + "$(location //src/tools/android/java/com/google/devtools/build/android/desugar/scan:KeepScanner)", + "$(location :testdata)", + "$(location testdata_golden.txt)", + ], + data = [ + "testdata_golden.txt", + ":testdata", + "//src/tools/android/java/com/google/devtools/build/android/desugar/scan:KeepScanner", + ], +) + +java_library( + name = "testdata", + srcs = glob(["testdata/**/*.java"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/scan/test_keep_scanner.sh b/src/test/java/com/google/devtools/build/android/desugar/scan/test_keep_scanner.sh new file mode 100755 index 00000000000000..d42859fe35c322 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/scan/test_keep_scanner.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Copyright 2018 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. +set -eux + +out=$(mktemp) +"$1" --input "$2" --keep_file "${out}" --prefix java/ + +if ! diff "$3" "${out}"; then + echo "Unexpected output" + cat "${out}" + rm "${out}" + exit 1 +fi +rm "${out}" diff --git a/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/CollectionReferences.java b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/CollectionReferences.java new file mode 100644 index 00000000000000..830364c06c2c84 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/CollectionReferences.java @@ -0,0 +1,64 @@ +// Copyright 2018 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.scan.testdata; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +/** Test data for {@code KeepScanner} with references to java.* */ +public class CollectionReferences { + + private final List dates; + + public CollectionReferences() { + dates = new ArrayList<>(7); + assert !(dates instanceof LinkedList); + } + + @SuppressWarnings("unchecked") + public void add(Date date) { + List l = (AbstractList) Collection.class.cast(dates); + l.add(date); + } + + public Date first() { + try { + return dates.get(0); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + public long min() { + long result = Long.MAX_VALUE; // compile-time constant, no ref + for (Date d : dates) { + if (d.getTime() < result) { + result = d.getTime(); + } + } + return result; + } + + public void expire(long before) { + dates.removeIf(d -> d.getTime() < before); + } + + static { + System.out.println("Hello!"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/OverlappingCollectionReferences.java b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/OverlappingCollectionReferences.java new file mode 100644 index 00000000000000..e743a0df7a34ff --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/OverlappingCollectionReferences.java @@ -0,0 +1,49 @@ +// Copyright 2018 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.scan.testdata; + +import java.util.ArrayList; +import java.util.Date; + +/** Supplements {@link CollectionReferences} with additional and overlapping references to java.* */ +public class OverlappingCollectionReferences { + + private final ArrayList dates; + + public OverlappingCollectionReferences() { + dates = new ArrayList<>(); + } + + public void add(Date date) { + dates.add(date); + } + + public Date first() { + try { + return dates.get(0); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + public Date max() { + long result = Long.MIN_VALUE; // compile-time constant, no ref + for (Date d : dates) { + if (d.getTime() > result) { + result = d.getTime(); + } + } + return new Date(result); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/scan/testdata_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata_golden.txt new file mode 100644 index 00000000000000..60825763f5b1a9 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata_golden.txt @@ -0,0 +1,62 @@ +-keep class java.io.PrintStream { + *** println(java.lang.String); +} +-keep class java.lang.AssertionError { + (); +} +-keep class java.lang.Class { + *** cast(java.lang.Object); + *** desiredAssertionStatus(); +} +-keep class java.lang.IndexOutOfBoundsException { +} +-keep class java.lang.Object { + (); +} +-keep class java.lang.String { +} +-keep class java.lang.System { + *** out; +} +-keep class java.lang.invoke.CallSite { +} +-keep class java.lang.invoke.LambdaMetafactory { + *** metafactory(java.lang.invoke.MethodHandles$Lookup, java.lang.String, java.lang.invoke.MethodType, java.lang.invoke.MethodType, java.lang.invoke.MethodHandle, java.lang.invoke.MethodType); +} +-keep class java.lang.invoke.MethodHandle { +} +-keep class java.lang.invoke.MethodHandles { +} +-keep class java.lang.invoke.MethodHandles$Lookup { +} +-keep class java.lang.invoke.MethodType { +} +-keep class java.util.AbstractList { +} +-keep class java.util.ArrayList { + (); + (int); + *** add(java.lang.Object); + *** get(int); + *** iterator(); +} +-keep class java.util.Collection { + *** removeIf(java.util.function.Predicate); +} +-keep class java.util.Date { + (long); + *** getTime(); +} +-keep class java.util.Iterator { + *** hasNext(); + *** next(); +} +-keep class java.util.LinkedList { +} +-keep class java.util.List { + *** add(java.lang.Object); + *** get(int); + *** iterator(); +} +-keep class java.util.function.Predicate { +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/stringconcat/BUILD b/src/test/java/com/google/devtools/build/android/desugar/stringconcat/BUILD new file mode 100644 index 00000000000000..6f1a9ea4c08bdc --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/stringconcat/BUILD @@ -0,0 +1,59 @@ +load("@rules_java//java:defs.bzl", "java_library", "java_test") + +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +java_test( + name = "IndyStringConcatDesugaringlTest", + size = "medium", + srcs = ["IndyStringConcatDesugaringlTest.java"], + data = [ + ":string_concat_cases_srcs", + "//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 :string_concat_cases_srcs)", + # Required by Desugar#verifyLambdaDumpDirectoryRegistered + "-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)", + "-Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT", + ], + test_class = "com.google.devtools.build.android.desugar.stringconcat.IndyStringConcatDesugaringlTest", + 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:jsr305", + "//third_party:jsr330_inject", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_library( + name = "string_concat_cases", + srcs = [":string_concat_cases_srcs"], + javacopts = [ + "-source 11", + "-target 11", + "-XDstringConcat=indyWithConstants", # Enable OpenJDK version of String concat. + ], +) + +filegroup( + name = "string_concat_cases_srcs", + srcs = ["StringConcatTestCases.java"], +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/stringconcat/IndyStringConcatDesugaringlTest.java b/src/test/java/com/google/devtools/build/android/desugar/stringconcat/IndyStringConcatDesugaringlTest.java new file mode 100644 index 00000000000000..530a114bd9e835 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/stringconcat/IndyStringConcatDesugaringlTest.java @@ -0,0 +1,320 @@ +/* + * Copyright 2021 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.stringconcat; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; + +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.FromParameterValueSource; +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.ParameterValueSource; +import com.google.devtools.build.android.desugar.testing.junit.RuntimeMethodHandle; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +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.MethodNode; + +/** + * Tests for accessing a series of private fields, constructors and methods from another class + * within a nest. + */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public final class IndyStringConcatDesugaringlTest { + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, MethodHandles.lookup()) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("-source 11", "-target 11", "-XDstringConcat=indyWithConstants") + .setWorkingJavaPackage("com.google.devtools.build.android.desugar.stringconcat") + .addCommandOptions("desugar_indy_string_concat", "true") + .build(); + + @Test + public void invokeDynamicInstr_presentBeforeDesugaring( + @AsmNode(className = "StringConcatTestCases", memberName = "simplePrefix", round = 0) + MethodNode simpleStrConcatBeforeDesugar) { + AbstractInsnNode[] instructions = simpleStrConcatBeforeDesugar.instructions.toArray(); + assertThat(Arrays.stream(instructions).map(AbstractInsnNode::getOpcode)) + .contains(Opcodes.INVOKEDYNAMIC); + } + + @Test + public void invokeDynamicInstr_absentAfterDesugaring( + @AsmNode(className = "StringConcatTestCases", memberName = "simplePrefix", round = 1) + MethodNode simpleStrConcatAfterDesugar) { + AbstractInsnNode[] instructions = simpleStrConcatAfterDesugar.instructions.toArray(); + assertThat(Arrays.stream(instructions).map(AbstractInsnNode::getOpcode)) + .doesNotContain(Opcodes.INVOKEDYNAMIC); + } + + @Test + @ParameterValueSource({"", "", ""}) + @ParameterValueSource({"", "b", "b"}) + @ParameterValueSource({"a", "", "a"}) + @ParameterValueSource({"a", "b", "ab"}) + @ParameterValueSource({"ab", "cd", "abcd"}) + public void twoConcat( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcat", + memberDescriptor = "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") + MethodHandle twoConcat, + @FromParameterValueSource String x, + @FromParameterValueSource String y, + @FromParameterValueSource String expectedResult) + throws Throwable { + String result = (String) twoConcat.invoke(x, y); + assertThat(result).isEqualTo(expectedResult); + } + + @Test + public void twoConcat_StringAndObject( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcat", + memberDescriptor = "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;") + MethodHandle twoConcat) + throws Throwable { + String result = (String) twoConcat.invoke("ab", (Object) "cd"); + assertThat(result).isEqualTo("T:abcd"); + } + + @Test + public void twoConcatWithConstants( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcatWithConstants", + memberDescriptor = "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") + MethodHandle twoConcat) + throws Throwable { + String result = (String) twoConcat.invoke("ab", "cd"); + assertThat(result).isEqualTo("abcd"); + } + + @Test + public void threeConcat( + @RuntimeMethodHandle(className = "StringConcatTestCases", memberName = "threeConcat") + MethodHandle threeConcat) + throws Throwable { + String result = (String) threeConcat.invoke("ab", "cd", "ef"); + assertThat(result).isEqualTo("abcdef"); + } + + @Test + public void twoConcatWithRecipe( + @RuntimeMethodHandle(className = "StringConcatTestCases", memberName = "twoConcatWithRecipe") + MethodHandle twoConcat) + throws Throwable { + String result = (String) twoConcat.invoke("ab", "cd"); + assertThat(result).isEqualTo("

ab
cd

"); + } + + @Test + public void threeConcatWithRecipe( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "threeConcatWithRecipe") + MethodHandle threeConcat) + throws Throwable { + String result = (String) threeConcat.invoke("ab", "cd", "ef"); + assertThat(result).isEqualTo("

ab
cd
ef

"); + } + + @Test + @ParameterValueSource({"a", "0", "a0"}) + @ParameterValueSource({"a", "1", "a1"}) + @ParameterValueSource({"b", "3", "b3"}) + @ParameterValueSource({"c", "7", "c7"}) + @ParameterValueSource({"a", "15", "a15"}) + @ParameterValueSource({"d", "2147483647", "d2147483647"}) // Integer.MAX_VALUE + @ParameterValueSource({"d", "-2147483648", "d-2147483648"}) // Integer.MIN_VALUE + public void twoConcatWithPrimitives_StringAndInt( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcatWithPrimitives", + memberDescriptor = "(Ljava/lang/String;I)Ljava/lang/String;") + MethodHandle twoConcat, + @FromParameterValueSource String x, + @FromParameterValueSource int y, + @FromParameterValueSource String expectedResult) + throws Throwable { + String result = (String) twoConcat.invoke(x, y); + assertThat(result).isEqualTo(expectedResult); + } + + @Test + @ParameterValueSource({"a", "1", "a1"}) + @ParameterValueSource({"b", "3", "b3"}) + @ParameterValueSource({"c", "7", "c7"}) + @ParameterValueSource({"a", "15", "a15"}) + @ParameterValueSource({"d", "9223372036854775807", "d9223372036854775807"}) // Long.MAX_VALUE + @ParameterValueSource({"e", "-9223372036854775808", "e-9223372036854775808"}) // Long.MIN_VALUE + public void twoConcatWithPrimitives_StringAndLong( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcatWithPrimitives", + memberDescriptor = "(Ljava/lang/String;J)Ljava/lang/String;") + MethodHandle twoConcat, + @FromParameterValueSource String x, + @FromParameterValueSource long y, + @FromParameterValueSource String expectedResult) + throws Throwable { + String result = (String) twoConcat.invoke(x, y); + assertThat(result).isEqualTo(expectedResult); + } + + @Test + @ParameterValueSource({"a", "123.125", "a123.125"}) + @ParameterValueSource({ + "max-double-", + "1.7976931348623157E+308", + "max-double-1.7976931348623157E308" + }) + @ParameterValueSource({ + "min-double-", + "2.2250738585072014E-308", + "min-double-2.2250738585072014E-308" + }) + public void twoConcatWithPrimitives_StringAndDouble( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcatWithPrimitives", + memberDescriptor = "(Ljava/lang/String;D)Ljava/lang/String;") + MethodHandle twoConcat, + @FromParameterValueSource String x, + @FromParameterValueSource double y, + @FromParameterValueSource String expectedResult) + throws Throwable { + String result = (String) twoConcat.invoke(x, y); + assertThat(result).isEqualTo(expectedResult); + } + + @Test + public void twoConcatWithPrimitives_intAndString( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcatWithPrimitives", + memberDescriptor = "(ILjava/lang/String;)Ljava/lang/String;") + MethodHandle twoConcat) + throws Throwable { + String result = (String) twoConcat.invoke(123, "ABC"); + assertThat(result).isEqualTo("123ABC"); + } + + @Test + public void twoConcatWithPrimitives_longAndString( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcatWithPrimitives", + memberDescriptor = "(JLjava/lang/String;)Ljava/lang/String;") + MethodHandle twoConcat) + throws Throwable { + String result = (String) twoConcat.invoke(123L, "ABC"); + assertThat(result).isEqualTo("123ABC"); + } + + @Test + public void twoConcatWithPrimitives_doubleAndString( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "twoConcatWithPrimitives", + memberDescriptor = "(DLjava/lang/String;)Ljava/lang/String;") + MethodHandle twoConcat) + throws Throwable { + String result = (String) twoConcat.invoke(123.125, "ABC"); + assertThat(result).isEqualTo("123.125ABC"); + } + + @Test + @ParameterValueSource({ + "head", // string value + "1", // int value + "true", // boolean value + "2", // byte value + "c", // char value + "4", // short value + "5.25", // double value + "6.5F", // float value + "7", // long value + "head/1/true/2/c/4/5.25/6.5/7" // expectedResult + }) + @ParameterValueSource({ + "min:", // string value + "-2147483648", // int value + "false", // boolean value + "-128", // byte value + "~", // char value + "-32768", // short value + "1.7976931348623157E+308", // double value + "1.4E-45F", // float value + "-9223372036854775808", // long value + "min:/-2147483648/false/-128/~/-32768/1.7976931348623157E308/1.4E-45/-9223372036854775808" + }) + @ParameterValueSource({ + "max:", // string value + "2147483647", // int value + "true", // boolean value + "127", // byte value + "~", // char value + "32767", // short value + "2.2250738585072014E-308", // double value + "3.4028235E+38F", // float value + "9223372036854775807", // long value + "max:/2147483647/true/127/~/32767/2.2250738585072014E-308/3.4028235E38/9223372036854775807" + }) + public void concatWithAllPrimitiveTypes( + @RuntimeMethodHandle( + className = "StringConcatTestCases", + memberName = "concatWithAllPrimitiveTypes") + MethodHandle concatWithAllPrimitiveTypes, + @FromParameterValueSource String stringValue, + @FromParameterValueSource int intValue, + @FromParameterValueSource boolean booleanValue, + @FromParameterValueSource byte byteValue, + @FromParameterValueSource char charValue, + @FromParameterValueSource short shortValue, + @FromParameterValueSource double doubleValue, + @FromParameterValueSource float floatValue, + @FromParameterValueSource long longValue, + @FromParameterValueSource String expectedResult) + throws Throwable { + String result = + (String) + concatWithAllPrimitiveTypes.invoke( + stringValue, + intValue, + booleanValue, + byteValue, + charValue, + shortValue, + doubleValue, + floatValue, + longValue); + assertThat(result).isEqualTo(expectedResult); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/stringconcat/StringConcatTestCases.java b/src/test/java/com/google/devtools/build/android/desugar/stringconcat/StringConcatTestCases.java new file mode 100644 index 00000000000000..20a1a2f73227b5 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/stringconcat/StringConcatTestCases.java @@ -0,0 +1,106 @@ +/* + * Copyright 2019 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.stringconcat; + +/** Test cases for string concatenations. */ +public final class StringConcatTestCases { + + private static final String TEXT_CONSTANT = ""; + + public static String simplePrefix(String content) { + return "prefix:" + content; + } + + public static String twoConcat(String x, String y) { + return x + y; + } + + public static String twoConcat(String x, T y) { + return "T:" + x + y; + } + + public static String threeConcat(String x, String y, String z) { + return x + y + z; + } + + public static String twoConcatWithConstants(String x, String y) { + return x + TEXT_CONSTANT + y; + } + + public static String twoConcatWithRecipe(String x, String y) { + return "

" + x + "
" + y + "

"; + } + + public static String twoConcatWithPrimitives(String x, int y) { + return x + y; + } + + public static String twoConcatWithPrimitives(String x, long y) { + return x + y; + } + + public static String twoConcatWithPrimitives(String x, double y) { + return x + y; + } + + public static String twoConcatWithPrimitives(int x, String y) { + return x + y; + } + + public static String twoConcatWithPrimitives(long x, String y) { + return x + y; + } + + public static String twoConcatWithPrimitives(double x, String y) { + return x + y; + } + + public static String threeConcatWithRecipe(String x, String y, String z) { + return "

" + x + "
" + y + "
" + z + "

"; + } + + public static String concatWithAllPrimitiveTypes( + String stringValue, + int intValue, + boolean booleanValue, + byte byteValue, + char charValue, + short shortValue, + double doubleValue, + float floatValue, + long longValue) { + return stringValue + + '/' + + intValue + + '/' + + booleanValue + + '/' + + byteValue + + '/' + + charValue + + '/' + + shortValue + + '/' + + doubleValue + + '/' + + floatValue + + '/' + + longValue; + } + + private StringConcatTestCases() {} +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testing/junit/BUILD b/src/test/java/com/google/devtools/build/android/desugar/testing/junit/BUILD new file mode 100644 index 00000000000000..9132da9cbaa00f --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testing/junit/BUILD @@ -0,0 +1,69 @@ +load("@rules_java//java:defs.bzl", "java_library", "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 = "DesugarRuleTest", + srcs = ["DesugarRuleTest.java"], + data = [ + ":desugar_rule_test_target", + ":test_source", + "//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_jar=$(location :desugar_rule_test_target)", + "-Dinput_srcs=$(location :test_source)", + # Required by Desugar#verifyLambdaDumpDirectoryRegistered + "-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)", + ], + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", + "//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:jsr305", + "//third_party:jsr330_inject", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_library( + name = "desugar_rule_test_target", + testonly = 1, + srcs = ["DesugarRuleTestTarget.java"], +) + +# DO NOT depend on this target, kept for build verification only. +java_library( + name = "test_source_lib", + srcs = [":test_source"], + javacopts = [ + "-source 11", + "-target 11", + ], + neverlink = 1, + visibility = ["//visibility:private"], +) + +filegroup( + name = "test_source", + srcs = ["DesugarRuleTestSourceTarget.java"], +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTest.java b/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTest.java new file mode 100644 index 00000000000000..572e1f5d3340b6 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTest.java @@ -0,0 +1,236 @@ +/* + * Copyright 2019 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.testing.junit; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; +import static org.junit.Assert.assertThrows; + +import com.google.devtools.build.android.desugar.io.JarItem; +import com.google.devtools.build.android.desugar.testing.junit.RuntimeMethodHandle.MemberUseContext; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.nio.file.Paths; +import java.util.Arrays; +import javax.inject.Inject; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +/** The test for {@link DesugarRule}. */ +@JdkSuppress(minJdkVersion = JdkVersion.V11) +@RunWith(DesugarRunner.class) +public final class DesugarRuleTest { + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, MethodHandles.lookup()) + .addInputs(Paths.get(System.getProperty("input_jar"))) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("--release 11") + .enableIterativeTransformation(3) + .setWorkingJavaPackage("com.google.devtools.build.android.desugar.testing.junit") + .build(); + + @Inject + @DynamicClassLiteral("DesugarRuleTestTarget$InterfaceSubjectToDesugar") + private Class interfaceSubjectToDesugarRound1; + + @Inject + @DynamicClassLiteral(value = "DesugarRuleTestTarget$InterfaceSubjectToDesugar", round = 2) + private Class interfaceSubjectToDesugarRound2; + + @Inject + @DynamicClassLiteral("DesugarRuleTestTarget$InterfaceSubjectToDesugar") + private Class interfaceSubjectToDesugarFromSimpleClassName; + + @Inject + @DynamicClassLiteral( + "com.google.devtools.build.android.desugar.testing.junit.DesugarRuleTestTarget$InterfaceSubjectToDesugar") + private Class interfaceSubjectToDesugarFromQualifiedClassName; + + @Inject + @DynamicClassLiteral("DesugarRuleTestTarget$InterfaceSubjectToDesugar$$CC") + private Class interfaceSubjectToDesugarCompanionClassRound1; + + @Inject + @DynamicClassLiteral(value = "DesugarRuleTestTarget$InterfaceSubjectToDesugar$$CC", round = 2) + private Class interfaceSubjectToDesugarCompanionClassRound2; + + @Inject + @AsmNode(className = "DesugarRuleTestTarget") + private ClassNode desugarRuleTestTargetClassNode; + + @Inject + @AsmNode(className = "DesugarRuleTestTarget$Alpha", memberName = "twoIntSum") + private MethodNode twoIntSum; + + @Inject + @AsmNode( + className = "DesugarRuleTestTarget$Alpha", + memberName = "multiplier", + memberDescriptor = "J") + private FieldNode multiplier; + + @Inject + @RuntimeMethodHandle(className = "DesugarRuleTestTarget$Alpha", memberName = "") + private MethodHandle alphaConstructor; + + @Inject + @RuntimeMethodHandle( + className = "DesugarRuleTestTarget$Alpha", + memberName = "linearLongTransform") + private MethodHandle linearLongTransform; + + @Inject + @RuntimeMethodHandle( + className = "DesugarRuleTestTarget$Alpha", + memberName = "multiplier", + usage = MemberUseContext.FIELD_GETTER) + private MethodHandle alphaMultiplierGetter; + + @Inject + @RuntimeMethodHandle( + className = "DesugarRuleTestTarget$Alpha", + memberName = "multiplier", + usage = MemberUseContext.FIELD_SETTER) + private MethodHandle alphaMultiplierSetter; + + @Test + public void staticMethodsAreMovedFromOriginatingClass() { + assertThrows( + NoSuchMethodException.class, + () -> interfaceSubjectToDesugarRound1.getDeclaredMethod("staticMethod")); + } + + @Test + public void staticMethodsAreMovedFromOriginatingClass_desugarTwice() { + assertThrows( + NoSuchMethodException.class, + () -> interfaceSubjectToDesugarRound2.getDeclaredMethod("staticMethod")); + } + + @Test + public void staticMethodsAreMovedToCompanionClass() { + assertThat( + Arrays.stream(interfaceSubjectToDesugarCompanionClassRound1.getDeclaredMethods()) + .map(Method::getName)) + .contains("staticMethod$$STATIC$$"); + } + + @Test + public void innerClasses() { + assertThat( + desugarRuleTestTargetClassNode.innerClasses.stream().map(classNode -> classNode.name)) + .contains( + "com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTestTarget$InterfaceSubjectToDesugar"); + } + + @Test + public void idempotencyOperation( + @RuntimeJarEntry( + value = "DesugarRuleTestTarget$InterfaceSubjectToDesugar$$CC.class", + round = 1) + JarItem interfaceSubjectToDesugarJarEntryRound1, + @RuntimeJarEntry( + value = "DesugarRuleTestTarget$InterfaceSubjectToDesugar$$CC.class", + round = 2) + JarItem interfaceSubjectToDesugarJarEntryRound2) { + assertThat(interfaceSubjectToDesugarJarEntryRound1.jarEntry().getCrc()) + .isEqualTo(interfaceSubjectToDesugarJarEntryRound2.jarEntry().getCrc()); + } + + @Test + public void classLoaders_sameInstanceInSameRound() { + assertThat(interfaceSubjectToDesugarRound1.getClassLoader()) + .isSameInstanceAs(interfaceSubjectToDesugarCompanionClassRound1.getClassLoader()); + assertThat(interfaceSubjectToDesugarRound2.getClassLoader()) + .isSameInstanceAs(interfaceSubjectToDesugarCompanionClassRound2.getClassLoader()); + } + + @Test + public void classLiterals_requestedWithFullQualifiedClassName() { + assertThat(interfaceSubjectToDesugarFromQualifiedClassName) + .isSameInstanceAs(interfaceSubjectToDesugarFromSimpleClassName); + } + + @Test + public void injectClassLiteralsFromParam( + @DynamicClassLiteral("DesugarRuleTestTarget$InterfaceSubjectToDesugar") + Class interfaceSubjectToDesugarParam) { + assertThat(interfaceSubjectToDesugarParam).isSameInstanceAs(interfaceSubjectToDesugarRound1); + } + + @Test + public void injectFieldNodes() { + assertThat(twoIntSum.desc).isEqualTo("(II)I"); + } + + @Test + public void injectMethodNodes() { + assertThat(multiplier.desc).isEqualTo("J"); + } + + @Test + @ParameterValueSource({"1", "2", "3"}) + @ParameterValueSource({"100", "400", "500"}) + public void invokeStaticMethodHandle( + @RuntimeMethodHandle(className = "DesugarRuleTestTarget$Alpha", memberName = "twoIntSum") + MethodHandle twoIntSum, + @FromParameterValueSource int x, + @FromParameterValueSource int y, + @FromParameterValueSource int expectedResult) + throws Throwable { + int result = (int) twoIntSum.invoke(x, y); + assertThat(result).isEqualTo(expectedResult); + } + + @Test + public void invokeVirtualMethodHandle() throws Throwable { + long result = (long) linearLongTransform.invoke(alphaConstructor.invoke(1000, 2), 3L); + assertThat(result).isEqualTo(3002); + } + + @Test + public void invokeFieldGetter() throws Throwable { + Object alpha = alphaConstructor.invoke(1000, 2); + long result = (long) alphaMultiplierGetter.invoke(alpha); + assertThat(result).isEqualTo(1000); + } + + @Test + public void invokeFieldSetter() throws Throwable { + Object alpha = alphaConstructor.invoke(1000, 2); + alphaMultiplierSetter.invoke(alpha, 1111); + + long result = (long) alphaMultiplierGetter.invoke(alpha); + assertThat(result).isEqualTo(1111); + } + + @Test + public void invokeRuntimeCompiledTarget( + @RuntimeMethodHandle(className = "DesugarRuleTestSourceTarget", memberName = "twoSum") + MethodHandle twoSum) + throws Throwable { + int result = (int) twoSum.invokeExact(1, 2); + assertThat(result).isEqualTo(3); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTestSourceTarget.java b/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTestSourceTarget.java new file mode 100644 index 00000000000000..33fe7347d10730 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTestSourceTarget.java @@ -0,0 +1,32 @@ +/* + * 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.testing.junit; + +/** + * A Java source file to be compiled during the execution of {@link + * com.google.devtools.build.android.desugar.testing.junit.DesugarRuleTest}. + * + *

Note: This class is used for checking the desugar pipeline and {@link + * com.google.devtools.build.android.desugar.testing.junit.DesugarRule} working as expected. DO NOT + * use this target for testing individual desugar logic. + */ +@SuppressWarnings("PrivateConstructorForUtilityClass") +public class DesugarRuleTestSourceTarget { + public static int twoSum(int x, int y) { + return x + y; + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTestTarget.java b/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTestTarget.java new file mode 100644 index 00000000000000..7c91499fd8c7b4 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testing/junit/DesugarRuleTestTarget.java @@ -0,0 +1,53 @@ +/* + * Copyright 2019 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.testing.junit; + +/** + * The source class used as data in {@link DesugarRuleTest}. + * + *

Note: This class is used for checking the desugar pipeline and {@link DesugarRule} working as + * expected. DO NOT use this target for testing individual desugar logic. + */ +@SuppressWarnings({"PrivateConstructorForUtilityClass", "InterfaceWithOnlyStatics"}) // testing-only +class DesugarRuleTestTarget { + interface InterfaceSubjectToDesugar { + static void staticMethod() {} + + default int defaultMethod(int x, int y) { + return x + y; + } + } + + public static class Alpha { + + public long multiplier; + public long offset; + + public Alpha(int multiplier, int offset) { + this.multiplier = multiplier; + this.offset = offset; + } + + public static int twoIntSum(int x, int y) { + return x + y; + } + + public long linearLongTransform(long x) { + return multiplier * x + offset; + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/typeannotation/AnnotationUser.java b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/AnnotationUser.java new file mode 100644 index 00000000000000..3adc41ad3355a0 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/AnnotationUser.java @@ -0,0 +1,63 @@ +/* + * 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.typeannotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +/** The test source class for testing annotation desugaring. */ +public class AnnotationUser { + + @SuppressWarnings("unused") // Test source + public void localVarWithTypeUseAnnotation(List inTextList) { + List<@EnhancedType String> localTextList = inTextList; + } + + public void instructionTypeUseAnnotation() { + new ArrayList<@EnhancedType String>(); + } + + public void tryCatchTypeAnnotation(RuntimeException inException) { + try { + throw inException; + } catch (@EnhancedType IllegalArgumentException e) { + throw new UnsupportedOperationException(e); + } + } + + // @EnhancedVar is expected to be absent in Javac-compiled bytecode, even through @EnhancedVar is + // declared with a runtime retention policy. This test case ensures any retained annotation use + // within a method body is a type annotation. + @SuppressWarnings("unused") // Test source + public Function localNonTypeAnnotations(String inputText) { + @EnhancedVar String localText = inputText; + try { + return (@EnhancedVar Integer x) -> 2 * x; + } catch (@EnhancedVar IllegalStateException e) { + throw new UnsupportedOperationException(e); + } + } + + @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + @interface EnhancedType {} + + @Retention(RetentionPolicy.RUNTIME) + @interface EnhancedVar {} +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/typeannotation/BUILD b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/BUILD new file mode 100644 index 00000000000000..c513585ad75fe5 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/BUILD @@ -0,0 +1,48 @@ +load("@rules_java//java:defs.bzl", "java_test") + +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +java_test( + name = "LocalTypeAnnotationUseTest", + size = "medium", + srcs = ["LocalTypeAnnotationUseTest.java"], + data = [ + ":annotation_test_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 :annotation_test_src)", + # Required by Desugar#verifyLambdaDumpDirectoryRegistered + "-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.typeannotation.LocalTypeAnnotationUseTest", + 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:jsr305", + "//third_party:jsr330_inject", + "//third_party:junit4", + "//third_party:truth", + ], +) + +filegroup( + name = "annotation_test_src", + srcs = ["AnnotationUser.java"], +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUseTest.java b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUseTest.java new file mode 100644 index 00000000000000..406cc31e4bfae1 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUseTest.java @@ -0,0 +1,97 @@ +/* + * 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.typeannotation; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.android.desugar.testing.junit.DesugarTestHelpers.filterInstructions; +import static org.objectweb.asm.tree.AbstractInsnNode.TYPE_INSN; + +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 java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.Objects; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.objectweb.asm.tree.MethodNode; + +/** Tests for desugaring annotations. */ +@RunWith(DesugarRunner.class) +@JdkSuppress(minJdkVersion = JdkVersion.V11) +public class LocalTypeAnnotationUseTest { + + @Rule + public final DesugarRule desugarRule = + DesugarRule.builder(this, MethodHandles.lookup()) + .addSourceInputsFromJvmFlag("input_srcs") + .addJavacOptions("--release 11") + .setWorkingJavaPackage("com.google.devtools.build.android.desugar.typeannotation") + .addCommandOptions("desugar_try_with_resources_if_needed", "true") + .build(); + + @Test + public void localNonTypeAnnotationsWithRuntimeRetention_discardedByJavac( + @AsmNode(className = "AnnotationUser", memberName = "localNonTypeAnnotations", round = 0) + MethodNode before) { + assertThat(before.visibleLocalVariableAnnotations).isNull(); + assertThat( + Arrays.stream(before.instructions.toArray()) + .map(insn -> insn.visibleTypeAnnotations) + .allMatch(Objects::isNull)) + .isTrue(); + } + + @Test + public void localVarWithTypeUseAnnotation( + @AsmNode( + className = "AnnotationUser", + memberName = "localVarWithTypeUseAnnotation", + round = 0) + MethodNode before, + @AsmNode( + className = "AnnotationUser", + memberName = "localVarWithTypeUseAnnotation", + round = 1) + MethodNode after) { + assertThat(before.visibleLocalVariableAnnotations).isNotEmpty(); + assertThat(after.visibleLocalVariableAnnotations).isNull(); + } + + @Test + public void instructionTypeUseAnnotation( + @AsmNode(className = "AnnotationUser", memberName = "instructionTypeUseAnnotation", round = 0) + MethodNode before, + @AsmNode(className = "AnnotationUser", memberName = "instructionTypeUseAnnotation", round = 1) + MethodNode after) { + assertThat(getOnlyElement(filterInstructions(before, TYPE_INSN)).visibleTypeAnnotations) + .isNotEmpty(); + assertThat(getOnlyElement(filterInstructions(after, TYPE_INSN)).visibleTypeAnnotations) + .isNull(); + } + + @Test + public void tryCatchTypeAnnotation( + @AsmNode(className = "AnnotationUser", memberName = "tryCatchTypeAnnotation", round = 0) + MethodNode before, + @AsmNode(className = "AnnotationUser", memberName = "tryCatchTypeAnnotation", round = 1) + MethodNode after) { + assertThat(getOnlyElement(before.tryCatchBlocks).visibleTypeAnnotations).isNotEmpty(); + assertThat(getOnlyElement(after.tryCatchBlocks).visibleTypeAnnotations).isNull(); + } +}