/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package art; import java.lang.invoke.*; import java.lang.ref.*; import java.lang.reflect.*; import java.util.*; public class Test1981 { // Allow us to hide the var-handle portions when running this on CTS. public interface VarHandler { public boolean doVarHandleTests(); public default Object findStaticVarHandle(MethodHandles.Lookup l, Class c, String n, Class t) throws Throwable { return null; } public default Object get(Object vh) throws Throwable { throw new Error("Illegal call!"); } public default void set(Object vh, Object v) throws Throwable { throw new Error("Illegal call!"); } public default boolean instanceofVarHandle(Object v) { return false; } public default Object getVarTypeName(Object v) { throw new Error("Illegal call!"); } } // CTS Entrypoint. public static void run() throws Exception { run(() -> false); } public static void run(VarHandler varhandle_portion) throws Exception { Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); doTest(varhandle_portion); } private static final boolean PRINT_NONDETERMINISTIC = false; public static WeakHashMap id_nums = new WeakHashMap<>(); public static long next_id = 0; public static String printGeneric(VarHandler vh, Object o) { Long id = id_nums.get(o); if (id == null) { id = Long.valueOf(next_id++); id_nums.put(o, id); } if (o == null) { return "(ID: " + id + ") "; } Class oc = o.getClass(); if (oc.isArray() && oc.getComponentType() == Byte.TYPE) { return "(ID: " + id + ") " + Arrays.toString(Arrays.copyOf((byte[]) o, 10)).replace(']', ',') + " ...]"; } else if (vh.instanceofVarHandle(o)) { // These don't have a good to-string. Give them one. return "(ID: " + id + ") " + o.getClass().getName() + "()->" + vh.getVarTypeName(o); } else { return "(ID: " + id + ") " + o.toString(); } } public static class Transform { static { } private static Object BAR = new Object() { public String toString() { return "value of <" + this.get() + ">"; } public Object get() { return "BAR FIELD"; } }; private static Object FOO = new Object() { public String toString() { return "value of <" + this.get() + ">"; } public Object get() { return "FOO FIELD"; } }; public static MethodHandles.Lookup getLookup() { return MethodHandles.lookup(); } public static String staticToString() { return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + "]"; } } /* Base64 encoded class of: * public static class Transform { * static {} * // NB This is the order the fields will be laid out in memory. * private static Object BAR; * private static Object BAZ; * private static Object FOO; * public static MethodHandles.Lookup getLookup() { return null; } * private static void reinitialize() { * BAZ = 42; * } * public static String staticToString() { * return Transform.class.toString() + "[FOO: " + FOO + ", BAR: " + BAR + ", BAZ: " + BAZ + "]"; * } * } */ private static byte[] REDEFINED_DEX_BYTES = Base64.getDecoder() .decode( "ZGV4CjAzNQDY+Vd3k8SVBE6A35RavIBzYN76h51YIqVwBgAAcAAAAHhWNBIAAAAAAAAAAKwFAAAk" + "AAAAcAAAAAwAAAAAAQAABgAAADABAAADAAAAeAEAAAwAAACQAQAAAQAAAPABAABgBAAAEAIAABoD" + "AAAjAwAALAMAADYDAAA+AwAAQwMAAEgDAABNAwAAUAMAAFMDAABXAwAAWwMAAHUDAACFAwAAqQMA" + "AMkDAADcAwAA8QMAAAUEAAAZBAAANAQAAF0EAABsBAAAdwQAAHoEAACCBAAAhQQAAJIEAACaBAAA" + "pQQAAKsEAAC5BAAAyQQAANMEAADaBAAA4wQAAAcAAAALAAAADAAAAA0AAAAOAAAADwAAABAAAAAR" + "AAAAEgAAABMAAAAUAAAAFwAAAAkAAAAGAAAABAMAAAgAAAAIAAAAAAAAAAoAAAAJAAAADAMAAAoA" + "AAAJAAAAFAMAAAgAAAAKAAAAAAAAABcAAAALAAAAAAAAAAEABwAEAAAAAQAHAAUAAAABAAcABgAA" + "AAEABQACAAAAAQAFAAMAAAABAAQAHAAAAAEABQAeAAAAAQABAB8AAAAFAAEAIAAAAAYAAAAiAAAA" + "BwAFAAMAAAAJAAUAAwAAAAkAAgAbAAAACQADABsAAAAJAAEAIAAAAAEAAAABAAAABwAAAAAAAAAV" + "AAAAnAUAAGoFAAAAAAAABQAAAAIAAAD/AgAANgAAABwAAQBuEAUAAAAMAGIBAgBiAgAAYgMBACIE" + "CQBwEAgABABuIAoABAAaABgAbiAKAAQAbiAJABQAGgAAAG4gCgAEAG4gCQAkABoAAQBuIAoABABu" + "IAkANAAaABkAbiAKAAQAbhALAAQADAARAAEAAAAAAAAA9gIAAAIAAAASABEAAAAAAAAAAADuAgAA" + "AQAAAA4AAAABAAEAAQAAAPICAAAEAAAAcBAHAAAADgABAAAAAQAAAPoCAAAJAAAAEwAqAHEQBgAA" + "AAwAaQABAA4ACgAOAAkADgAPAA4AEQAOhwAUAA4AAAEAAAAAAAAAAQAAAAcAAAABAAAACAAHLCBC" + "QVI6IAAHLCBCQVo6IAAIPGNsaW5pdD4ABjxpbml0PgADQkFSAANCQVoAA0ZPTwABSQABTAACTEkA" + "AkxMABhMYXJ0L1Rlc3QxOTgxJFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5ODE7ACJMZGFsdmlrL2Fu" + "bm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJDbGFzczsA" + "EUxqYXZhL2xhbmcvQ2xhc3M7ABNMamF2YS9sYW5nL0ludGVnZXI7ABJMamF2YS9sYW5nL09iamVj" + "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwAnTGphdmEv" + "bGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlcyRMb29rdXA7AA1UZXN0MTk4MS5qYXZhAAlUcmFuc2Zv" + "cm0AAVYABltGT086IAABXQALYWNjZXNzRmxhZ3MABmFwcGVuZAAJZ2V0TG9va3VwAARuYW1lAAxy" + "ZWluaXRpYWxpemUADnN0YXRpY1RvU3RyaW5nAAh0b1N0cmluZwAFdmFsdWUAB3ZhbHVlT2YAdn5+" + "RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiYTgzNTJm" + "MjU0ODg1MzYyY2NkOGQ5MDlkMzUyOWM2MDA5NGRkODk2ZSIsInZlcnNpb24iOiIxLjYuMjAtZGV2" + "In0AAgMBIRgCAgQCGgQJHRcWAwAFAAAKAQoBCgCIgASgBQGBgAS0BQEJjAUBCswFAQmQBAAAAAAC" + "AAAAWwUAAGEFAACQBQAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAkAAAAcAAAAAIA" + "AAAMAAAAAAEAAAMAAAAGAAAAMAEAAAQAAAADAAAAeAEAAAUAAAAMAAAAkAEAAAYAAAABAAAA8AEA" + "AAEgAAAFAAAAEAIAAAMgAAAFAAAA7gIAAAEQAAADAAAABAMAAAIgAAAkAAAAGgMAAAQgAAACAAAA" + "WwUAAAAgAAABAAAAagUAAAMQAAACAAAAjAUAAAYgAAABAAAAnAUAAAAQAAABAAAArAUAAA=="); public static void doTest(VarHandler vh) throws Exception { try { System.out.println("Initial: " + Transform.staticToString()); MethodHandles.Lookup lookup = Transform.getLookup(); String[] names = new String[] { "FOO", "BAR", }; MethodHandle[] handles = new MethodHandle[] { lookup.findStaticGetter(Transform.class, "FOO", Object.class), lookup.findStaticGetter(Transform.class, "BAR", Object.class), }; Object foo_handle = vh.findStaticVarHandle(lookup, Transform.class, "FOO", Object.class); Object[] var_handles = new Object[] { foo_handle, vh.findStaticVarHandle(lookup, Transform.class, "BAR", Object.class), }; for (int i = 0; i < names.length; i++) { System.out.println( "Reading field " + names[i] + " using " + printGeneric(vh, handles[i]) + " = " + printGeneric(vh, handles[i].invoke())); if (vh.doVarHandleTests()) { System.out.println( "Reading field " + names[i] + " using " + printGeneric(vh, var_handles[i]) + " = " + printGeneric(vh, vh.get(var_handles[i]))); } } MethodHandle old_field_write = lookup.findStaticSetter(Transform.class, "FOO", Object.class); System.out.println("Redefining Transform class"); Redefinition.doCommonStructuralClassRedefinition(Transform.class, REDEFINED_DEX_BYTES); System.out.println("Post redefinition : " + Transform.staticToString()); String[] new_names = new String[] { "BAZ", "FOO", "BAR", }; MethodHandle[] new_handles = new MethodHandle[] { lookup.findStaticGetter(Transform.class, "BAZ", Object.class), lookup.findStaticGetter(Transform.class, "FOO", Object.class), lookup.findStaticGetter(Transform.class, "BAR", Object.class), }; Object baz_handle = vh.findStaticVarHandle(lookup, Transform.class, "BAZ", Object.class); Object[] new_var_handles = new Object[] { baz_handle, vh.findStaticVarHandle(lookup, Transform.class, "FOO", Object.class), vh.findStaticVarHandle(lookup, Transform.class, "BAR", Object.class), }; for (int i = 0; i < names.length; i++) { System.out.println( "Reading field " + names[i] + " using " + printGeneric(vh, handles[i]) + " = " + printGeneric(vh, handles[i].invoke())); if (vh.doVarHandleTests()) { System.out.println( "Reading field " + names[i] + " using " + printGeneric(vh, var_handles[i]) + " = " + printGeneric(vh, vh.get(var_handles[i]))); } } for (int i = 0; i < new_names.length; i++) { System.out.println( "Reading new field " + new_names[i] + " using " + printGeneric(vh, new_handles[i]) + " = " + printGeneric(vh, new_handles[i].invoke())); if (vh.doVarHandleTests()) { System.out.println( "Reading new field " + new_names[i] + " using " + printGeneric(vh, new_var_handles[i]) + " = " + printGeneric(vh, vh.get(new_var_handles[i]))); } } String val = "foo"; System.out.println("Setting BAZ to " + printGeneric(vh, val) + " with new mh."); lookup.findStaticSetter(Transform.class, "BAZ", Object.class).invoke(val); System.out.println("Post set with new mh: " + Transform.staticToString()); System.out.println("Setting FOO to " + printGeneric(vh, Transform.class) + " with old mh."); old_field_write.invoke(Transform.class); System.out.println("Post set with old mh: " + Transform.staticToString()); Object new_val = new Object() { public String toString() { return "new_value object"; } }; if (vh.doVarHandleTests()) { System.out.println("Setting FOO to '" + printGeneric(vh, new_val) + "' with old varhandle."); vh.set(foo_handle, new_val); System.out.println("Post set with new varhandle: " + Transform.staticToString()); System.out.println("Setting BAZ to 'bar' with new varhandle."); vh.set(baz_handle, "bar"); System.out.println("Post set with old varhandle: " + Transform.staticToString()); } System.out.println("Using mh to call new private method."); MethodHandle reinit = lookup.findStatic(Transform.class, "reinitialize", MethodType.methodType(Void.TYPE)); reinit.invoke(); System.out.println("Post reinit with mh: " + Transform.staticToString()); for (int i = 0; i < names.length; i++) { System.out.println( "Reading field " + names[i] + " using " + printGeneric(vh, handles[i]) + " = " + printGeneric(vh, handles[i].invoke())); if (vh.doVarHandleTests()) { System.out.println( "Reading field " + names[i] + " using " + printGeneric(vh, var_handles[i]) + " = " + printGeneric(vh, vh.get(var_handles[i]))); } } for (int i = 0; i < new_names.length; i++) { System.out.println( "Reading new field " + new_names[i] + " using " + printGeneric(vh, new_handles[i]) + " = " + printGeneric(vh, new_handles[i].invoke())); if (vh.doVarHandleTests()) { System.out.println( "Reading new field " + new_names[i] + " using " + printGeneric(vh, new_var_handles[i]) + " = " + printGeneric(vh, vh.get(new_var_handles[i]))); } } } catch (Throwable t) { if (t instanceof Exception) { throw (Exception) t; } else if (t instanceof Error) { throw (Error) t; } else { throw new RuntimeException("Unexpected throwable!", t); } } } }