blob: ac8a01904a6a5fa48687729723b396ae21a1deec [file] [log] [blame]
/*
* 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<Object, Long> 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 + ") <NULL>";
}
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);
}
}
}
}