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.invoke.*;
20 import java.lang.ref.*;
21 import java.lang.reflect.*;
22 import java.util.*;
23 
24 public class Test1981 {
25   // Allow us to hide the var-handle portions when running this on CTS.
26   public interface VarHandler {
doVarHandleTests()27     public boolean doVarHandleTests();
28 
findStaticVarHandle(MethodHandles.Lookup l, Class c, String n, Class t)29     public default Object findStaticVarHandle(MethodHandles.Lookup l, Class c, String n, Class t)
30         throws Throwable {
31       return null;
32     }
33 
get(Object vh)34     public default Object get(Object vh) throws Throwable {
35       throw new Error("Illegal call!");
36     }
37 
set(Object vh, Object v)38     public default void set(Object vh, Object v) throws Throwable {
39       throw new Error("Illegal call!");
40     }
instanceofVarHandle(Object v)41     public default boolean instanceofVarHandle(Object v) {
42       return false;
43     }
getVarTypeName(Object v)44     public default Object getVarTypeName(Object v) {
45       throw new Error("Illegal call!");
46     }
47   }
48 
49   // CTS Entrypoint.
run()50   public static void run() throws Exception {
51     run(() -> false);
52   }
53 
run(VarHandler varhandle_portion)54   public static void run(VarHandler varhandle_portion) throws Exception {
55     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
56     doTest(varhandle_portion);
57   }
58 
59   private static final boolean PRINT_NONDETERMINISTIC = false;
60 
61   public static WeakHashMap<Object, Long> id_nums = new WeakHashMap<>();
62   public static long next_id = 0;
63 
printGeneric(VarHandler vh, Object o)64   public static String printGeneric(VarHandler vh, Object o) {
65     Long id = id_nums.get(o);
66     if (id == null) {
67       id = Long.valueOf(next_id++);
68       id_nums.put(o, id);
69     }
70     if (o == null) {
71       return "(ID: " + id + ") <NULL>";
72     }
73     Class oc = o.getClass();
74     if (oc.isArray() && oc.getComponentType() == Byte.TYPE) {
75       return "(ID: "
76           + id
77           + ") "
78           + Arrays.toString(Arrays.copyOf((byte[]) o, 10)).replace(']', ',')
79           + " ...]";
80     } else if (vh.instanceofVarHandle(o)) {
81       // These don't have a good to-string. Give them one.
82       return "(ID: " + id + ") " + o.getClass().getName() + "()->" + vh.getVarTypeName(o);
83     } else {
84       return "(ID: " + id + ") " + o.toString();
85     }
86   }
87 
88   public static class Transform {
89     static {
90     }
91 
92     private static Object BAR =
93         new Object() {
94           public String toString() {
95             return "value of <" + this.get() + ">";
96           }
97 
98           public Object get() {
99             return "BAR FIELD";
100           }
101         };
102     private static Object FOO =
103         new Object() {
104           public String toString() {
105             return "value of <" + this.get() + ">";
106           }
107 
108           public Object get() {
109             return "FOO FIELD";
110           }
111         };
112 
getLookup()113     public static MethodHandles.Lookup getLookup() {
114       return MethodHandles.lookup();
115     }
116 
staticToString()117     public static String staticToString() {
118       return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + "]";
119     }
120   }
121 
122   /* Base64 encoded class of:
123    * public static class Transform {
124    *   static {}
125    *   // NB This is the order the fields will be laid out in memory.
126    *   private static Object BAR;
127    *   private static Object BAZ;
128    *   private static Object FOO;
129    *   public static MethodHandles.Lookup getLookup() { return null; }
130    *   private static void reinitialize() {
131    *     BAZ = 42;
132    *   }
133    *   public static String staticToString() {
134    *    return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + ", BAZ: " + BAZ + "]";
135    *   }
136    * }
137    */
138   private static byte[] REDEFINED_DEX_BYTES =
139       Base64.getDecoder()
140           .decode(
141               "ZGV4CjAzNQDY+Vd3k8SVBE6A35RavIBzYN76h51YIqVwBgAAcAAAAHhWNBIAAAAAAAAAAKwFAAAk"
142                   + "AAAAcAAAAAwAAAAAAQAABgAAADABAAADAAAAeAEAAAwAAACQAQAAAQAAAPABAABgBAAAEAIAABoD"
143                   + "AAAjAwAALAMAADYDAAA+AwAAQwMAAEgDAABNAwAAUAMAAFMDAABXAwAAWwMAAHUDAACFAwAAqQMA"
144                   + "AMkDAADcAwAA8QMAAAUEAAAZBAAANAQAAF0EAABsBAAAdwQAAHoEAACCBAAAhQQAAJIEAACaBAAA"
145                   + "pQQAAKsEAAC5BAAAyQQAANMEAADaBAAA4wQAAAcAAAALAAAADAAAAA0AAAAOAAAADwAAABAAAAAR"
146                   + "AAAAEgAAABMAAAAUAAAAFwAAAAkAAAAGAAAABAMAAAgAAAAIAAAAAAAAAAoAAAAJAAAADAMAAAoA"
147                   + "AAAJAAAAFAMAAAgAAAAKAAAAAAAAABcAAAALAAAAAAAAAAEABwAEAAAAAQAHAAUAAAABAAcABgAA"
148                   + "AAEABQACAAAAAQAFAAMAAAABAAQAHAAAAAEABQAeAAAAAQABAB8AAAAFAAEAIAAAAAYAAAAiAAAA"
149                   + "BwAFAAMAAAAJAAUAAwAAAAkAAgAbAAAACQADABsAAAAJAAEAIAAAAAEAAAABAAAABwAAAAAAAAAV"
150                   + "AAAAnAUAAGoFAAAAAAAABQAAAAIAAAD/AgAANgAAABwAAQBuEAUAAAAMAGIBAgBiAgAAYgMBACIE"
151                   + "CQBwEAgABABuIAoABAAaABgAbiAKAAQAbiAJABQAGgAAAG4gCgAEAG4gCQAkABoAAQBuIAoABABu"
152                   + "IAkANAAaABkAbiAKAAQAbhALAAQADAARAAEAAAAAAAAA9gIAAAIAAAASABEAAAAAAAAAAADuAgAA"
153                   + "AQAAAA4AAAABAAEAAQAAAPICAAAEAAAAcBAHAAAADgABAAAAAQAAAPoCAAAJAAAAEwAqAHEQBgAA"
154                   + "AAwAaQABAA4ACgAOAAkADgAPAA4AEQAOhwAUAA4AAAEAAAAAAAAAAQAAAAcAAAABAAAACAAHLCBC"
155                   + "QVI6IAAHLCBCQVo6IAAIPGNsaW5pdD4ABjxpbml0PgADQkFSAANCQVoAA0ZPTwABSQABTAACTEkA"
156                   + "AkxMABhMYXJ0L1Rlc3QxOTgxJFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5ODE7ACJMZGFsdmlrL2Fu"
157                   + "bm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJDbGFzczsA"
158                   + "EUxqYXZhL2xhbmcvQ2xhc3M7ABNMamF2YS9sYW5nL0ludGVnZXI7ABJMamF2YS9sYW5nL09iamVj"
159                   + "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwAnTGphdmEv"
160                   + "bGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlcyRMb29rdXA7AA1UZXN0MTk4MS5qYXZhAAlUcmFuc2Zv"
161                   + "cm0AAVYABltGT086IAABXQALYWNjZXNzRmxhZ3MABmFwcGVuZAAJZ2V0TG9va3VwAARuYW1lAAxy"
162                   + "ZWluaXRpYWxpemUADnN0YXRpY1RvU3RyaW5nAAh0b1N0cmluZwAFdmFsdWUAB3ZhbHVlT2YAdn5+"
163                   + "RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiYTgzNTJm"
164                   + "MjU0ODg1MzYyY2NkOGQ5MDlkMzUyOWM2MDA5NGRkODk2ZSIsInZlcnNpb24iOiIxLjYuMjAtZGV2"
165                   + "In0AAgMBIRgCAgQCGgQJHRcWAwAFAAAKAQoBCgCIgASgBQGBgAS0BQEJjAUBCswFAQmQBAAAAAAC"
166                   + "AAAAWwUAAGEFAACQBQAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAkAAAAcAAAAAIA"
167                   + "AAAMAAAAAAEAAAMAAAAGAAAAMAEAAAQAAAADAAAAeAEAAAUAAAAMAAAAkAEAAAYAAAABAAAA8AEA"
168                   + "AAEgAAAFAAAAEAIAAAMgAAAFAAAA7gIAAAEQAAADAAAABAMAAAIgAAAkAAAAGgMAAAQgAAACAAAA"
169                   + "WwUAAAAgAAABAAAAagUAAAMQAAACAAAAjAUAAAYgAAABAAAAnAUAAAAQAAABAAAArAUAAA==");
170 
doTest(VarHandler vh)171   public static void doTest(VarHandler vh) throws Exception {
172     try {
173       System.out.println("Initial: " + Transform.staticToString());
174       MethodHandles.Lookup lookup = Transform.getLookup();
175       String[] names =
176           new String[] {
177             "FOO", "BAR",
178           };
179       MethodHandle[] handles =
180           new MethodHandle[] {
181             lookup.findStaticGetter(Transform.class, "FOO", Object.class),
182             lookup.findStaticGetter(Transform.class, "BAR", Object.class),
183           };
184       Object foo_handle = vh.findStaticVarHandle(lookup, Transform.class, "FOO", Object.class);
185       Object[] var_handles =
186           new Object[] {
187             foo_handle, vh.findStaticVarHandle(lookup, Transform.class, "BAR", Object.class),
188           };
189 
190       for (int i = 0; i < names.length; i++) {
191         System.out.println(
192             "Reading field "
193                 + names[i]
194                 + " using "
195                 + printGeneric(vh, handles[i])
196                 + " = "
197                 + printGeneric(vh, handles[i].invoke()));
198         if (vh.doVarHandleTests()) {
199           System.out.println(
200               "Reading field "
201                   + names[i]
202                   + " using "
203                   + printGeneric(vh, var_handles[i])
204                   + " = "
205                   + printGeneric(vh, vh.get(var_handles[i])));
206         }
207       }
208       MethodHandle old_field_write = lookup.findStaticSetter(Transform.class, "FOO", Object.class);
209 
210       System.out.println("Redefining Transform class");
211       Redefinition.doCommonStructuralClassRedefinition(Transform.class, REDEFINED_DEX_BYTES);
212       System.out.println("Post redefinition : " + Transform.staticToString());
213 
214       String[] new_names =
215           new String[] {
216             "BAZ", "FOO", "BAR",
217           };
218       MethodHandle[] new_handles =
219           new MethodHandle[] {
220             lookup.findStaticGetter(Transform.class, "BAZ", Object.class),
221             lookup.findStaticGetter(Transform.class, "FOO", Object.class),
222             lookup.findStaticGetter(Transform.class, "BAR", Object.class),
223           };
224       Object baz_handle = vh.findStaticVarHandle(lookup, Transform.class, "BAZ", Object.class);
225       Object[] new_var_handles =
226           new Object[] {
227             baz_handle,
228             vh.findStaticVarHandle(lookup, Transform.class, "FOO", Object.class),
229             vh.findStaticVarHandle(lookup, Transform.class, "BAR", Object.class),
230           };
231 
232       for (int i = 0; i < names.length; i++) {
233         System.out.println(
234             "Reading field "
235                 + names[i]
236                 + " using "
237                 + printGeneric(vh, handles[i])
238                 + " = "
239                 + printGeneric(vh, handles[i].invoke()));
240         if (vh.doVarHandleTests()) {
241           System.out.println(
242               "Reading field "
243                   + names[i]
244                   + " using "
245                   + printGeneric(vh, var_handles[i])
246                   + " = "
247                   + printGeneric(vh, vh.get(var_handles[i])));
248         }
249       }
250 
251       for (int i = 0; i < new_names.length; i++) {
252         System.out.println(
253             "Reading new field "
254                 + new_names[i]
255                 + " using "
256                 + printGeneric(vh, new_handles[i])
257                 + " = "
258                 + printGeneric(vh, new_handles[i].invoke()));
259         if (vh.doVarHandleTests()) {
260           System.out.println(
261               "Reading new field "
262                   + new_names[i]
263                   + " using "
264                   + printGeneric(vh, new_var_handles[i])
265                   + " = "
266                   + printGeneric(vh, vh.get(new_var_handles[i])));
267         }
268       }
269 
270       String val = "foo";
271       System.out.println("Setting BAZ to " + printGeneric(vh, val) + " with new mh.");
272       lookup.findStaticSetter(Transform.class, "BAZ", Object.class).invoke(val);
273       System.out.println("Post set with new mh: " + Transform.staticToString());
274 
275       System.out.println("Setting FOO to " + printGeneric(vh, Transform.class) + " with old mh.");
276       old_field_write.invoke(Transform.class);
277       System.out.println("Post set with old mh: " + Transform.staticToString());
278 
279       Object new_val =
280           new Object() {
281             public String toString() {
282               return "new_value object";
283             }
284           };
285       if (vh.doVarHandleTests()) {
286         System.out.println("Setting FOO to '" + printGeneric(vh, new_val) + "' with old varhandle.");
287         vh.set(foo_handle, new_val);
288         System.out.println("Post set with new varhandle: " + Transform.staticToString());
289 
290         System.out.println("Setting BAZ to 'bar' with new varhandle.");
291         vh.set(baz_handle, "bar");
292         System.out.println("Post set with old varhandle: " + Transform.staticToString());
293       }
294 
295       System.out.println("Using mh to call new private method.");
296       MethodHandle reinit =
297           lookup.findStatic(Transform.class, "reinitialize", MethodType.methodType(Void.TYPE));
298       reinit.invoke();
299       System.out.println("Post reinit with mh: " + Transform.staticToString());
300 
301       for (int i = 0; i < names.length; i++) {
302         System.out.println(
303             "Reading field "
304                 + names[i]
305                 + " using "
306                 + printGeneric(vh, handles[i])
307                 + " = "
308                 + printGeneric(vh, handles[i].invoke()));
309         if (vh.doVarHandleTests()) {
310           System.out.println(
311               "Reading field "
312                   + names[i]
313                   + " using "
314                   + printGeneric(vh, var_handles[i])
315                   + " = "
316                   + printGeneric(vh, vh.get(var_handles[i])));
317         }
318       }
319       for (int i = 0; i < new_names.length; i++) {
320         System.out.println(
321             "Reading new field "
322                 + new_names[i]
323                 + " using "
324                 + printGeneric(vh, new_handles[i])
325                 + " = "
326                 + printGeneric(vh, new_handles[i].invoke()));
327         if (vh.doVarHandleTests()) {
328           System.out.println(
329               "Reading new field "
330                   + new_names[i]
331                   + " using "
332                   + printGeneric(vh, new_var_handles[i])
333                   + " = "
334                   + printGeneric(vh, vh.get(new_var_handles[i])));
335         }
336       }
337     } catch (Throwable t) {
338       if (t instanceof Exception) {
339         throw (Exception) t;
340       } else if (t instanceof Error) {
341         throw (Error) t;
342       } else {
343         throw new RuntimeException("Unexpected throwable!", t);
344       }
345     }
346   }
347 }
348