blob: d76687641297031b441e5bcf319dce5ebc4a4c9a [file] [log] [blame]
/*
* Copyright (C) 2017 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.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Vector;
import java.util.function.Function;
public class Test990 {
// Fields of these classes are accessed/modified differently in the RI and ART so we ignore them.
static Collection<Class<?>> IGNORED_CLASSES = Arrays.asList(new Class<?>[] {
ClassLoader.class,
Vector.class,
});
static interface Printable { public void Print(); }
static final class FieldWrite implements Printable {
private Executable method;
private Object target;
private Field f;
private String initialValue;
private Class<?> initialValueType;
public FieldWrite(Executable method, Object target, Field f, Object v) {
this.method = method;
this.target = target;
this.f = f;
this.initialValue = genericToString(v);
this.initialValueType = v != null ? v.getClass() : null;
}
@Override
public void Print() {
System.out.println("MODIFY of " + f + " on object of" +
" type: " + (target == null ? null : target.getClass()) +
" in method " + method +
". New value: " + initialValue + " (type: " + initialValueType + ")");
}
}
static final class FieldRead implements Printable {
private Executable method;
private Object target;
private Field f;
public FieldRead(Executable method, Object target, Field f) {
this.method = method;
this.target = target;
this.f = f;
}
@Override
public void Print() {
System.out.println("ACCESS of " + f + " on object of" +
" type " + (target == null ? null : target.getClass()) +
" in method " + method);
}
}
private static String genericToString(Object val) {
if (val == null) {
return "null";
} else if (val.getClass().isArray()) {
return arrayToString(val);
} else if (val instanceof Throwable) {
StringWriter w = new StringWriter();
((Throwable) val).printStackTrace(new PrintWriter(w));
return w.toString();
} else {
return val.toString();
}
}
private static String charArrayToString(char[] src) {
String[] res = new String[src.length];
for (int i = 0; i < src.length; i++) {
if (Character.isISOControl(src[i])) {
res[i] = Character.getName(src[i]);
} else {
res[i] = Character.toString(src[i]);
}
}
return Arrays.toString(res);
}
private static String arrayToString(Object val) {
Class<?> klass = val.getClass();
if ((new Object[0]).getClass().isAssignableFrom(klass)) {
return Arrays.toString(
Arrays.stream((Object[])val).map(new Function<Object, String>() {
public String apply(Object o) {
return genericToString(o);
}
}).toArray());
} else if ((new byte[0]).getClass().isAssignableFrom(klass)) {
return Arrays.toString((byte[])val);
} else if ((new char[0]).getClass().isAssignableFrom(klass)) {
return charArrayToString((char[])val);
} else if ((new short[0]).getClass().isAssignableFrom(klass)) {
return Arrays.toString((short[])val);
} else if ((new int[0]).getClass().isAssignableFrom(klass)) {
return Arrays.toString((int[])val);
} else if ((new long[0]).getClass().isAssignableFrom(klass)) {
return Arrays.toString((long[])val);
} else if ((new float[0]).getClass().isAssignableFrom(klass)) {
return Arrays.toString((float[])val);
} else if ((new double[0]).getClass().isAssignableFrom(klass)) {
return Arrays.toString((double[])val);
} else {
throw new Error("Unknown type " + klass);
}
}
private static List<Printable> results = new ArrayList<>();
public static void notifyFieldModify(
Executable m, long location, Class<?> f_klass, Object target, Field f, Object value) {
if (IGNORED_CLASSES.contains(f_klass)) {
return;
}
results.add(new FieldWrite(m, target, f, value));
}
public static void notifyFieldAccess(
Executable m, long location, Class<?> f_klass, Object target, Field f) {
if (IGNORED_CLASSES.contains(f_klass)) {
return;
}
results.add(new FieldRead(m, target, f));
}
static class TestClass1 {
Object abc;
int xyz;
int foobar;
public TestClass1(int xyz, Object abc) {
this.xyz = xyz;
this.abc = abc;
}
public void tweak(int def) {
if (def == xyz) {
foobar++;
}
}
public String toString() {
return "TestClass1 { abc: \"" + genericToString(abc) + "\", xyz: " + xyz
+ ", foobar: " + foobar + " }";
}
}
static class TestClass2 extends TestClass1 {
static long TOTAL = 0;
long baz;
public TestClass2(long baz) {
super(1337, "TESTING");
this.baz = baz;
}
public void tweak(int def) {
TOTAL++;
super.tweak(def);
baz++;
}
public String toString() {
return "TestClass2 { super: \"%s\", TOTAL: %d, baz: %d }".format(
super.toString(), TOTAL, baz);
}
}
public static void run() throws Exception {
Trace.disableTracing(Thread.currentThread());
Trace.enableFieldTracing(
Test990.class,
Test990.class.getDeclaredMethod("notifyFieldAccess",
Executable.class, Long.TYPE, Class.class, Object.class, Field.class),
Test990.class.getDeclaredMethod("notifyFieldModify",
Executable.class, Long.TYPE, Class.class, Object.class, Field.class, Object.class),
Thread.currentThread());
Trace.watchAllFieldAccesses();
Trace.watchAllFieldModifications();
TestClass1 t1 = new TestClass1(1, "tc1");
TestClass1 t2 = new TestClass2(2);
TestClass1 t3 = new TestClass1(3, t1);
TestClass1 t4 = new TestClass1(4, t2);
t1.tweak(1);
t1.tweak(1);
t2.tweak(12);
t2.tweak(1337);
t2.tweak(12);
t2.tweak(1338);
t1.tweak(t3.foobar);
t4.tweak((int)((TestClass2)t2).baz);
t4.tweak((int)TestClass2.TOTAL);
t2.tweak((int)TestClass2.TOTAL);
// Turn off tracing so we don't have to deal with print internals.
Trace.disableTracing(Thread.currentThread());
printResults();
}
public static void printResults() {
for (Printable p : results) {
p.Print();
}
}
}