1 /*
2  * Copyright (C) 2020 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 Test2031 {
24   public static class MyClass {
starting()25     public void starting() {
26       System.out.println("Starting up!");
27     }
toString()28     public String toString() {
29       return "This is my object!";
30     }
31   }
32 
$noinline$doSimulateZygoteFork()33   public static void $noinline$doSimulateZygoteFork() {
34     simulateZygoteFork();
35   }
36 
$noinline$run(String testdir)37   public static void $noinline$run(String testdir) throws Exception {
38     $noinline$doSimulateZygoteFork();
39     final MyClass myObject = new MyClass();
40     $noinline$startTest(testdir, myObject);
41     System.out.println(myObject);
42   }
43 
$noinline$startTest(String testdir, final MyClass myObject)44   public static void $noinline$startTest(String testdir, final MyClass myObject) throws Exception {
45     Thread thr = new Thread(() -> {
46       try {
47         waitForNativeSleep();
48         setupJvmti(testdir);
49         Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
50         Thread tester = new Thread(() -> {
51           try {
52             myObject.starting();
53             doTest();
54           } catch (Exception e) {
55             throw new Error("Failure!", e);
56           }
57         });
58         tester.start();
59         tester.join();
60         wakeupNativeSleep();
61       } catch (Exception e) {
62         throw new Error("Failure!", e);
63       }
64     });
65     thr.start();
66     nativeSleep();
67     thr.join();
68   }
69 
simulateZygoteFork()70   public static native void simulateZygoteFork();
setupJvmti(String testdir)71   public static native void setupJvmti(String testdir);
waitForNativeSleep()72   public static native void waitForNativeSleep();
wakeupNativeSleep()73   public static native void wakeupNativeSleep();
nativeSleep()74   public static native void nativeSleep();
75 
doRedefinition()76   private static void doRedefinition() {
77     Redefinition.doCommonStructuralClassRedefinition(Transform.class, REDEFINED_DEX_BYTES);
78   }
79 
80   public static class SuperTransform {
81     public int id;
82 
SuperTransform(int id)83     public SuperTransform(int id) {
84       this.id = id;
85     }
86 
toString()87     public String toString() {
88       return "SuperTransform { id: " + id + ", class: " + getClass() + " }";
89     }
90   }
91 
92   public static class Transform extends SuperTransform {
93     static {
94     }
95 
96     public static Object BAR =
97         new Object() {
98           public String toString() {
99             return "value of <" + this.get() + ">";
100           }
101 
102           public Object get() {
103             return "BAR FIELD";
104           }
105         };
106     public static Object FOO =
107         new Object() {
108           public String toString() {
109             return "value of <" + this.get() + ">";
110           }
111 
112           public Object get() {
113             return "FOO FIELD";
114           }
115         };
116     // This class has no virtual fields or methods. This means we can structurally redefine it
117     // without having to change the size of any instances.
Transform(int id)118     public Transform(int id) {
119       super(id);
120     }
121 
staticToString()122     public static String staticToString() {
123       return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + "]";
124     }
125   }
126 
127   public static class SubTransform extends Transform {
SubTransform(int id)128     public SubTransform(int id) {
129       super(id);
130     }
131 
myToString()132     public String myToString() {
133       return "SubTransform (subclass of: " + staticToString() + ") { id: " + id + " }";
134     }
135   }
136 
137   /* Base64 encoded class of:
138    * public static class Transform extends SuperTransform {
139    *   static {}
140    *   public Transform(int id) { super(id + 1000); }
141    *   // NB This is the order the fields will be laid out in memory.
142    *   public static Object BAR;
143    *   public static Object BAZ;
144    *   public static Object FOO;
145    *   public static String staticToString() {
146    *    return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + ", BAZ: " + BAZ + "]";
147    *   }
148    * }
149    */
150   private static byte[] REDEFINED_DEX_BYTES =
151       Base64.getDecoder()
152           .decode(
153 "ZGV4CjAzNQC78lC18jI6omumTaKUcf/8pvcR4/Hx2u3QBQAAcAAAAHhWNBIAAAAAAAAAAAwFAAAg" +
154 "AAAAcAAAAAsAAADwAAAABQAAABwBAAADAAAAWAEAAAkAAABwAQAAAQAAALgBAAD4AwAA2AEAAKoC" +
155 "AACzAgAAvAIAAMYCAADOAgAA0wIAANgCAADdAgAA4AIAAOMCAADnAgAABgMAACADAAAwAwAAVAMA" +
156 "AHQDAACHAwAAmwMAAK8DAADKAwAA2QMAAOQDAADnAwAA6wMAAPMDAAD2AwAAAwQAAAsEAAARBAAA" +
157 "IQQAACsEAAAyBAAABwAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAABAAAAARAAAAEgAAABUAAAAI" +
158 "AAAACAAAAAAAAAAJAAAACQAAAJQCAAAJAAAACQAAAJwCAAAVAAAACgAAAAAAAAAWAAAACgAAAKQC" +
159 "AAACAAcABAAAAAIABwAFAAAAAgAHAAYAAAABAAQAAwAAAAIAAwACAAAAAgAEAAMAAAACAAAAHAAA" +
160 "AAYAAAAdAAAACQADAAMAAAAJAAEAGgAAAAkAAgAaAAAACQAAAB0AAAACAAAAAQAAAAEAAAAAAAAA" +
161 "EwAAAPwEAADQBAAAAAAAAAUAAAACAAAAjQIAADYAAAAcAAIAbhAEAAAADABiAQIAYgIAAGIDAQAi" +
162 "BAkAcBAFAAQAbiAHAAQAGgAXAG4gBwAEAG4gBgAUABoAAABuIAcABABuIAYAJAAaAAEAbiAHAAQA" +
163 "biAGADQAGgAYAG4gBwAEAG4QCAAEAAwAEQAAAAAAAAAAAIQCAAABAAAADgAAAAIAAgACAAAAiAIA" +
164 "AAYAAADQEegDcCAAABAADgAPAA4AEAEADgAWAA4AAAAAAQAAAAcAAAABAAAACAAAAAEAAAAAAAcs" +
165 "IEJBUjogAAcsIEJBWjogAAg8Y2xpbml0PgAGPGluaXQ+AANCQVIAA0JBWgADRk9PAAFJAAFMAAJM" +
166 "TAAdTGFydC9UZXN0MjAzMSRTdXBlclRyYW5zZm9ybTsAGExhcnQvVGVzdDIwMzEkVHJhbnNmb3Jt" +
167 "OwAOTGFydC9UZXN0MjAzMTsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxk" +
168 "YWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwARTGphdmEvbGFuZy9DbGFzczsAEkxqYXZhL2xh" +
169 "bmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7" +
170 "AA1UZXN0MjAzMS5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZJAAZbRk9POiAAAV0AC2FjY2Vzc0ZsYWdz" +
171 "AAZhcHBlbmQABG5hbWUADnN0YXRpY1RvU3RyaW5nAAh0b1N0cmluZwAFdmFsdWUAjAF+fkQ4eyJj" +
172 "b21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJoYXMtY2hlY2tzdW1zIjpmYWxzZSwibWluLWFwaSI6" +
173 "MSwic2hhLTEiOiJkMWQ1MWMxY2IzZTg1YWEzMGUwMGE2ODIyY2NhODNiYmUxZGZlOTQ1IiwidmVy" +
174 "c2lvbiI6IjIuMC4xMy1kZXYifQACBAEeGAMCBQIZBAkbFxQDAAMAAAkBCQEJAYiABNQEAYGABOgE" +
175 "AQnYAwAAAAAAAAIAAADBBAAAxwQAAPAEAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAA" +
176 "ACAAAABwAAAAAgAAAAsAAADwAAAAAwAAAAUAAAAcAQAABAAAAAMAAABYAQAABQAAAAkAAABwAQAA" +
177 "BgAAAAEAAAC4AQAAASAAAAMAAADYAQAAAyAAAAMAAACEAgAAARAAAAMAAACUAgAAAiAAACAAAACq" +
178 "AgAABCAAAAIAAADBBAAAACAAAAEAAADQBAAAAxAAAAIAAADsBAAABiAAAAEAAAD8BAAAABAAAAEA" +
179 "AAAMBQAA");
180 
doTest()181   public static void doTest() throws Exception {
182     Transform t1 = new Transform(1);
183     SuperTransform t2 = new SubTransform(2);
184     doRedefinition();
185   }
186 }
187