1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package art;
18 
19 import java.lang.ref.*;
20 import java.lang.reflect.*;
21 import java.lang.invoke.*;
22 import java.util.*;
23 
24 public class Test1976 {
25 
26   // The fact that the target is having methods added makes it annoying to test since we cannot
27   // initially call them. To work around this in a simple-ish way just use (non-structural)
28   // redefinition to change the implementation of the caller of Transform1976 after redefining the
29   // target.
30   public static final class RunTransformMethods implements Runnable {
run()31     public void run() {
32       System.out.println("Saying everything!");
33       Transform1976.sayEverything();
34       System.out.println("Saying hi!");
35       Transform1976.sayHi();
36     }
37   }
38 
39   /* Base64 encoded dex bytes of:
40    * public static final class RunTransformMethods implements Runnable {
41    *   public void run() {
42    *    System.out.println("Saying everything!");
43    *    Transform1976.sayEverything();
44    *    System.out.println("Saying hi!");
45    *    Transform1976.sayHi();
46    *    System.out.println("Saying bye!");
47    *    Transform1976.sayBye();
48    *   }
49    * }
50    */
51   public static final byte[] RUN_DEX_BYTES =
52       Base64.getDecoder()
53           .decode(
54               "ZGV4CjAzNQCv3eV8jFcpSsqMGl1ZXRk2iraZO41D0TIgBQAAcAAAAHhWNBIAAAAAAAAAAFwEAAAc"
55                   + "AAAAcAAAAAsAAADgAAAAAgAAAAwBAAABAAAAJAEAAAcAAAAsAQAAAQAAAGQBAACcAwAAhAEAAAYC"
56                   + "AAAOAgAAMgIAAEICAABXAgAAewIAAJsCAACyAgAAxgIAANwCAADwAgAABAMAABkDAAAmAwAAOgMA"
57                   + "AEYDAABVAwAAWAMAAFwDAABpAwAAbwMAAHQDAAB9AwAAggMAAIoDAACZAwAAoAMAAKcDAAABAAAA"
58                   + "AgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAAEAAAABAAAAAKAAAAAAAAABEAAAAK"
59                   + "AAAAAAIAAAkABQAUAAAAAAAAAAAAAAAAAAAAFgAAAAIAAAAXAAAAAgAAABgAAAACAAAAGQAAAAUA"
60                   + "AQAVAAAABgAAAAAAAAAAAAAAEQAAAAYAAAD4AQAADwAAAEwEAAAuBAAAAAAAAAEAAQABAAAA6gEA"
61                   + "AAQAAABwEAYAAAAOAAMAAQACAAAA7gEAAB8AAABiAAAAGgENAG4gBQAQAHEAAwAAAGIAAAAaAQ4A"
62                   + "biAFABAAcQAEAAAAYgAAABoBDABuIAUAEABxAAIAAAAOAAYADgAIAA54PHg8eDwAAQAAAAcAAAAB"
63                   + "AAAACAAGPGluaXQ+ACJMYXJ0L1Rlc3QxOTc2JFJ1blRyYW5zZm9ybU1ldGhvZHM7AA5MYXJ0L1Rl"
64                   + "c3QxOTc2OwATTGFydC9UcmFuc2Zvcm0xOTc2OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu"
65                   + "Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9QcmludFN0"
66                   + "cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xh"
67                   + "bmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABNSdW5UcmFuc2Zvcm1NZXRob2RzAAtTYXlp"
68                   + "bmcgYnllIQASU2F5aW5nIGV2ZXJ5dGhpbmchAApTYXlpbmcgaGkhAA1UZXN0MTk3Ni5qYXZhAAFW"
69                   + "AAJWTAALYWNjZXNzRmxhZ3MABG5hbWUAA291dAAHcHJpbnRsbgADcnVuAAZzYXlCeWUADXNheUV2"
70                   + "ZXJ5dGhpbmcABXNheUhpAAV2YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwi"
71                   + "bWluLWFwaSI6MSwic2hhLTEiOiJhODM1MmYyNTQ4ODUzNjJjY2Q4ZDkwOWQzNTI5YzYwMDk0ZGQ4"
72                   + "OTZlIiwidmVyc2lvbiI6IjEuNi4yMC1kZXYifQACAwEaGAECBAISBBkTFwsAAAEBAIGABIQDAQGc"
73                   + "AwAAAAACAAAAHwQAACUEAABABAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAA"
74                   + "cAAAAAIAAAALAAAA4AAAAAMAAAACAAAADAEAAAQAAAABAAAAJAEAAAUAAAAHAAAALAEAAAYAAAAB"
75                   + "AAAAZAEAAAEgAAACAAAAhAEAAAMgAAACAAAA6gEAAAEQAAACAAAA+AEAAAIgAAAcAAAABgIAAAQg"
76                   + "AAACAAAAHwQAAAAgAAABAAAALgQAAAMQAAACAAAAPAQAAAYgAAABAAAATAQAAAAQAAABAAAAXAQA"
77                   + "AA==");
78 
run()79   public static void run() throws Exception {
80     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
81     doTest();
82   }
83 
84   private static final boolean PRINT_ID_NUM = false;
85 
printRun(long id, Method m)86   public static void printRun(long id, Method m) {
87     if (PRINT_ID_NUM) {
88       System.out.println("Running method " + id + " " + m + " using JNI.");
89     } else {
90       System.out.println("Running method " + m + " using JNI.");
91     }
92   }
93 
94   public static final class MethodHandleWrapper {
95     private MethodHandle mh;
96     private Method m;
MethodHandleWrapper(MethodHandle mh, Method m)97     public MethodHandleWrapper(MethodHandle mh, Method m) {
98       this.m = m;
99       this.mh = mh;
100     }
getHandle()101     public MethodHandle getHandle() {
102       return mh;
103     }
getMethod()104     public Method getMethod() {
105       return m;
106     }
invoke()107     public Object invoke() throws Throwable {
108       return mh.invoke();
109     }
toString()110     public String toString() {
111       return mh.toString();
112     }
113   }
114 
getMethodHandles(Method[] methods)115   public static MethodHandleWrapper[] getMethodHandles(Method[] methods) throws Exception {
116     final MethodHandles.Lookup l = MethodHandles.lookup();
117     ArrayList<MethodHandleWrapper> res = new ArrayList<>();
118     for (Method m : methods) {
119       if (!Modifier.isStatic(m.getModifiers())) {
120         continue;
121       }
122       res.add(new MethodHandleWrapper(l.unreflect(m), m));
123     }
124     return res.toArray(new MethodHandleWrapper[0]);
125   }
126 
runMethodHandles(MethodHandleWrapper[] handles)127   public static void runMethodHandles(MethodHandleWrapper[] handles) throws Exception {
128     for (MethodHandleWrapper h : handles) {
129       try {
130         System.out.println("Invoking " + h + " (" + h.getMethod() + ")");
131         h.invoke();
132       } catch (Throwable t) {
133         if (t instanceof Exception) {
134           throw (Exception)t;
135         } else if (t instanceof Error) {
136           throw (Error)t;
137         } else {
138           throw new RuntimeException("Unexpected throwable thrown!", t);
139         }
140       }
141     }
142   }
143 
doTest()144   public static void doTest() throws Exception {
145     Runnable r = new RunTransformMethods();
146     System.out.println("Running directly");
147     r.run();
148     System.out.println("Running reflective");
149     Method[] methods = Transform1976.class.getDeclaredMethods();
150     for (Method m : methods) {
151       if (Modifier.isStatic(m.getModifiers())) {
152         System.out.println("Reflectively invoking " + m);
153         m.invoke(null);
154       } else {
155         System.out.println("Not invoking non-static method " + m);
156       }
157     }
158     System.out.println("Running jni");
159     long[] mids = getMethodIds(methods);
160     callNativeMethods(Transform1976.class, mids);
161     MethodHandleWrapper[] handles = getMethodHandles(methods);
162     System.out.println("Running method handles");
163     runMethodHandles(handles);
164     Redefinition.doCommonStructuralClassRedefinition(
165         Transform1976.class, Transform1976.REDEFINED_DEX_BYTES);
166     // Change RunTransformMethods to also call the 'runBye' method. No RI support so no classfile
167     // bytes required.
168     Redefinition.doCommonClassRedefinition(RunTransformMethods.class, new byte[] {}, RUN_DEX_BYTES);
169     System.out.println("Running directly after redef");
170     r.run();
171     System.out.println("Running reflective after redef using old j.l.r.Method");
172     for (Method m : methods) {
173       if (Modifier.isStatic(m.getModifiers())) {
174         System.out.println("Reflectively invoking " + m + " on old j.l.r.Method");
175         m.invoke(null);
176       } else {
177         System.out.println("Not invoking non-static method " + m);
178       }
179     }
180     System.out.println("Running reflective after redef using new j.l.r.Method");
181     for (Method m : Transform1976.class.getDeclaredMethods()) {
182       if (Modifier.isStatic(m.getModifiers())) {
183         System.out.println("Reflectively invoking " + m + " on new j.l.r.Method");
184         m.invoke(null);
185       } else {
186         System.out.println("Not invoking non-static method " + m);
187       }
188     }
189     System.out.println("Running jni with old ids");
190     callNativeMethods(Transform1976.class, mids);
191     System.out.println("Running jni with new ids");
192     callNativeMethods(Transform1976.class, getMethodIds(Transform1976.class.getDeclaredMethods()));
193 
194     System.out.println("Running method handles using old handles");
195     runMethodHandles(handles);
196     System.out.println("Running method handles using new handles");
197     runMethodHandles(getMethodHandles(Transform1976.class.getDeclaredMethods()));
198   }
199 
getMethodIds(Method[] m)200   public static native long[] getMethodIds(Method[] m);
201 
callNativeMethods(Class<?> k, long[] smethods)202   public static native void callNativeMethods(Class<?> k, long[] smethods);
203 }
204