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 import java.util.concurrent.CountDownLatch; 23 import java.util.function.Supplier; 24 25 public class Test1979 { run()26 public static void run() throws Exception { 27 Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); 28 doTest(); 29 } 30 31 private static final boolean PRINT_NONDETERMINISTIC = false; 32 33 public static WeakHashMap<Object, Long> id_nums = new WeakHashMap<>(); 34 public static long next_id = 0; 35 printGeneric(Object o)36 public static String printGeneric(Object o) { 37 Long id = id_nums.get(o); 38 if (id == null) { 39 id = Long.valueOf(next_id++); 40 id_nums.put(o, id); 41 } 42 if (o == null) { 43 return "(ID: " + id + ") <NULL>"; 44 } 45 Class oc = o.getClass(); 46 if (oc.isArray() && oc.getComponentType() == Byte.TYPE) { 47 return "(ID: " 48 + id 49 + ") " 50 + Arrays.toString(Arrays.copyOf((byte[]) o, 10)).replace(']', ',') 51 + " ...]"; 52 } else { 53 return "(ID: " + id + ") " + o.toString(); 54 } 55 } 56 doRedefinition()57 private static void doRedefinition() { 58 Redefinition.doCommonStructuralClassRedefinition( 59 Transform.class, REDEFINED_DEX_BYTES); 60 } 61 readReflective(String msg)62 private static void readReflective(String msg) throws Exception { 63 System.out.println(msg); 64 for (Field f : Transform.class.getFields()) { 65 System.out.println(f.toString() + " = " + printGeneric(f.get(null))); 66 } 67 } 68 69 public static class Transform { 70 static {} 71 public static Object BAR = new Object() { 72 public String toString() { 73 return "value of <" + this.get() + ">"; 74 } 75 public Object get() { 76 return "BAR FIELD"; 77 } 78 }; 79 public static Object FOO = new Object() { 80 public String toString() { 81 return "value of <" + this.get() + ">"; 82 } 83 public Object get() { 84 return "FOO FIELD"; 85 } 86 }; staticToString()87 public static String staticToString() { 88 return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + "]"; 89 } 90 } 91 92 /* Base64 encoded class of: 93 * public static class Transform { 94 * static {} 95 * // NB This is the order the fields will be laid out in memory. 96 * public static Object BAR; 97 * public static Object BAZ; 98 * public static Object FOO; 99 * public static String staticToString() { 100 * return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + ", BAZ: " + BAZ + "]"; 101 * } 102 * } 103 */ 104 private static byte[] REDEFINED_DEX_BYTES = Base64.getDecoder().decode( 105 "ZGV4CjAzNQDrznAlv8Fs6FNeDAHAxiU9uy8DUayd82ZkBQAAcAAAAHhWNBIAAAAAAAAAAKAEAAAd" + 106 "AAAAcAAAAAkAAADkAAAABAAAAAgBAAADAAAAOAEAAAkAAABQAQAAAQAAAJgBAACsAwAAuAEAAHoC" + 107 "AACDAgAAjAIAAJYCAACeAgAAowIAAKgCAACtAgAAsAIAALQCAADOAgAA3gIAAAIDAAAiAwAANQMA" + 108 "AEkDAABdAwAAeAMAAIcDAACSAwAAlQMAAJ0DAACgAwAArQMAALUDAAC7AwAAywMAANUDAADcAwAA" + 109 "CQAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAABAAAAATAAAABwAAAAYAAAAAAAAACAAAAAcAAABs" + 110 "AgAACAAAAAcAAAB0AgAAEwAAAAgAAAAAAAAAAAAFAAQAAAAAAAUABQAAAAAABQAGAAAAAAADAAIA" + 111 "AAAAAAMAAwAAAAAAAAAZAAAABAAAABoAAAAFAAMAAwAAAAcAAwADAAAABwABABcAAAAHAAIAFwAA" + 112 "AAcAAAAaAAAAAAAAAAEAAAAFAAAAAAAAABEAAACQBAAAYwQAAAAAAAAFAAAAAgAAAGgCAAA2AAAA" + 113 "HAAAAG4QAwAAAAwAYgECAGICAABiAwEAIgQHAHAQBQAEAG4gBwAEABoAFABuIAcABABuIAYAFAAa" + 114 "AAAAbiAHAAQAbiAGACQAGgABAG4gBwAEAG4gBgA0ABoAFQBuIAcABABuEAgABAAMABEAAAAAAAAA" + 115 "AABgAgAAAQAAAA4AAAABAAEAAQAAAGQCAAAEAAAAcBAEAAAADgAIAA4ABwAOAA4ADgABAAAABQAA" + 116 "AAEAAAAGAAcsIEJBUjogAAcsIEJBWjogAAg8Y2xpbml0PgAGPGluaXQ+AANCQVIAA0JBWgADRk9P" + 117 "AAFMAAJMTAAYTGFydC9UZXN0MTk3OSRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTc5OwAiTGRhbHZp" + 118 "ay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xh" + 119 "c3M7ABFMamF2YS9sYW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0" + 120 "cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsADVRlc3QxOTc5LmphdmEACVRyYW5zZm9y" + 121 "bQABVgAGW0ZPTzogAAFdAAthY2Nlc3NGbGFncwAGYXBwZW5kAARuYW1lAA5zdGF0aWNUb1N0cmlu" + 122 "ZwAIdG9TdHJpbmcABXZhbHVlAHZ+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJtaW4t" + 123 "YXBpIjoxLCJzaGEtMSI6ImE4MzUyZjI1NDg4NTM2MmNjZDhkOTA5ZDM1MjljNjAwOTRkZDg5NmUi" + 124 "LCJ2ZXJzaW9uIjoiMS42LjIwLWRldiJ9AAICARsYAQIDAhYECRgXEgMAAwAACQEJAQkAiIAEtAQB" + 125 "gYAEyAQBCbgDAAAAAAAAAAIAAABUBAAAWgQAAIQEAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAA" + 126 "AAAAAQAAAB0AAABwAAAAAgAAAAkAAADkAAAAAwAAAAQAAAAIAQAABAAAAAMAAAA4AQAABQAAAAkA" + 127 "AABQAQAABgAAAAEAAACYAQAAASAAAAMAAAC4AQAAAyAAAAMAAABgAgAAARAAAAIAAABsAgAAAiAA" + 128 "AB0AAAB6AgAABCAAAAIAAABUBAAAACAAAAEAAABjBAAAAxAAAAIAAACABAAABiAAAAEAAACQBAAA" + 129 "ABAAAAEAAACgBAAA"); 130 131 public interface TRunnable { run()132 public void run() throws Exception; 133 } 134 doTest()135 public static void doTest() throws Exception { 136 final CountDownLatch cdl = new CountDownLatch(1); 137 final CountDownLatch continueLatch = new CountDownLatch(1); 138 // Make sure the transformed class is already loaded before we start running (and possibly 139 // compiling) the test thread. 140 System.out.println("Hitting class " + Transform.staticToString()); 141 Thread t = new Thread(() -> { 142 try { 143 // We don't want to read these in the same method here to ensure that no reference to 144 // Transform is active on this thread at the time the redefinition occurs. To accomplish 145 // this just run the code in a different method, which is good enough. 146 ((TRunnable)() -> { 147 System.out.println("Initial: " + Transform.staticToString()); 148 readReflective("Reading with reflection."); 149 System.out.println("Reading normally."); 150 System.out.println("Read BAR field: " + printGeneric(Transform.BAR)); 151 System.out.println("Read FOO field: " + printGeneric(Transform.FOO)); 152 }).run(); 153 cdl.countDown(); 154 continueLatch.await(); 155 // Now that redefinition has occurred without this frame having any references to the 156 // Transform class we want to make sure we have the correct offsets. 157 System.out.println("Redefined: " + Transform.staticToString()); 158 readReflective("Reading with reflection after possible modification."); 159 System.out.println("Reading normally after possible modification."); 160 System.out.println("Read FOO field: " + printGeneric(Transform.FOO)); 161 System.out.println("Read BAR field: " + printGeneric(Transform.BAR)); 162 } catch (Exception e) { 163 throw new Error(e); 164 } 165 }); 166 t.start(); 167 cdl.await(); 168 doRedefinition(); 169 continueLatch.countDown(); 170 t.join(); 171 } 172 } 173