| // 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(); |
| } |
| } |