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 dalvik.system.InMemoryDexClassLoader;
20 import java.nio.ByteBuffer;
21 import java.util.ArrayList;
22 import java.util.Base64;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.function.Supplier;
25 
26 public class Test2001 {
27   private static final int NUM_THREADS = 20;
28   // Don't perform more than this many repeats per thread to prevent OOMEs
29   private static final int TASK_COUNT_LIMIT = 1000;
30 
31   public static class Transform {
32     public String greetingEnglish;
33 
Transform()34     public Transform() {
35       this.greetingEnglish = "Hello";
36     }
37 
sayHi()38     public String sayHi() {
39       return greetingEnglish + " from " + Thread.currentThread().getName();
40     }
41   }
42 
43   /**
44    * base64 encoded class/dex file for
45    * public static class Transform {
46    *   public String greetingEnglish;
47    *   public String greetingFrench;
48    *   public String greetingDanish;
49    *   public String greetingJapanese;
50    *
51    *   public Transform() {
52    *     this.greetingEnglish = "Hello World";
53    *     this.greetingFrench = "Bonjour le Monde";
54    *     this.greetingDanish = "Hej Verden";
55    *     this.greetingJapanese = "こんにちは世界";
56    *   }
57    *   public String sayHi() {
58    *     return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " + sayHiJapanese() + " from " + Thread.currentThread().getName();
59    *   }
60    *   public String sayHiEnglish() {
61    *     return greetingEnglish;
62    *   }
63    *   public String sayHiDanish() {
64    *     return greetingDanish;
65    *   }
66    *   public String sayHiJapanese() {
67    *     return greetingJapanese;
68    *   }
69    *   public String sayHiFrench() {
70    *     return greetingFrench;
71    *   }
72    * }
73    */
74   private static final byte[] DEX_BYTES =
75       Base64.getDecoder()
76           .decode(
77               "ZGV4CjAzNQCnKPY06VRa4aM/zFW0MYLmRxT/NtXxD/H4BgAAcAAAAHhWNBIAAAAAAAAAADQGAAAl"
78                   + "AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAAAABQAA+AEAAEoD"
79                   + "AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA"
80                   + "AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA"
81                   + "7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAMUFAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO"
82                   + "AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA"
83                   + "AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA"
84                   + "AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA"
85                   + "BwACABUAAAAHAAAAFgAAAAAAAAABAAAABAAAAAAAAAAQAAAAJAYAAOsFAAAAAAAABwABAAIAAAAt"
86                   + "AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF"
87                   + "BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA"
88                   + "AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA"
89                   + "NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU"
90                   + "EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA"
91                   + "WxADAA4ACwAOPEtLS0sAEgAOABgADgAVAA4AHgAOABsADgAAAAABAAAABQAGIGZyb20gAAIsIAAG"
92                   + "PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA"
93                   + "GExhcnQvVGVzdDIwMDEkVHJhbnNmb3JtOwAOTGFydC9UZXN0MjAwMTsAIkxkYWx2aWsvYW5ub3Rh"
94                   + "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph"
95                   + "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp"
96                   + "bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDIwMDEuamF2YQAJVHJhbnNmb3JtAAFWAAth"
97                   + "Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz"
98                   + "aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt"
99                   + "ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph"
100                   + "cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAiwF+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWci"
101                   + "LCJoYXMtY2hlY2tzdW1zIjpmYWxzZSwibWluLWFwaSI6MSwic2hhLTEiOiJmNjJiOGNlNmEwNTkw"
102                   + "MDU0ZWYzNGExYWVkZTcwYjQ2NjY4ZThiNDlmIiwidmVyc2lvbiI6IjIuMC4xLWRldiJ9AAfjgZPj"
103                   + "gpPjgavjgaHjga/kuJbnlYwAAgIBIhgBAgMCEwQJGxcRAAQBBQABAQEBAQEBAIGABOwFAQH4AwEB"
104                   + "jAUBAaQFAQG8BQEB1AUAAAAAAAAAAgAAANwFAADiBQAAGAYAAAAAAAAAAAAAAAAAABAAAAAAAAAA"
105                   + "AQAAAAAAAAABAAAAJQAAAHAAAAACAAAACQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAF"
106                   + "AAAADAAAAHgBAAAGAAAAAQAAANgBAAABIAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQD"
107                   + "AAACIAAAJQAAAEoDAAAEIAAAAgAAANwFAAAAIAAAAQAAAOsFAAADEAAAAgAAABQGAAAGIAAAAQAA"
108                   + "ACQGAAAAEAAAAQAAADQGAAA=");
109 
110   /*
111    * base64 encoded class/dex file for
112     package art;
113     import java.util.function.Supplier;
114     public class SubTransform extends art.Test2001.Transform implements Supplier<String> {
115       public SubTransform() {
116         super();
117       }
118       public String get() {
119         return "from SUBCLASS: " + super.sayHi();
120       }
121     }
122    */
123   private static final byte[] SUB_DEX_BYTES =
124       Base64.getDecoder()
125           .decode(
126               "ZGV4CjAzNQBawzkIDf9khFw00md41U4vIqRuhqBTjM+0BAAAcAAAAHhWNBIAAAAAAAAAAPwDAAAV"
127                   + "AAAAcAAAAAgAAADEAAAABAAAAOQAAAAAAAAAAAAAAAgAAAAUAQAAAQAAAFQBAABAAwAAdAEAAAIC"
128                   + "AAAKAgAADgIAABECAAAVAgAAKQIAAEMCAABiAgAAdgIAAIoCAAClAgAAxAIAAOMCAAD2AgAA+QIA"
129                   + "AAEDAAASAwAAFwMAAB4DAAAoAwAALwMAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAANAAAA"
130                   + "AgAAAAMAAAAAAAAAAgAAAAQAAAAAAAAAAwAAAAUAAAD8AQAADQAAAAcAAAAAAAAAAAADAAAAAAAA"
131                   + "AAAAEAAAAAAAAQAQAAAAAQADAAAAAAABAAEAEQAAAAUAAwAAAAAABQACAA4AAAAFAAEAEgAAAAAA"
132                   + "AAABAAAAAQAAAPQBAAAMAAAA7AMAAMsDAAAAAAAAAgABAAEAAADpAQAABQAAAG4QAgABAAwAEQAA"
133                   + "AAQAAQACAAAA7QEAABYAAABvEAQAAwAMACIBBQBwEAUAAQAaAg8AbiAGACEAbiAGAAEAbhAHAAEA"
134                   + "DAARAAEAAQABAAAA5AEAAAQAAABwEAMAAAAOAAYADjwABAAOAAkADgAAAAABAAAABgAAAAEAAAAE"
135                   + "AAY8aW5pdD4AAj47AAFMAAJMTAASTGFydC9TdWJUcmFuc2Zvcm07ABhMYXJ0L1Rlc3QyMDAxJFRy"
136                   + "YW5zZm9ybTsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABJMamF2YS9sYW5nL09iamVj"
137                   + "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwAdTGphdmEv"
138                   + "dXRpbC9mdW5jdGlvbi9TdXBwbGllcjsAHUxqYXZhL3V0aWwvZnVuY3Rpb24vU3VwcGxpZXI8ABFT"
139                   + "dWJUcmFuc2Zvcm0uamF2YQABVgAGYXBwZW5kAA9mcm9tIFNVQkNMQVNTOiAAA2dldAAFc2F5SGkA"
140                   + "CHRvU3RyaW5nAAV2YWx1ZQCLAX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsImhhcy1j"
141                   + "aGVja3N1bXMiOmZhbHNlLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImY2MmI4Y2U2YTA1OTAwNTRlZjM0"
142                   + "YTFhZWRlNzBiNDY2NjhlOGI0OWYiLCJ2ZXJzaW9uIjoiMi4wLjEtZGV2In0AAgIBExwEFwUXCxcI"
143                   + "FwEAAAECAIGABMwDAcEg9AIBAZADAAAAAAAAAQAAAL0DAADkAwAAAAAAAAAAAAAAAAAADwAAAAAA"
144                   + "AAABAAAAAAAAAAEAAAAVAAAAcAAAAAIAAAAIAAAAxAAAAAMAAAAEAAAA5AAAAAUAAAAIAAAAFAEA"
145                   + "AAYAAAABAAAAVAEAAAEgAAADAAAAdAEAAAMgAAADAAAA5AEAAAEQAAACAAAA9AEAAAIgAAAVAAAA"
146                   + "AgIAAAQgAAABAAAAvQMAAAAgAAABAAAAywMAAAMQAAACAAAA4AMAAAYgAAABAAAA7AMAAAAQAAAB"
147                   + "AAAA/AMAAA==");
148 
run()149   public static void run() throws Exception {
150     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
151     doTest();
152   }
153 
mkTransform()154   public static Supplier<String> mkTransform() {
155     try {
156       return (Supplier<String>)
157           (new InMemoryDexClassLoader(
158                   ByteBuffer.wrap(SUB_DEX_BYTES), Test2001.class.getClassLoader())
159               .loadClass("art.SubTransform")
160               .newInstance());
161     } catch (Exception e) {
162       return () -> {
163         return e.toString();
164       };
165     }
166   }
167 
168   public static final class MyThread extends Thread {
MyThread(CountDownLatch delay, int id)169     public MyThread(CountDownLatch delay, int id) {
170       super("Thread: " + id);
171       this.thr_id = id;
172       this.results = new ArrayList<>(TASK_COUNT_LIMIT);
173       this.finish = false;
174       this.delay = delay;
175     }
176 
run()177     public void run() {
178       delay.countDown();
179       while (!finish && results.size() < TASK_COUNT_LIMIT) {
180         Supplier<String> t = mkTransform();
181         results.add(t.get());
182       }
183     }
184 
finish()185     public void finish() throws Exception {
186       finish = true;
187       this.join();
188     }
189 
Check()190     public void Check() throws Exception {
191       for (String s : results) {
192         if (!s.equals("from SUBCLASS: Hello from " + getName())
193             && !s.equals("from SUBCLASS: Hello, null, null, null from " + getName())
194             && !s.equals(
195                 "from SUBCLASS: Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from "
196                     + getName())) {
197           System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
198         }
199       }
200     }
201 
202     public ArrayList<String> results;
203     public volatile boolean finish;
204     public int thr_id;
205     public CountDownLatch delay;
206   }
207 
startThreads(int num_threads)208   public static MyThread[] startThreads(int num_threads) throws Exception {
209     CountDownLatch cdl = new CountDownLatch(num_threads);
210     MyThread[] res = new MyThread[num_threads];
211     for (int i = 0; i < num_threads; i++) {
212       res[i] = new MyThread(cdl, i);
213       res[i].start();
214     }
215     cdl.await();
216     return res;
217   }
218 
finishThreads(MyThread[] thrs)219   public static void finishThreads(MyThread[] thrs) throws Exception {
220     for (MyThread t : thrs) {
221       t.finish();
222     }
223     for (MyThread t : thrs) {
224       t.Check();
225     }
226   }
227 
doTest()228   public static void doTest() throws Exception {
229     MyThread[] threads = startThreads(NUM_THREADS);
230     Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
231     finishThreads(threads);
232   }
233 }
234