blob: f6c3c99a30b14c7f8bbb3b0ddf6e72f2df0db867 [file] [log] [blame]
// 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();
}
}