blob: f400169aefc78944cc63a9bc058c7e4d18a6f170 [file] [log] [blame]
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
// The method MethodHandleDump.transform() translates methods in MethodHandleTest that look like
// MethodType.methodType(TYPES)
// and
// MethodHandles.lookup().findKIND(FOO.class, "METHOD", TYPE)
// into LDC instructions.
// This is necessary since there is no Java syntax that compiles to
// LDC of a constant method handle or constant method type.
//
// The method dumpD() dumps a class equivalent to MethodHandleTest.D
// that uses an LDC instruction instead of MethodHandles.lookup().findSpecial().
// The LDC instruction loads an InvokeSpecial constant method handle to a C method,
// so this LDC instruction must be in a subclass of C, and not directly on MethodHandleTest.
public class MethodHandleDump implements Opcodes {
private static final String cDesc = "com/android/tools/r8/cf/MethodHandleTest$C";
private static final String eDesc = "com/android/tools/r8/cf/MethodHandleTest$E";
private static final String fDesc = "com/android/tools/r8/cf/MethodHandleTest$F";
private static final String iDesc = "com/android/tools/r8/cf/MethodHandleTest$I";
private static final Type viType = Type.getMethodType(Type.VOID_TYPE, Type.INT_TYPE);
private static final Type jiType = Type.getMethodType(Type.LONG_TYPE, Type.INT_TYPE);
private static final Type vicType =
Type.getMethodType(Type.VOID_TYPE, Type.INT_TYPE, Type.CHAR_TYPE);
private static final Type jicType =
Type.getMethodType(Type.LONG_TYPE, Type.INT_TYPE, Type.CHAR_TYPE);
private static final Type veType = Type.getMethodType(Type.VOID_TYPE, Type.getObjectType(eDesc));
private static final Type fType = Type.getMethodType(Type.getObjectType(fDesc));
private static final String viDesc = viType.getDescriptor();
private static final String jiDesc = jiType.getDescriptor();
private static final String vicDesc = vicType.getDescriptor();
private static final String jicDesc = jicType.getDescriptor();
private static final String intDesc = Type.INT_TYPE.getDescriptor();
public static byte[] transform(byte[] input) throws Exception {
ImmutableMap.Builder<String, Type> typesBuilder = ImmutableMap.builder();
ImmutableMap<String, Type> types =
typesBuilder
.put("viType", viType)
.put("jiType", jiType)
.put("vicType", vicType)
.put("jicType", jicType)
.put("veType", veType)
.put("fType", fType)
.build();
Builder<String, Handle> methodsBuilder = ImmutableMap.builder();
methodsBuilder
.put("scviMethod", new Handle(H_INVOKESTATIC, cDesc, "svi", viDesc, false))
.put("scjiMethod", new Handle(H_INVOKESTATIC, cDesc, "sji", jiDesc, false))
.put("scvicMethod", new Handle(H_INVOKESTATIC, cDesc, "svic", vicDesc, false))
.put("scjicMethod", new Handle(H_INVOKESTATIC, cDesc, "sjic", jicDesc, false))
.put("vcviMethod", new Handle(H_INVOKEVIRTUAL, cDesc, "vvi", viDesc, false))
.put("vcjiMethod", new Handle(H_INVOKEVIRTUAL, cDesc, "vji", jiDesc, false))
.put("vcvicMethod", new Handle(H_INVOKEVIRTUAL, cDesc, "vvic", vicDesc, false))
.put("vcjicMethod", new Handle(H_INVOKEVIRTUAL, cDesc, "vjic", jicDesc, false))
.put("siviMethod", new Handle(H_INVOKESTATIC, iDesc, "svi", viDesc, true))
.put("sijiMethod", new Handle(H_INVOKESTATIC, iDesc, "sji", jiDesc, true))
.put("sivicMethod", new Handle(H_INVOKESTATIC, iDesc, "svic", vicDesc, true))
.put("sijicMethod", new Handle(H_INVOKESTATIC, iDesc, "sjic", jicDesc, true))
.put("diviMethod", new Handle(H_INVOKEINTERFACE, iDesc, "dvi", viDesc, true))
.put("dijiMethod", new Handle(H_INVOKEINTERFACE, iDesc, "dji", jiDesc, true))
.put("divicMethod", new Handle(H_INVOKEINTERFACE, iDesc, "dvic", vicDesc, true))
.put("dijicMethod", new Handle(H_INVOKEINTERFACE, iDesc, "djic", jicDesc, true))
.put("vciSetField", new Handle(H_PUTFIELD, cDesc, "vi", intDesc, false))
.put("sciSetField", new Handle(H_PUTSTATIC, cDesc, "si", intDesc, false))
.put("vciGetField", new Handle(H_GETFIELD, cDesc, "vi", intDesc, false))
.put("sciGetField", new Handle(H_GETSTATIC, cDesc, "si", intDesc, false))
.put("iiSetField", new Handle(H_PUTSTATIC, iDesc, "ii", intDesc, true))
.put("iiGetField", new Handle(H_GETSTATIC, iDesc, "ii", intDesc, true))
.put("constructorMethod", new Handle(H_NEWINVOKESPECIAL, cDesc, "<init>", viDesc, false));
ImmutableMap<String, Handle> methods = methodsBuilder.build();
ClassReader cr = new ClassReader(input);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cr.accept(
new ClassVisitor(InternalOptions.ASM_VERSION, cw) {
@Override
public MethodVisitor visitMethod(
int access, String name, String desc, String signature, String[] exceptions) {
switch (desc) {
case "()Ljava/lang/invoke/MethodType;":
{
Type type = types.get(name);
assert type != null : name;
assert access == ACC_PUBLIC + ACC_STATIC;
assert signature == null;
assert exceptions == null;
MethodVisitor mv = cw.visitMethod(access, name, desc, null, null);
mv.visitCode();
mv.visitLdcInsn(type);
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
return null;
}
case "()Ljava/lang/invoke/MethodHandle;":
{
Handle method = methods.get(name);
assert access == ACC_PUBLIC + ACC_STATIC;
assert method != null : name;
assert signature == null;
assert exceptions == null;
MethodVisitor mv = cw.visitMethod(access, name, desc, null, null);
mv.visitCode();
mv.visitLdcInsn(method);
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
return null;
}
default:
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
},
0);
return cw.toByteArray();
}
public static byte[] dumpD() throws Exception {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
cw.visit(
V1_8,
ACC_PUBLIC + ACC_SUPER,
"com/android/tools/r8/cf/MethodHandleTest$D",
null,
"com/android/tools/r8/cf/MethodHandleTest$C",
null);
cw.visitInnerClass(
"com/android/tools/r8/cf/MethodHandleTest$D",
"com/android/tools/r8/cf/MethodHandleTest",
"D",
ACC_PUBLIC + ACC_STATIC);
cw.visitInnerClass(
"com/android/tools/r8/cf/MethodHandleTest$C",
"com/android/tools/r8/cf/MethodHandleTest",
"C",
ACC_PUBLIC + ACC_STATIC);
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, "com/android/tools/r8/cf/MethodHandleTest$C", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv =
cw.visitMethod(
ACC_PUBLIC + ACC_STATIC,
"vcviSpecialMethod",
"()Ljava/lang/invoke/MethodHandle;",
null,
null);
mv.visitCode();
mv.visitLdcInsn(new Handle(H_INVOKESPECIAL, cDesc, "vvi", viDesc, false));
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "vvi", "(I)V", null, null);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 2);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
}