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.reflect.Field;
20 import java.util.ArrayList;
21 import java.util.Base64;
22 import java.util.concurrent.CountDownLatch;
23 public class Test1995 {
24   private static final int NUM_THREADS = 20;
25   // Don't perform more than this many repeats per thread to prevent OOMEs
26   private static final int TASK_COUNT_LIMIT = 1000;
27 
28   public static final class Transform {
29     public String greetingEnglish;
Transform()30     public Transform() {
31       this.greetingEnglish = "Hello";
32     }
sayHi()33     public String sayHi() {
34       return greetingEnglish + " from " + Thread.currentThread().getName();
35     }
36   }
37 
38   /**
39    * base64 encoded class/dex file for
40    * public static final class Transform {
41    *   public String greetingEnglish;
42    *   public String greetingFrench;
43    *   public String greetingDanish;
44    *   public String greetingJapanese;
45    *
46    *   public Transform() {
47    *     this.greetingEnglish = "Hello World";
48    *     this.greetingFrench = "Bonjour le Monde";
49    *     this.greetingDanish = "Hej Verden";
50    *     this.greetingJapanese = "こんにちは世界";
51    *   }
52    *   public String sayHi() {
53    *     return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " + sayHiJapanese() + " from " + Thread.currentThread().getName();
54    *   }
55    *   public String sayHiEnglish() {
56    *     return greetingEnglish;
57    *   }
58    *   public String sayHiDanish() {
59    *     return greetingDanish;
60    *   }
61    *   public String sayHiJapanese() {
62    *     return greetingJapanese;
63    *   }
64    *   public String sayHiFrench() {
65    *     return greetingFrench;
66    *   }
67    * }
68    */
69   private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
70 "ZGV4CjAzNQCsHrUqkb8cYgT2oYN7HlVbeOxJT/kONRvgBgAAcAAAAHhWNBIAAAAAAAAAABwGAAAl" +
71 "AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAADoBAAA+AEAAEoD" +
72 "AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA" +
73 "AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA" +
74 "7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAK8FAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO" +
75 "AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA" +
76 "AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA" +
77 "AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA" +
78 "BwACABUAAAAHAAAAFgAAAAAAAAARAAAABAAAAAAAAAAQAAAADAYAANUFAAAAAAAABwABAAIAAAAt" +
79 "AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF" +
80 "BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA" +
81 "AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA" +
82 "NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU" +
83 "EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA" +
84 "WxADAA4ACQAOPEtLS0sAEAAOABYADgATAA4AHAAOABkADgAAAAABAAAABQAGIGZyb20gAAIsIAAG" +
85 "PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA" +
86 "GExhcnQvVGVzdDE5OTUkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5NTsAIkxkYWx2aWsvYW5ub3Rh" +
87 "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
88 "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
89 "bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDE5OTUuamF2YQAJVHJhbnNmb3JtAAFWAAth" +
90 "Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz" +
91 "aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt" +
92 "ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph" +
93 "cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIs" +
94 "Im1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2ZDg4" +
95 "YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AB+OBk+OCk+OBq+OBoeOBr+S4lueVjAACAgEi" +
96 "GAECAwITBBkbFxEABAEFAAEBAQEBAQEAgYAE7AUBAfgDAQGMBQEBpAUBAbwFAQHUBQAAAAAAAgAA" +
97 "AMYFAADMBQAAAAYAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAJQAAAHAAAAACAAAA" +
98 "CQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAFAAAADAAAAHgBAAAGAAAAAQAAANgBAAAB" +
99 "IAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQDAAACIAAAJQAAAEoDAAAEIAAAAgAAAMYF" +
100 "AAAAIAAAAQAAANUFAAADEAAAAgAAAPwFAAAGIAAAAQAAAAwGAAAAEAAAAQAAABwGAAA=");
101 
102 
run()103   public static void run() throws Exception {
104     Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
105     doTest();
106   }
107 
108   public static final class MyThread extends Thread {
MyThread(CountDownLatch delay, int id)109     public MyThread(CountDownLatch delay, int id) {
110       super("Thread: " + id);
111       this.thr_id = id;
112       this.results = new ArrayList<>(TASK_COUNT_LIMIT);
113       this.finish = false;
114       this.delay = delay;
115     }
116 
run()117     public void run() {
118       delay.countDown();
119       while (!finish && results.size() < TASK_COUNT_LIMIT) {
120         Transform t = new Transform();
121         results.add(t.sayHi());
122       }
123     }
124 
finish()125     public void finish() throws Exception {
126       finish = true;
127       this.join();
128     }
129 
Check()130     public void Check() throws Exception {
131       for (String s : results) {
132         if (!s.equals("Hello from " + getName()) &&
133             !s.equals("Hello, null, null, null from " + getName()) &&
134             !s.equals("Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from " + getName())) {
135           System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
136         }
137       }
138     }
139 
140     public ArrayList<String> results;
141     public volatile boolean finish;
142     public int thr_id;
143     public CountDownLatch delay;
144   }
145 
startThreads(int num_threads)146   public static MyThread[] startThreads(int num_threads) throws Exception {
147     CountDownLatch cdl = new CountDownLatch(num_threads);
148     MyThread[] res = new MyThread[num_threads];
149     for (int i = 0; i < num_threads; i++) {
150       res[i] = new MyThread(cdl, i);
151       res[i].start();
152     }
153     cdl.await();
154     return res;
155   }
finishThreads(MyThread[] thrs)156   public static void finishThreads(MyThread[] thrs) throws Exception {
157     for (MyThread t : thrs) {
158       t.finish();
159     }
160     for (MyThread t : thrs) {
161       t.Check();
162     }
163   }
164 
doTest()165   public static void doTest() throws Exception {
166     MyThread[] threads = startThreads(NUM_THREADS);
167     Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
168     finishThreads(threads);
169   }
170 }
171