diff options
author | Googler <noreply@google.com> | 2017-11-28 05:54:07 -0800 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2017-11-28 05:56:02 -0800 |
commit | af10f2a83bdda8406a8c0a012db6d38e19d022b0 (patch) | |
tree | d384623998a0096577b5c747d3bc9a97e135efcc /src/test/java/com/google | |
parent | 683bdc7a069a8328676740798d2fb6abc2119a2f (diff) |
Let to push lambda arguments not only with *LOAD instructions but with SIPUSH and *CONST_*.
RELNOTES: None
PiperOrigin-RevId: 177149410
Diffstat (limited to 'src/test/java/com/google')
3 files changed, 417 insertions, 5 deletions
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 a079ef80c5..1e33dd6996 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/BUILD +++ b/src/test/java/com/google/devtools/build/android/desugar/BUILD @@ -61,14 +61,28 @@ java_test( ], ) +# Test for b/62060793. Verifies constant lambda arguments that were pushed using *CONST_0 +# instructions. +java_test( + name = "DesugarFunctionalTestForConstantArgumentsInLambdas", + size = "small", + srcs = ["DesugarLambdaTest.java"], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarLambdaTest", + deps = [ + ":desugar_lambda_with_constant_arguments_lib", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + # Test for b/62456849. If a synthetic method is named "lambda$XXX", but not used in invokedynamic, # then Desugar should keep it in the class, rather than renaming it. java_test( name = "DesugarFunctionalTestForSyntheticMethodsWithLambdaNames", size = "small", - srcs = [ - "DesugarFunctionalTest.java", - ], + srcs = ["DesugarFunctionalTest.java"], tags = ["no_windows"], test_class = "com.google.devtools.build.android.desugar.DesugarFunctionalTest", deps = [ @@ -792,6 +806,39 @@ genrule( tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], ) +# Generates special lambda invocation so we can test for b/62060793. +genrule( + name = "generate_lambda_with_constant_arguments_in_test_data", + outs = ["testdata_generate_lambda_with_constant_arguments.jar"], + cmd = "$(location :generate_lambda_with_constant_arguments) $@", + tags = ["no_windows"], + tools = [":generate_lambda_with_constant_arguments"], +) + +# Desugar the test data for b/62456849 +genrule( + name = "desugar_lambda_with_constant_arguments", + srcs = [ + ":generate_lambda_with_constant_arguments_in_test_data", + # Depend on Jacoco runtime in case testdata was built with coverage instrumentation + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugar_generate_lambda_with_constant_arguments.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "-i $(location :generate_lambda_with_constant_arguments_in_test_data) -o $@ " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +java_import( + name = "desugar_lambda_with_constant_arguments_lib", + jars = [":desugar_lambda_with_constant_arguments"], + tags = ["no_windows"], +) + # Convert human-written methods whose names start with "lambda$XXX" to synthetic methods, so we can # test for b/62456849. genrule( @@ -812,8 +859,7 @@ genrule( "//third_party:guava-jars", ":separate", ":generate_synthetic_methods_with_lambda_names_in_test_data", - # Depend on Jacoco runtime in case testdata was built with coverage - # instrumentation + # Depend on Jacoco runtime in case testdata was built with coverage instrumentation "//third_party/java/jacoco:blaze-agent", "//tools/defaults:android_jar", ], @@ -1864,6 +1910,16 @@ genrule( ) java_binary( + name = "generate_lambda_with_constant_arguments", + srcs = ["Bug62060793TestDataGenerator.java"], + main_class = "com.google.devtools.build.android.desugar.Bug62060793TestDataGenerator", + deps = [ + "//third_party:asm", + "//third_party:guava", + ], +) + +java_binary( name = "generate_synthetic_method_with_lambda_name_convention", srcs = ["Bug62456849TestDataGenerator.java"], main_class = "com.google.devtools.build.android.desugar.Bug62456849TestDataGenerator", diff --git a/src/test/java/com/google/devtools/build/android/desugar/Bug62060793TestDataGenerator.java b/src/test/java/com/google/devtools/build/android/desugar/Bug62060793TestDataGenerator.java new file mode 100644 index 0000000000..f6c3c99a30 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/Bug62060793TestDataGenerator.java @@ -0,0 +1,318 @@ +// Copyright 2017 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; + +import static com.google.common.base.Preconditions.checkArgument; +import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; +import static org.objectweb.asm.Opcodes.ACC_FINAL; +import static org.objectweb.asm.Opcodes.ACC_INTERFACE; +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ACC_SUPER; +import static org.objectweb.asm.Opcodes.ACONST_NULL; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ARETURN; +import static org.objectweb.asm.Opcodes.ASTORE; +import static org.objectweb.asm.Opcodes.BIPUSH; +import static org.objectweb.asm.Opcodes.DCONST_0; +import static org.objectweb.asm.Opcodes.DLOAD; +import static org.objectweb.asm.Opcodes.DUP_X1; +import static org.objectweb.asm.Opcodes.FCONST_0; +import static org.objectweb.asm.Opcodes.FLOAD; +import static org.objectweb.asm.Opcodes.GOTO; +import static org.objectweb.asm.Opcodes.IADD; +import static org.objectweb.asm.Opcodes.ICONST_0; +import static org.objectweb.asm.Opcodes.ICONST_1; +import static org.objectweb.asm.Opcodes.ICONST_2; +import static org.objectweb.asm.Opcodes.ICONST_4; +import static org.objectweb.asm.Opcodes.IFNONNULL; +import static org.objectweb.asm.Opcodes.ILOAD; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.ISTORE; +import static org.objectweb.asm.Opcodes.LCONST_0; +import static org.objectweb.asm.Opcodes.LLOAD; +import static org.objectweb.asm.Opcodes.NEW; +import static org.objectweb.asm.Opcodes.RETURN; +import static org.objectweb.asm.Opcodes.SIPUSH; +import static org.objectweb.asm.Opcodes.SWAP; +import static org.objectweb.asm.Opcodes.V1_8; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * Test data generator for b/62060793. This class creates a special labmda invocation that + * contains *CONST_0 values on stack, which are passed as lambda arguments. + */ +public class Bug62060793TestDataGenerator { + + private static final String CLASS_NAME = + "com/google/devtools/build/android/desugar/testdata/ConstantArgumentsInLambda"; + + private static final String INTERFACE_TYPE_NAME = CLASS_NAME + "$Interface"; + + public static void main(String[] args) throws IOException { + checkArgument( + args.length == 1, + "Usage: %s <output-jar>", + Bug62060793TestDataGenerator.class.getName()); + Path outputJar = Paths.get(args[0]); + + try (ZipOutputStream outZip = + new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(outputJar)))) { + String className = CLASS_NAME + ".class"; + writeToZipFile(outZip, className, createClass()); + String interfaceName = INTERFACE_TYPE_NAME + ".class"; + writeToZipFile(outZip, interfaceName, createInterface()); + } + } + + private static void writeToZipFile(ZipOutputStream outZip, String entryName, byte[] content) + throws IOException { + ZipEntry result = new ZipEntry(entryName); + result.setTime(0L); + outZip.putNextEntry(result); + outZip.write(content); + outZip.closeEntry(); + } + + private static byte[] createClass() { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + MethodVisitor mv; + cw.visit( + V1_8, ACC_PUBLIC | ACC_SUPER, + CLASS_NAME, + null, "java/lang/Object", null); + + cw.visitInnerClass( + INTERFACE_TYPE_NAME, + CLASS_NAME, + "Interface", + ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); + + cw.visitInnerClass( + "java/lang/invoke/MethodHandles$Lookup", + "java/lang/invoke/MethodHandles", + "Lookup", + ACC_PUBLIC | ACC_FINAL | ACC_STATIC); + + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + mv.visitInsn(RETURN); + mv.visitEnd(); + } + { + mv = cw.visitMethod( + ACC_PRIVATE | ACC_STATIC, + "method", + "(Ljava/lang/String;)Ljava/lang/String;", + null, + null); + mv.visitParameter("str", 0); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ARETURN); + mv.visitEnd(); + } + { + mv = cw.visitMethod( + ACC_PRIVATE | ACC_STATIC, + "method", + "(ZCBFDJISLjava/lang/Object;[Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String;", + null, + null); + mv.visitParameter("bool", 0); + mv.visitParameter("c", 0); + mv.visitParameter("b", 0); + mv.visitParameter("f", 0); + mv.visitParameter("d", 0); + mv.visitParameter("l", 0); + mv.visitParameter("i", 0); + mv.visitParameter("s", 0); + mv.visitParameter("o", 0); + mv.visitParameter("array", 0); + mv.visitParameter("str", 0); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 10); + mv.visitMethodInsn( + INVOKESTATIC, + "java/lang/String", + "valueOf", + "(Ljava/lang/Object;)Ljava/lang/String;", false); + mv.visitVarInsn(ASTORE, 13); + mv.visitVarInsn(ALOAD, 11); + Label l0 = new Label(); + mv.visitJumpInsn(IFNONNULL, l0); + mv.visitInsn(ICONST_1); + Label l1 = new Label(); + mv.visitJumpInsn(GOTO, l1); + mv.visitLabel(l0); + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {"java/lang/String"}, 0, null); + mv.visitInsn(ICONST_0); + mv.visitLabel(l1); + mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {Opcodes.INTEGER}); + mv.visitVarInsn(ISTORE, 14); + mv.visitIntInsn(BIPUSH, 91); + mv.visitVarInsn(ALOAD, 12); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", + "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", + "length", "()I", false); + mv.visitInsn(IADD); + mv.visitVarInsn(ALOAD, 13); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", + "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", + "length", "()I", false); + mv.visitInsn(IADD); + mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); + mv.visitInsn(DUP_X1); + mv.visitInsn(SWAP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", + "<init>", "(I)V", false); + mv.visitVarInsn(ALOAD, 12); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(ILOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(Z)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(ILOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(C)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(ILOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(I)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(FLOAD, 3); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(F)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(DLOAD, 4); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(D)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(LLOAD, 6); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(J)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(ILOAD, 8); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(I)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(ILOAD, 9); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(I)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(ALOAD, 13); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); + mv.visitVarInsn(ILOAD, 14); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", "(Z)Ljava/lang/StringBuilder;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", + "toString", "()Ljava/lang/String;", false); + mv.visitInsn(ARETURN); + mv.visitEnd(); + } + { + mv = cw.visitMethod( + ACC_PUBLIC | ACC_STATIC, + "lambdaWithConstantArguments", + "()L" + INTERFACE_TYPE_NAME + ";", + null, null); + mv.visitCode(); + mv.visitInsn(ICONST_0); + mv.visitInsn(ICONST_1); + mv.visitInsn(ICONST_2); + mv.visitInsn(FCONST_0); + mv.visitInsn(DCONST_0); + mv.visitInsn(LCONST_0); + mv.visitInsn(ICONST_4); + mv.visitIntInsn(SIPUSH, 9); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ACONST_NULL); + mv.visitInvokeDynamicInsn( + "call", + "(ZCBFDJISLjava/lang/Object;[Ljava/lang/Object;)L" + INTERFACE_TYPE_NAME + ";", + new Handle( + Opcodes.H_INVOKESTATIC, + "java/lang/invoke/LambdaMetafactory", + "metafactory", + "(Ljava/lang/invoke/MethodHandles$Lookup;" + + "Ljava/lang/String;Ljava/lang/invoke/MethodType;" + + "Ljava/lang/invoke/MethodType;" + + "Ljava/lang/invoke/MethodHandle;" + + "Ljava/lang/invoke/MethodType;" + + ")Ljava/lang/invoke/CallSite;", + false), + new Object[] { + Type.getType("(Ljava/lang/String;)Ljava/lang/String;"), + new Handle( + Opcodes.H_INVOKESTATIC, + CLASS_NAME, + "method", + "(ZCBFDJISLjava/lang/Object;[Ljava/lang/Object;Ljava/lang/String;" + + ")Ljava/lang/String;", + false), + Type.getType("(Ljava/lang/String;)Ljava/lang/String;")}); + mv.visitInsn(ARETURN); + mv.visitEnd(); + } + + cw.visitEnd(); + + return cw.toByteArray(); + } + + private static byte[] createInterface() { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, + INTERFACE_TYPE_NAME, + null, "java/lang/Object", null); + + cw.visitInnerClass( + INTERFACE_TYPE_NAME, + CLASS_NAME, + "Interface", + ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); + + { + mv = cw.visitMethod( + ACC_PUBLIC | ACC_ABSTRACT, + "call", + "(Ljava/lang/String;)Ljava/lang/String;", + null, + null); + mv.visitParameter("input", 0); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarLambdaTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarLambdaTest.java new file mode 100644 index 0000000000..7c3d7eeeb3 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarLambdaTest.java @@ -0,0 +1,38 @@ +// Copyright 2016 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; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.devtools.build.android.desugar.testdata.ConstantArgumentsInLambda; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests uncommon lambda scenarious. + */ +@RunWith(JUnit4.class) +public class DesugarLambdaTest { + + /** + * Test for b/62060793. Verifies constant lambda arguments that were pushed using *CONST_0 + * instructions. + */ + @Test + public void testCallLambdaWithConstants() throws Exception { + assertThat(ConstantArgumentsInLambda.lambdaWithConstantArguments().call("test")) + .isEqualTo("testfalse\00120.00.0049nulltrue"); + } +} |