1cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light/* 2cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * Copyright (C) 2018 The Android Open Source Project 3cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * 4cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * Licensed under the Apache License, Version 2.0 (the "License"); 5cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * you may not use this file except in compliance with the License. 6cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * You may obtain a copy of the License at 7cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * 8cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * http://www.apache.org/licenses/LICENSE-2.0 9cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * 10cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * Unless required by applicable law or agreed to in writing, software 11cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * distributed under the License is distributed on an "AS IS" BASIS, 12cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * See the License for the specific language governing permissions and 14cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light * limitations under the License. 15cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light */ 16cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 17cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightpackage art.constmethodhandle; 18cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 19cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport java.io.*; 20cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport java.util.*; 21cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport java.lang.invoke.CallSite; 22cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport java.lang.invoke.MethodHandle; 23cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport java.lang.invoke.MethodHandles; 24cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport java.lang.invoke.MethodType; 25cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport java.nio.file.*; 26cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport org.objectweb.asm.ClassReader; 27cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport org.objectweb.asm.ClassVisitor; 28cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport org.objectweb.asm.ClassWriter; 29cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport org.objectweb.asm.Handle; 30cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport org.objectweb.asm.MethodVisitor; 31cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport org.objectweb.asm.Opcodes; 32cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightimport org.objectweb.asm.Type; 33cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 34cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light// This test will modify in place the compiled java files to fill in the transformed version and 35cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light// fill in the TestInvoker.runTest function with a load-constant of a method-handle. It will use d8 36cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light// (passed in as an argument) to create the dex we will transform TestInvoke into. 37cc917d9f0495402f5301aa9df0071ad38ab78142Alex Lightpublic class TestGenerator { 38cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 39cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light public static void main(String[] args) throws IOException { 40cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light if (args.length != 2) { 41cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light throw new Error("Unable to convert class to dex without d8 binary!"); 42cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 43cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Path base = Paths.get(args[0]); 44cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light String d8Bin = args[1]; 45cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 46cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Path initTestInvoke = base.resolve(TestGenerator.class.getPackage().getName().replace('.', '/')) 47cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light .resolve(TestInvoke.class.getSimpleName() + ".class"); 48cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light byte[] initClass = new FileInputStream(initTestInvoke.toFile()).readAllBytes(); 49cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 50cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // Make the initial version of TestInvoker 51cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light generateInvoker(initClass, "sayHi", new FileOutputStream(initTestInvoke.toFile())); 52cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 53cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // Make the final 'class' version of testInvoker 54cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light ByteArrayOutputStream finalClass = new ByteArrayOutputStream(); 55cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light generateInvoker(initClass, "sayBye", finalClass); 56cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 57cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Path initTest1948 = base.resolve("art").resolve(art.Test1948.class.getSimpleName() + ".class"); 58cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light byte[] finalClassBytes = finalClass.toByteArray(); 59cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light byte[] finalDexBytes = getFinalDexBytes(d8Bin, finalClassBytes); 60cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light generateTestCode( 61cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light new FileInputStream(initTest1948.toFile()).readAllBytes(), 62cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light finalClassBytes, 63cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light finalDexBytes, 64cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light new FileOutputStream(initTest1948.toFile())); 65cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 66cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 67cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // Modify the Test1948 class bytecode so it has the transformed version of TestInvoker as a string 68cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // constant. 69cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light private static void generateTestCode( 70cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light byte[] initClass, byte[] transClass, byte[] transDex, OutputStream out) throws IOException { 71cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light ClassReader cr = new ClassReader(initClass); 72cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 73cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light cr.accept( 74cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light new ClassVisitor(Opcodes.ASM6, cw) { 75cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light @Override 76cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light public void visitEnd() { 77cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light generateStringAccessorMethod( 78cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light cw, "getDexBase64", Base64.getEncoder().encodeToString(transDex)); 79cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light generateStringAccessorMethod( 80cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light cw, "getClassBase64", Base64.getEncoder().encodeToString(transClass)); 81cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light super.visitEnd(); 82cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 83cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light }, 0); 84cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light out.write(cw.toByteArray()); 85cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 86cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 87cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // Insert a string accessor method so we can get the transformed versions of TestInvoker. 88cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light private static void generateStringAccessorMethod(ClassVisitor cv, String name, String ret) { 89cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light MethodVisitor mv = cv.visitMethod( 90cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, 91cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light name, "()Ljava/lang/String;", null, null); 92cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitLdcInsn(ret); 93cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitInsn(Opcodes.ARETURN); 94cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitMaxs(-1, -1); 95cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 96cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 97cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // Use d8bin to convert the classBytes into a dex file bytes. We need to do this here because we 98cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // need the dex-file bytes to be used by the test class to redefine TestInvoker. We use d8 because 99cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // it doesn't require setting up a directory structures or matching file names like dx does. 100cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // TODO We should maybe just call d8 functions directly? 101cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light private static byte[] getFinalDexBytes(String d8Bin, byte[] classBytes) throws IOException { 102cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Path tempDir = Files.createTempDirectory("FinalTestInvoker_Gen"); 103cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light File tempInput = Files.createTempFile(tempDir, "temp_input_class", ".class").toFile(); 104cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 105cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light OutputStream tempClassStream = new FileOutputStream(tempInput); 106cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light tempClassStream.write(classBytes); 107cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light tempClassStream.close(); 108cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light tempClassStream = null; 109cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 110cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Process d8Proc = new ProcessBuilder(d8Bin, 111cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // Put classes.dex in the temp-dir we made. 112cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light "--output", tempDir.toAbsolutePath().toString(), 113cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light "--min-api", "28", // Allow the new invoke ops. 114cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light "--no-desugaring", // Don't try to be clever please. 115cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light tempInput.toPath().toAbsolutePath().toString()) 116cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light .inheritIO() // Just print to stdio. 117cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light .start(); 118cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light int res; 119cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light try { 120cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light res = d8Proc.waitFor(); 121cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } catch (Exception e) { 122cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light System.out.println("Failed to dex: ".concat(e.toString())); 123cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light e.printStackTrace(); 124cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light res = -123; 125cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 126cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light tempInput.delete(); 127cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light try { 128cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light if (res == 0) { 129cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light byte[] out = new FileInputStream(tempDir.resolve("classes.dex").toFile()).readAllBytes(); 130cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light tempDir.resolve("classes.dex").toFile().delete(); 131cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light return out; 132cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 133cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } finally { 134cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light tempDir.toFile().delete(); 135cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 136cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light throw new Error("Failed to get dex file! " + res); 137cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 138cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 139cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light private static void generateInvoker( 140cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light byte[] inputClass, 141cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light String toCall, 142cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light OutputStream output) throws IOException { 143cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light ClassReader cr = new ClassReader(inputClass); 144cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 145cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light cr.accept( 146cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light new ClassVisitor(Opcodes.ASM6, cw) { 147cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light @Override 148cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light public void visitEnd() { 149cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light generateRunTest(cw, toCall); 150cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light super.visitEnd(); 151cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 152cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light }, 0); 153cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light output.write(cw.toByteArray()); 154cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 155cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light 156cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // Creates the following method: 157cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // public runTest(Runnable preCall) { 158cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // preCall.run(); 159cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // MethodHandle mh = <CONSTANT MH>; 160cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // mh.invokeExact(); 161cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light // } 162cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light private static void generateRunTest(ClassVisitor cv, String toCall) { 163cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, 164cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light "runTest", "(Ljava/lang/Runnable;)V", null, null); 165cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light MethodType mt = MethodType.methodType(Void.TYPE); 166cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Handle mh = new Handle( 167cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Opcodes.H_INVOKESTATIC, 168cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Type.getInternalName(Responses.class), 169cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light toCall, 170cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mt.toMethodDescriptorString(), 171cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light false); 172cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light String internalName = Type.getInternalName(Runnable.class); 173cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitVarInsn(Opcodes.ALOAD, 1); 174cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, internalName, "run", "()V", true); 175cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitLdcInsn(mh); 176cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitMethodInsn( 177cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Opcodes.INVOKEVIRTUAL, 178cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light Type.getInternalName(MethodHandle.class), 179cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light "invokeExact", 180cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light "()V", 181cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light false); 182cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitInsn(Opcodes.RETURN); 183cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light mv.visitMaxs(-1, -1); 184cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light } 185cc917d9f0495402f5301aa9df0071ad38ab78142Alex Light} 186