/*
* Copyright 2016, Stuart Douglas, and individual contributors as indicated
* by the @authors tag.
*
* 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 org.fakereplace.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.Descriptor;
import org.fakereplace.core.Constants;
import org.fakereplace.data.ClassData;
import org.fakereplace.data.ClassDataStore;
import org.fakereplace.data.FieldData;
import org.fakereplace.data.MemberType;
import sun.reflect.Reflection;
/**
* Class that handles access to re-written fields.
*
* @author stuart
*/
public class FieldReflection {
public static Class<?> getDeclaringClass(Field f) {
Class<?> c = f.getDeclaringClass();
if (c.getName().startsWith(Constants.GENERATED_CLASS_PACKAGE)) {
return ClassDataStore.instance().getRealClassFromProxyName(c.getName());
}
return c;
}
public static Field[] getDeclaredFields(Class<?> clazz) {
if (!ClassDataStore.instance().isClassReplaced(clazz)) {
return clazz.getDeclaredFields();
}
try {
ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
Field[] meth = clazz.getDeclaredFields();
Collection<FieldData> fieldData = cd.getFields();
List<Field> visible = new ArrayList<Field>(meth.length);
for (int i = 0; i < meth.length; ++i) {
for (FieldData f : fieldData) {
if (f.getAccessFlags() == meth[i].getModifiers() && f.getName().equals(meth[i].getName())) {
if (f.getMemberType() == MemberType.NORMAL) {
visible.add(meth[i]);
break;
}
}
}
}
for (FieldData i : cd.getFields()) {
if (i.getMemberType() == MemberType.FAKE) {
Class<?> c = clazz.getClassLoader().loadClass(i.getClassName());
visible.add(i.getField(c));
}
}
Field[] ret = new Field[visible.size()];
for (int i = 0; i < visible.size(); ++i) {
ret[i] = visible.get(i);
}
return ret;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Field[] getFields(Class<?> clazz) {
if (!ClassDataStore.instance().isClassReplaced(clazz)) {
return clazz.getFields();
}
try {
ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
if (cd == null) {
return clazz.getDeclaredFields();
}
Field[] meth = clazz.getFields();
Collection<FieldData> fieldData = cd.getFields();
List<Field> visible = new ArrayList<Field>(meth.length);
for (int i = 0; i < meth.length; ++i) {
for (FieldData f : fieldData) {
if (f.getAccessFlags() == meth[i].getModifiers() && f.getName().equals(meth[i].getName())) {
if (f.getMemberType() == MemberType.NORMAL) {
visible.add(meth[i]);
break;
}
}
}
}
ClassData cta = cd;
while (cta != null) {
for (FieldData i : cta.getFields()) {
if (i.getMemberType() == MemberType.FAKE && AccessFlag.isPublic(i.getAccessFlags())) {
Class<?> c = clazz.getClassLoader().loadClass(i.getClassName());
visible.add(i.getField(c));
}
}
cta = cta.getSuperClassInformation();
}
Field[] ret = new Field[visible.size()];
for (int i = 0; i < visible.size(); ++i) {
ret[i] = visible.get(i);
}
return ret;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Field getField(Class<?> clazz, String name) throws NoSuchFieldException {
if (!ClassDataStore.instance().isClassReplaced(clazz)) {
return clazz.getField(name);
}
ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
if (cd == null) {
return clazz.getField(name);
}
FieldData fd = cd.getField(name);
if (fd == null) {
return clazz.getField(name);
}
if (!AccessFlag.isPublic(fd.getAccessFlags())) {
throw new NoSuchFieldException(clazz.getName() + "." + name);
}
switch (fd.getMemberType()) {
case NORMAL:
return clazz.getField(name);
case FAKE:
try {
Class<?> c = clazz.getClassLoader().loadClass(fd.getClassName());
return c.getField(name);
} catch (NoSuchFieldException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
throw new NoSuchFieldException();
}
public static Field getDeclaredField(Class<?> clazz, String name) throws NoSuchFieldException {
if (!ClassDataStore.instance().isClassReplaced(clazz)) {
return clazz.getDeclaredField(name);
}
ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
if (cd == null) {
return clazz.getDeclaredField(name);
}
FieldData fd = cd.getField(name);
if (fd == null) {
return clazz.getDeclaredField(name);
}
switch (fd.getMemberType()) {
case NORMAL:
return clazz.getDeclaredField(name);
case FAKE:
try {
Class<?> c = clazz.getClassLoader().loadClass(fd.getClassName());
return c.getDeclaredField(name);
} catch (NoSuchFieldException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
throw new NoSuchFieldException();
}
public static void set(Field f, Object object, Object val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static void setBoolean(Field f, Object object, boolean val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static void setByte(Field f, Object object, byte val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static void setChar(Field f, Object object, char val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static void setDouble(Field f, Object object, double val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static void setFloat(Field f, Object object, float val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static void setInt(Field f, Object object, int val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static void setLong(Field f, Object object, long val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static void setShort(Field f, Object object, short val) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
accessor.set(object, val);
}
public static Object get(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return accessor.get(object);
}
public static boolean getBoolean(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return (Boolean) accessor.get(object);
}
public static byte getByte(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return (Byte) accessor.get(object);
}
public static char getChar(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return (Character) accessor.get(object);
}
public static Double getDouble(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return (Double) accessor.get(object);
}
public static float getFloat(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return (Float) accessor.get(object);
}
public static int getInt(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return (Integer) accessor.get(object);
}
public static long getLong(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return (Long) accessor.get(object);
}
public static Object getShort(Field f, Object object) throws IllegalAccessException {
FieldAccessor accessor = ClassDataStore.instance().getFieldAccessor(f.getDeclaringClass().getName());
if (!Modifier.isPublic(f.getModifiers()) && !f.isAccessible()) {
Class<?> caller = findCallerClass();
Reflection.ensureMemberAccess(caller, accessor.getDeclaringClass(), null, f.getModifiers());
}
return (Short) accessor.get(object);
}
private static Class findCallerClass() {
Class<?> c = Reflection.getCallerClass(3);
if(c == FieldReflection.class) {
return Reflection.getCallerClass(4);
}
return c;
}
public static boolean isFakeField(Field f) {
if (f.getDeclaringClass().getName().startsWith(org.fakereplace.core.Constants.GENERATED_CLASS_PACKAGE)) {
return true;
}
return false;
}
}