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.util.*;
22 import java.util.concurrent.CountDownLatch;
23 import java.util.function.Supplier;
24 
25 public class Test1979 {
run()26   public static void run() throws Exception {
27     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
28     doTest();
29   }
30 
31   private static final boolean PRINT_NONDETERMINISTIC = false;
32 
33   public static WeakHashMap<Object, Long> id_nums = new WeakHashMap<>();
34   public static long next_id = 0;
35 
printGeneric(Object o)36   public static String printGeneric(Object o) {
37     Long id = id_nums.get(o);
38     if (id == null) {
39       id = Long.valueOf(next_id++);
40       id_nums.put(o, id);
41     }
42     if (o == null) {
43       return "(ID: " + id + ") <NULL>";
44     }
45     Class oc = o.getClass();
46     if (oc.isArray() && oc.getComponentType() == Byte.TYPE) {
47       return "(ID: "
48           + id
49           + ") "
50           + Arrays.toString(Arrays.copyOf((byte[]) o, 10)).replace(']', ',')
51           + " ...]";
52     } else {
53       return "(ID: " + id + ") " + o.toString();
54     }
55   }
56 
doRedefinition()57   private static void doRedefinition() {
58     Redefinition.doCommonStructuralClassRedefinition(
59         Transform.class, REDEFINED_DEX_BYTES);
60   }
61 
readReflective(String msg)62   private static void readReflective(String msg) throws Exception {
63     System.out.println(msg);
64     for (Field f : Transform.class.getFields()) {
65       System.out.println(f.toString() + " = " + printGeneric(f.get(null)));
66     }
67   }
68 
69   public static class Transform {
70     static {}
71     public static Object BAR = new Object() {
72       public String toString() {
73         return "value of <" + this.get() + ">";
74       }
75       public Object get() {
76         return "BAR FIELD";
77       }
78     };
79     public static Object FOO = new Object() {
80       public String toString() {
81         return "value of <" + this.get() + ">";
82       }
83       public Object get() {
84         return "FOO FIELD";
85       }
86     };
staticToString()87     public static String staticToString() {
88       return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + "]";
89     }
90   }
91 
92   /* Base64 encoded class of:
93    * public static class Transform {
94    *   static {}
95    *   // NB This is the order the fields will be laid out in memory.
96    *   public static Object BAR;
97    *   public static Object BAZ;
98    *   public static Object FOO;
99    *   public static String staticToString() {
100    *    return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + ", BAZ: " + BAZ + "]";
101    *   }
102    * }
103    */
104   private static byte[] REDEFINED_DEX_BYTES = Base64.getDecoder().decode(
105       "ZGV4CjAzNQDrznAlv8Fs6FNeDAHAxiU9uy8DUayd82ZkBQAAcAAAAHhWNBIAAAAAAAAAAKAEAAAd" +
106       "AAAAcAAAAAkAAADkAAAABAAAAAgBAAADAAAAOAEAAAkAAABQAQAAAQAAAJgBAACsAwAAuAEAAHoC" +
107       "AACDAgAAjAIAAJYCAACeAgAAowIAAKgCAACtAgAAsAIAALQCAADOAgAA3gIAAAIDAAAiAwAANQMA" +
108       "AEkDAABdAwAAeAMAAIcDAACSAwAAlQMAAJ0DAACgAwAArQMAALUDAAC7AwAAywMAANUDAADcAwAA" +
109       "CQAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAABAAAAATAAAABwAAAAYAAAAAAAAACAAAAAcAAABs" +
110       "AgAACAAAAAcAAAB0AgAAEwAAAAgAAAAAAAAAAAAFAAQAAAAAAAUABQAAAAAABQAGAAAAAAADAAIA" +
111       "AAAAAAMAAwAAAAAAAAAZAAAABAAAABoAAAAFAAMAAwAAAAcAAwADAAAABwABABcAAAAHAAIAFwAA" +
112       "AAcAAAAaAAAAAAAAAAEAAAAFAAAAAAAAABEAAACQBAAAYwQAAAAAAAAFAAAAAgAAAGgCAAA2AAAA" +
113       "HAAAAG4QAwAAAAwAYgECAGICAABiAwEAIgQHAHAQBQAEAG4gBwAEABoAFABuIAcABABuIAYAFAAa" +
114       "AAAAbiAHAAQAbiAGACQAGgABAG4gBwAEAG4gBgA0ABoAFQBuIAcABABuEAgABAAMABEAAAAAAAAA" +
115       "AABgAgAAAQAAAA4AAAABAAEAAQAAAGQCAAAEAAAAcBAEAAAADgAIAA4ABwAOAA4ADgABAAAABQAA" +
116       "AAEAAAAGAAcsIEJBUjogAAcsIEJBWjogAAg8Y2xpbml0PgAGPGluaXQ+AANCQVIAA0JBWgADRk9P" +
117       "AAFMAAJMTAAYTGFydC9UZXN0MTk3OSRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTc5OwAiTGRhbHZp" +
118       "ay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xh" +
119       "c3M7ABFMamF2YS9sYW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0" +
120       "cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsADVRlc3QxOTc5LmphdmEACVRyYW5zZm9y" +
121       "bQABVgAGW0ZPTzogAAFdAAthY2Nlc3NGbGFncwAGYXBwZW5kAARuYW1lAA5zdGF0aWNUb1N0cmlu" +
122       "ZwAIdG9TdHJpbmcABXZhbHVlAHZ+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJtaW4t" +
123       "YXBpIjoxLCJzaGEtMSI6ImE4MzUyZjI1NDg4NTM2MmNjZDhkOTA5ZDM1MjljNjAwOTRkZDg5NmUi" +
124       "LCJ2ZXJzaW9uIjoiMS42LjIwLWRldiJ9AAICARsYAQIDAhYECRgXEgMAAwAACQEJAQkAiIAEtAQB" +
125       "gYAEyAQBCbgDAAAAAAAAAAIAAABUBAAAWgQAAIQEAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAA" +
126       "AAAAAQAAAB0AAABwAAAAAgAAAAkAAADkAAAAAwAAAAQAAAAIAQAABAAAAAMAAAA4AQAABQAAAAkA" +
127       "AABQAQAABgAAAAEAAACYAQAAASAAAAMAAAC4AQAAAyAAAAMAAABgAgAAARAAAAIAAABsAgAAAiAA" +
128       "AB0AAAB6AgAABCAAAAIAAABUBAAAACAAAAEAAABjBAAAAxAAAAIAAACABAAABiAAAAEAAACQBAAA" +
129       "ABAAAAEAAACgBAAA");
130 
131   public interface TRunnable {
run()132     public void run() throws Exception;
133   }
134 
doTest()135   public static void doTest() throws Exception {
136     final CountDownLatch cdl = new CountDownLatch(1);
137     final CountDownLatch continueLatch = new CountDownLatch(1);
138     // Make sure the transformed class is already loaded before we start running (and possibly
139     // compiling) the test thread.
140     System.out.println("Hitting class " + Transform.staticToString());
141     Thread t = new Thread(() -> {
142       try {
143         // We don't want to read these in the same method here to ensure that no reference to
144         // Transform is active on this thread at the time the redefinition occurs. To accomplish
145         // this just run the code in a different method, which is good enough.
146         ((TRunnable)() -> {
147           System.out.println("Initial: " + Transform.staticToString());
148           readReflective("Reading with reflection.");
149           System.out.println("Reading normally.");
150           System.out.println("Read BAR field: " + printGeneric(Transform.BAR));
151           System.out.println("Read FOO field: " + printGeneric(Transform.FOO));
152         }).run();
153         cdl.countDown();
154         continueLatch.await();
155         // Now that redefinition has occurred without this frame having any references to the
156         // Transform class we want to make sure we have the correct offsets.
157         System.out.println("Redefined: " + Transform.staticToString());
158         readReflective("Reading with reflection after possible modification.");
159         System.out.println("Reading normally after possible modification.");
160         System.out.println("Read FOO field: " + printGeneric(Transform.FOO));
161         System.out.println("Read BAR field: " + printGeneric(Transform.BAR));
162       } catch (Exception e) {
163         throw new Error(e);
164       }
165     });
166     t.start();
167     cdl.await();
168     doRedefinition();
169     continueLatch.countDown();
170     t.join();
171   }
172 }
173