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 
23 public class Test1982 {
run()24   public static void run() throws Exception {
25     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
26     doTest();
27   }
28 
29   private static final boolean PRINT_NONDETERMINISTIC = false;
30 
31   public static WeakHashMap<Object, Long> id_nums = new WeakHashMap<>();
32   public static long next_id = 0;
33 
printGeneric(Object o)34   public static String printGeneric(Object o) {
35     Long id = id_nums.get(o);
36     if (id == null) {
37       id = Long.valueOf(next_id++);
38       id_nums.put(o, id);
39     }
40     if (o == null) {
41       return "(ID: " + id + ") <NULL>";
42     }
43     Class oc = o.getClass();
44     if (oc.isArray() && oc.getComponentType() == Byte.TYPE) {
45       return "(ID: "
46           + id
47           + ") "
48           + Arrays.toString(Arrays.copyOf((byte[]) o, 10)).replace(']', ',')
49           + " ...]";
50     } else {
51       return "(ID: " + id + ") " + o.toString();
52     }
53   }
54 
doRedefinition()55   private static void doRedefinition() {
56     Redefinition.doCommonStructuralClassRedefinition(Transform.class, REDEFINED_DEX_BYTES);
57   }
58 
readReflective(String msg, Field[] fields, Object recv)59   private static void readReflective(String msg, Field[] fields, Object recv) throws Exception {
60     System.out.println(msg);
61     for (Field f : fields) {
62       System.out.println(
63           f.toString() + " on " + printGeneric(recv) + " = " + printGeneric(f.get(recv)));
64     }
65   }
66 
67   public static class SuperTransform {
68     public int id;
69 
SuperTransform(int id)70     public SuperTransform(int id) {
71       this.id = id;
72     }
73 
toString()74     public String toString() {
75       return "SuperTransform { id: " + id + ", class: " + getClass() + " }";
76     }
77   }
78 
79   public static class Transform extends SuperTransform {
80     static {
81     }
82 
83     public static Object BAR =
84         new Object() {
85           public String toString() {
86             return "value of <" + this.get() + ">";
87           }
88 
89           public Object get() {
90             return "BAR FIELD";
91           }
92         };
93     public static Object FOO =
94         new Object() {
95           public String toString() {
96             return "value of <" + this.get() + ">";
97           }
98 
99           public Object get() {
100             return "FOO FIELD";
101           }
102         };
103     // This class has no virtual fields or methods. This means we can structurally redefine it
104     // without having to change the size of any instances.
Transform(int id)105     public Transform(int id) {
106       super(id);
107     }
108 
staticToString()109     public static String staticToString() {
110       return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + "]";
111     }
112   }
113 
114   public static class SubTransform extends Transform {
SubTransform(int id)115     public SubTransform(int id) {
116       super(id);
117     }
118 
myToString()119     public String myToString() {
120       return "SubTransform (subclass of: " + staticToString() + ") { id: " + id + " }";
121     }
122   }
123 
124   /* Base64 encoded class of:
125    * public static class Transform extends SuperTransform {
126    *   static {}
127    *   public Transform(int id) { super(id + 1000); }
128    *   // NB This is the order the fields will be laid out in memory.
129    *   public static Object BAR;
130    *   public static Object BAZ;
131    *   public static Object FOO;
132    *   public static String staticToString() {
133    *    return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + ", BAZ: " + BAZ + "]";
134    *   }
135    * }
136    */
137   private static byte[] REDEFINED_DEX_BYTES =
138       Base64.getDecoder()
139           .decode(
140               "ZGV4CjAzNQAV5GctNSI+SEKJDaJIQLEac9ClAxZUSZq4BQAAcAAAAHhWNBIAAAAAAAAAAPQEAAAg"
141                   + "AAAAcAAAAAsAAADwAAAABQAAABwBAAADAAAAWAEAAAkAAABwAQAAAQAAALgBAADgAwAA2AEAAKoC"
142                   + "AACzAgAAvAIAAMYCAADOAgAA0wIAANgCAADdAgAA4AIAAOMCAADnAgAABgMAACADAAAwAwAAVAMA"
143                   + "AHQDAACHAwAAmwMAAK8DAADKAwAA2QMAAOQDAADnAwAA6wMAAPMDAAD2AwAAAwQAAAsEAAARBAAA"
144                   + "IQQAACsEAAAyBAAABwAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAABAAAAARAAAAEgAAABUAAAAI"
145                   + "AAAACAAAAAAAAAAJAAAACQAAAJQCAAAJAAAACQAAAJwCAAAVAAAACgAAAAAAAAAWAAAACgAAAKQC"
146                   + "AAACAAcABAAAAAIABwAFAAAAAgAHAAYAAAABAAQAAwAAAAIAAwACAAAAAgAEAAMAAAACAAAAHAAA"
147                   + "AAYAAAAdAAAACQADAAMAAAAJAAEAGgAAAAkAAgAaAAAACQAAAB0AAAACAAAAAQAAAAEAAAAAAAAA"
148                   + "EwAAAOQEAAC5BAAAAAAAAAUAAAACAAAAjQIAADYAAAAcAAIAbhAEAAAADABiAQIAYgIAAGIDAQAi"
149                   + "BAkAcBAFAAQAbiAHAAQAGgAXAG4gBwAEAG4gBgAUABoAAABuIAcABABuIAYAJAAaAAEAbiAHAAQA"
150                   + "biAGADQAGgAYAG4gBwAEAG4QCAAEAAwAEQAAAAAAAAAAAIQCAAABAAAADgAAAAIAAgACAAAAiAIA"
151                   + "AAYAAADQEegDcCAAABAADgAKAA4ACwEADgARAA4AAAAAAQAAAAcAAAABAAAACAAAAAEAAAAAAAcs"
152                   + "IEJBUjogAAcsIEJBWjogAAg8Y2xpbml0PgAGPGluaXQ+AANCQVIAA0JBWgADRk9PAAFJAAFMAAJM"
153                   + "TAAdTGFydC9UZXN0MTk4MiRTdXBlclRyYW5zZm9ybTsAGExhcnQvVGVzdDE5ODIkVHJhbnNmb3Jt"
154                   + "OwAOTGFydC9UZXN0MTk4MjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxk"
155                   + "YWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwARTGphdmEvbGFuZy9DbGFzczsAEkxqYXZhL2xh"
156                   + "bmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7"
157                   + "AA1UZXN0MTk4Mi5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZJAAZbRk9POiAAAV0AC2FjY2Vzc0ZsYWdz"
158                   + "AAZhcHBlbmQABG5hbWUADnN0YXRpY1RvU3RyaW5nAAh0b1N0cmluZwAFdmFsdWUAdn5+RDh7ImNv"
159                   + "bXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiYTgzNTJmMjU0ODg1"
160                   + "MzYyY2NkOGQ5MDlkMzUyOWM2MDA5NGRkODk2ZSIsInZlcnNpb24iOiIxLjYuMjAtZGV2In0AAgQB"
161                   + "HhgDAgUCGQQJGxcUAwADAAAJAQkBCQGIgATUBAGBgAToBAEJ2AMAAAAAAAIAAACqBAAAsAQAANgE"
162                   + "AAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAACAAAABwAAAAAgAAAAsAAADwAAAAAwAA"
163                   + "AAUAAAAcAQAABAAAAAMAAABYAQAABQAAAAkAAABwAQAABgAAAAEAAAC4AQAAASAAAAMAAADYAQAA"
164                   + "AyAAAAMAAACEAgAAARAAAAMAAACUAgAAAiAAACAAAACqAgAABCAAAAIAAACqBAAAACAAAAEAAAC5"
165                   + "BAAAAxAAAAIAAADUBAAABiAAAAEAAADkBAAAABAAAAEAAAD0BAAA");
166 
doTest()167   public static void doTest() throws Exception {
168     Transform t1 = new Transform(1);
169     SuperTransform t2 = new SubTransform(2);
170     readReflective("Reading with reflection.", Transform.class.getDeclaredFields(), null);
171     readReflective(
172         "Reading with reflection on subtransform instance.", SubTransform.class.getFields(), t2);
173     System.out.println("Reading normally.");
174     System.out.println("Read BAR field: " + printGeneric(Transform.BAR));
175     System.out.println("Read FOO field: " + printGeneric(Transform.FOO));
176     System.out.println("t1 is " + printGeneric(t1));
177     System.out.println("t2 is " + printGeneric(t2));
178     doRedefinition();
179     System.out.println("Redefined: " + Transform.staticToString());
180     readReflective(
181         "Reading with reflection after redefinition.", Transform.class.getDeclaredFields(), null);
182     readReflective(
183         "Reading with reflection after redefinition on subtransform instance.",
184         SubTransform.class.getFields(),
185         t2);
186     System.out.println("Reading normally after possible modification.");
187     System.out.println("Read FOO field: " + printGeneric(Transform.FOO));
188     System.out.println("Read BAR field: " + printGeneric(Transform.BAR));
189     System.out.println("t1 is " + printGeneric(t1));
190     System.out.println("t2 is " + printGeneric(t2));
191     SubTransform t3 = new SubTransform(3);
192     System.out.println("new SubTransform is " + printGeneric(t3));
193     System.out.println("myToString of " + printGeneric(t3) + " is " + t3.myToString());
194     // We verified in test 1980 that getDeclaredConstructor will throw if the class is obsolete.
195     // This therefore is a reasonable test that the t1 object's declaring class was updated.
196     System.out.println(
197         "Creating new transform from t1 class = "
198             + printGeneric(t1.getClass().getDeclaredConstructor(Integer.TYPE).newInstance(4)));
199   }
200 }
201