| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * 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 art; |
| |
| import java.lang.ref.*; |
| import java.lang.reflect.*; |
| import java.lang.invoke.*; |
| import java.util.*; |
| |
| public class Test1976 { |
| |
| // The fact that the target is having methods added makes it annoying to test since we cannot |
| // initially call them. To work around this in a simple-ish way just use (non-structural) |
| // redefinition to change the implementation of the caller of Transform1976 after redefining the |
| // target. |
| public static final class RunTransformMethods implements Runnable { |
| public void run() { |
| System.out.println("Saying everything!"); |
| Transform1976.sayEverything(); |
| System.out.println("Saying hi!"); |
| Transform1976.sayHi(); |
| } |
| } |
| |
| /* Base64 encoded dex bytes of: |
| * public static final class RunTransformMethods implements Runnable { |
| * public void run() { |
| * System.out.println("Saying everything!"); |
| * Transform1976.sayEverything(); |
| * System.out.println("Saying hi!"); |
| * Transform1976.sayHi(); |
| * System.out.println("Saying bye!"); |
| * Transform1976.sayBye(); |
| * } |
| * } |
| */ |
| public static final byte[] RUN_DEX_BYTES = |
| Base64.getDecoder() |
| .decode( |
| "ZGV4CjAzNQCv3eV8jFcpSsqMGl1ZXRk2iraZO41D0TIgBQAAcAAAAHhWNBIAAAAAAAAAAFwEAAAc" |
| + "AAAAcAAAAAsAAADgAAAAAgAAAAwBAAABAAAAJAEAAAcAAAAsAQAAAQAAAGQBAACcAwAAhAEAAAYC" |
| + "AAAOAgAAMgIAAEICAABXAgAAewIAAJsCAACyAgAAxgIAANwCAADwAgAABAMAABkDAAAmAwAAOgMA" |
| + "AEYDAABVAwAAWAMAAFwDAABpAwAAbwMAAHQDAAB9AwAAggMAAIoDAACZAwAAoAMAAKcDAAABAAAA" |
| + "AgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAAEAAAABAAAAAKAAAAAAAAABEAAAAK" |
| + "AAAAAAIAAAkABQAUAAAAAAAAAAAAAAAAAAAAFgAAAAIAAAAXAAAAAgAAABgAAAACAAAAGQAAAAUA" |
| + "AQAVAAAABgAAAAAAAAAAAAAAEQAAAAYAAAD4AQAADwAAAEwEAAAuBAAAAAAAAAEAAQABAAAA6gEA" |
| + "AAQAAABwEAYAAAAOAAMAAQACAAAA7gEAAB8AAABiAAAAGgENAG4gBQAQAHEAAwAAAGIAAAAaAQ4A" |
| + "biAFABAAcQAEAAAAYgAAABoBDABuIAUAEABxAAIAAAAOAAYADgAIAA54PHg8eDwAAQAAAAcAAAAB" |
| + "AAAACAAGPGluaXQ+ACJMYXJ0L1Rlc3QxOTc2JFJ1blRyYW5zZm9ybU1ldGhvZHM7AA5MYXJ0L1Rl" |
| + "c3QxOTc2OwATTGFydC9UcmFuc2Zvcm0xOTc2OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu" |
| + "Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9QcmludFN0" |
| + "cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xh" |
| + "bmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABNSdW5UcmFuc2Zvcm1NZXRob2RzAAtTYXlp" |
| + "bmcgYnllIQASU2F5aW5nIGV2ZXJ5dGhpbmchAApTYXlpbmcgaGkhAA1UZXN0MTk3Ni5qYXZhAAFW" |
| + "AAJWTAALYWNjZXNzRmxhZ3MABG5hbWUAA291dAAHcHJpbnRsbgADcnVuAAZzYXlCeWUADXNheUV2" |
| + "ZXJ5dGhpbmcABXNheUhpAAV2YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwi" |
| + "bWluLWFwaSI6MSwic2hhLTEiOiJhODM1MmYyNTQ4ODUzNjJjY2Q4ZDkwOWQzNTI5YzYwMDk0ZGQ4" |
| + "OTZlIiwidmVyc2lvbiI6IjEuNi4yMC1kZXYifQACAwEaGAECBAISBBkTFwsAAAEBAIGABIQDAQGc" |
| + "AwAAAAACAAAAHwQAACUEAABABAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAA" |
| + "cAAAAAIAAAALAAAA4AAAAAMAAAACAAAADAEAAAQAAAABAAAAJAEAAAUAAAAHAAAALAEAAAYAAAAB" |
| + "AAAAZAEAAAEgAAACAAAAhAEAAAMgAAACAAAA6gEAAAEQAAACAAAA+AEAAAIgAAAcAAAABgIAAAQg" |
| + "AAACAAAAHwQAAAAgAAABAAAALgQAAAMQAAACAAAAPAQAAAYgAAABAAAATAQAAAAQAAABAAAAXAQA" |
| + "AA=="); |
| |
| public static void run() throws Exception { |
| Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); |
| doTest(); |
| } |
| |
| private static final boolean PRINT_ID_NUM = false; |
| |
| public static void printRun(long id, Method m) { |
| if (PRINT_ID_NUM) { |
| System.out.println("Running method " + id + " " + m + " using JNI."); |
| } else { |
| System.out.println("Running method " + m + " using JNI."); |
| } |
| } |
| |
| public static final class MethodHandleWrapper { |
| private MethodHandle mh; |
| private Method m; |
| public MethodHandleWrapper(MethodHandle mh, Method m) { |
| this.m = m; |
| this.mh = mh; |
| } |
| public MethodHandle getHandle() { |
| return mh; |
| } |
| public Method getMethod() { |
| return m; |
| } |
| public Object invoke() throws Throwable { |
| return mh.invoke(); |
| } |
| public String toString() { |
| return mh.toString(); |
| } |
| } |
| |
| public static MethodHandleWrapper[] getMethodHandles(Method[] methods) throws Exception { |
| final MethodHandles.Lookup l = MethodHandles.lookup(); |
| ArrayList<MethodHandleWrapper> res = new ArrayList<>(); |
| for (Method m : methods) { |
| if (!Modifier.isStatic(m.getModifiers())) { |
| continue; |
| } |
| res.add(new MethodHandleWrapper(l.unreflect(m), m)); |
| } |
| return res.toArray(new MethodHandleWrapper[0]); |
| } |
| |
| public static void runMethodHandles(MethodHandleWrapper[] handles) throws Exception { |
| for (MethodHandleWrapper h : handles) { |
| try { |
| System.out.println("Invoking " + h + " (" + h.getMethod() + ")"); |
| h.invoke(); |
| } catch (Throwable t) { |
| if (t instanceof Exception) { |
| throw (Exception)t; |
| } else if (t instanceof Error) { |
| throw (Error)t; |
| } else { |
| throw new RuntimeException("Unexpected throwable thrown!", t); |
| } |
| } |
| } |
| } |
| |
| public static void doTest() throws Exception { |
| Runnable r = new RunTransformMethods(); |
| System.out.println("Running directly"); |
| r.run(); |
| System.out.println("Running reflective"); |
| Method[] methods = Transform1976.class.getDeclaredMethods(); |
| for (Method m : methods) { |
| if (Modifier.isStatic(m.getModifiers())) { |
| System.out.println("Reflectively invoking " + m); |
| m.invoke(null); |
| } else { |
| System.out.println("Not invoking non-static method " + m); |
| } |
| } |
| System.out.println("Running jni"); |
| long[] mids = getMethodIds(methods); |
| callNativeMethods(Transform1976.class, mids); |
| MethodHandleWrapper[] handles = getMethodHandles(methods); |
| System.out.println("Running method handles"); |
| runMethodHandles(handles); |
| Redefinition.doCommonStructuralClassRedefinition( |
| Transform1976.class, Transform1976.REDEFINED_DEX_BYTES); |
| // Change RunTransformMethods to also call the 'runBye' method. No RI support so no classfile |
| // bytes required. |
| Redefinition.doCommonClassRedefinition(RunTransformMethods.class, new byte[] {}, RUN_DEX_BYTES); |
| System.out.println("Running directly after redef"); |
| r.run(); |
| System.out.println("Running reflective after redef using old j.l.r.Method"); |
| for (Method m : methods) { |
| if (Modifier.isStatic(m.getModifiers())) { |
| System.out.println("Reflectively invoking " + m + " on old j.l.r.Method"); |
| m.invoke(null); |
| } else { |
| System.out.println("Not invoking non-static method " + m); |
| } |
| } |
| System.out.println("Running reflective after redef using new j.l.r.Method"); |
| for (Method m : Transform1976.class.getDeclaredMethods()) { |
| if (Modifier.isStatic(m.getModifiers())) { |
| System.out.println("Reflectively invoking " + m + " on new j.l.r.Method"); |
| m.invoke(null); |
| } else { |
| System.out.println("Not invoking non-static method " + m); |
| } |
| } |
| System.out.println("Running jni with old ids"); |
| callNativeMethods(Transform1976.class, mids); |
| System.out.println("Running jni with new ids"); |
| callNativeMethods(Transform1976.class, getMethodIds(Transform1976.class.getDeclaredMethods())); |
| |
| System.out.println("Running method handles using old handles"); |
| runMethodHandles(handles); |
| System.out.println("Running method handles using new handles"); |
| runMethodHandles(getMethodHandles(Transform1976.class.getDeclaredMethods())); |
| } |
| |
| public static native long[] getMethodIds(Method[] m); |
| |
| public static native void callNativeMethods(Class<?> k, long[] smethods); |
| } |