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