libRefTools/src/com/tofvesson/reflection/SafeReflection.java
Gabriel Tofvesson f63b5beb93 Major update
- Updated code to support java 9 language level
- Moved field definitions in SafeReflection to clinit
2016-12-21 02:32:45 +01:00

429 lines
18 KiB
Java

package com.tofvesson.reflection;
import sun.misc.Unsafe;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
/**
* Safe tools to help simplify code when dealing with reflection.
*/
@SuppressWarnings("unused")
public class SafeReflection {
private static final Unsafe unsafe;
private static final Method newInstance, aConAccess, gFieldAccess, fieldAccess;
private static final Field ro;
private static final long override;
static{
Unsafe u = null;
Method m = null, m1 = null, m2 = null, m3 = null;
Field f = null;
long l = 0;
try{
Class<?> c;
f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
u = (Unsafe) f.get(null);
m = Class.forName("jdk.internal.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", Object[].class);
u.putBoolean(m, l=u.objectFieldOffset(AccessibleObject.class.getDeclaredField("override")), true);
m1 = Constructor.class.getDeclaredMethod("acquireConstructorAccessor");
m1.setAccessible(true);
m2 = Field.class.getDeclaredMethod("getFieldAccessor", Object.class);
m2.setAccessible(true);
m3 = (c=Class.forName("jdk.internal.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl")).getDeclaredMethod("set", Object.class, Object.class);
u.putBoolean(m3, l, true);
f = c.getSuperclass().getDeclaredField("isReadOnly");
u.putBoolean(f, l, true);
}catch(Exception ignored){ ignored.printStackTrace(); } // Exception is never thrown
unsafe = u;
newInstance = m;
aConAccess = m1;
gFieldAccess = m2;
fieldAccess = m3;
ro = f;
override = l;
}
/**
* Gets the constructor from the defined class with the specified parameters.
* @param c Class to get constructor from.
* @param params Definition of parameters that the constructor requires.
* @param <T> Return-type of constructor.
* @return Constructor with specified parameter requirements or null if constructor doesn't exist.
*/
public static <T> Constructor<T> getConstructor(Class<T> c, Class<?>... params){
try{
Constructor<T> c1 = c.getConstructor(params);
unsafe.putBoolean(c1, override, true);
return c1;
}catch(Exception e){}
return null;
}
/**
* Gets the first constructor available from the given class.
* @param c Class to get constructor from.
* @param <T> Return-type of constructor.
* @return Constructor or null if something goes horribly wrong.
*/
public static <T> Constructor<T> getFirstConstructor(Class<T> c){
try {
@SuppressWarnings("unchecked")
Constructor<T> c1 = (Constructor<T>) c.getDeclaredConstructors()[0];
unsafe.putBoolean(c1, override, true);
return c1;
}catch (Exception e){}
return null;
}
/**
* Gets the method from the defined class by name and parameters.
* Method is accessible.
* @param c Class to find method in.
* @param name Name of method.
* @param params Parameters of method.
* @return Method or null if specified method wasn't found.
*/
public static Method getMethod(Class<?> c, String name, Class<?>... params){
try{
Method m = c.getDeclaredMethod(name, params);
unsafe.putBoolean(m, override, true);
return m;
}catch(Exception e){}
return null;
}
/**
* Attempts to invoke a supplied static method with the given parameters.
* @param m Method to invoke.
* @param params Parameters to supply to method.
* @return Return value of method or null if method is null.
*/
public static Object invokeStaticMethod(Method m, Object... params){
try{ return invokeMethod(null, m, params); }catch(Exception e){}
return null;
}
/**
* Attempts to invoke a supplied method with the given parameters on the supplied object.
* @param inst Object to invoke method in.
* @param m Method to invoke.
* @param params Parameters to supply to method.
* @return Return value of method or null if method is null.
*/
public static Object invokeMethod(Object inst, Method m, Object... params){
if(m!=null) unsafe.putBoolean(m, override, true);
try{ return m!=null?m.invoke(inst, params):null; }catch(Exception e){}
return null;
}
/**
* Finds the first method available with the specified name from the class.
* Meant to be used in cases where a class only has one version of a method.
* Method is accessible.
* @param c Class to find method in.
* @param name Name of method.
* @return Method or null if no method with given name exists.
*/
public static Method getFirstMethod(Class<?> c, String name){
try{
Method[] m = c.getDeclaredMethods();
for (Method aM : m) if(aM.getName().equals(name)){ unsafe.putBoolean(aM, override, true); return aM;}
}catch(Exception e){}
return null;
}
/**
* Gets field object referring to field with specified name in defined class.
* @param c Class to find field in.
* @param name Name of field.
* @return Field or null if no field with specified name exists.
*/
public static Field getField(Class<?> c, String name){
try{
Field f = c.getDeclaredField(name);
unsafe.putBoolean(f, override, true);
return f;
}catch(Exception e){}
return null;
}
/**
* Gets the static object stored in the field with the specified name in the defined class.
* @param c Class to find object in.
* @param name Name of field.
* @return Object or null if object is null or field doesn't exist.
*/
public static Object getStaticFieldObject(Class<?> c, String name){
Field f = getField(c, name);
try { return f!=null?f.get(null):null; } catch (Exception e) { }
return null;
}
/**
* Gets the object stored in the field with the specified name in the class of the defined object.
* @param o Object to find object in.
* @param name Name of field.
* @return Object or null if object is null or field doesn't exist.
*/
public static Object getFieldObject(Object o, String name){
Field f = getField(o.getClass(), name);
try{ return f!=null?f.get(o):null; }catch(Exception e){}
return null;
}
/**
* Gets a reference to the enclosing class from a defined inner (nested) object.
* @param nested Object instance of a nested class.
* @return "this" reference to the outer class or null if class of object instance is static or isn't nested.
*/
public static Object getEnclosingClassObjectRef(Object nested){
try{
Field f = nested.getClass().getDeclaredField("this$0");
unsafe.putBoolean(f, override, true);
return f.get(nested);
}catch(Exception e){}
return null;
}
/**
* Checks whether a given class is an inner (nested) class.
* @param c Class to check.
* @return True if class is nested otherwise false.
*/
public static boolean isNestedClass(Class<?> c){ return c.getEnclosingClass()!=null; }
/**
* Get class file based on class name.
* @param c Class to find file of.
* @return File if class file exists.
*/
public static java.io.File getClassFile(Class c){ return new java.io.File(c.getResource(c.getSimpleName()+".class").getFile()); }
/**
* Allows you to create a completely custom Enum value. If the supplied value already exists, it will be returned.
* will not be created; the value of the existing one will be updated.
* @param clazz The class to attribute the new enum to.
* @param addToValuesArray Whether or not to update the internal, "immutable" values array with the new value (ignored if value already exists).
* @return A new/existing enum.
*/
@SuppressWarnings("unchecked")
public static <T extends Enum<T>> T customEnum(Class<T> clazz, boolean addToValuesArray, String name, EnumDefinition def){
T u;
try {
// Get a reference to the static method values() and get values
Method v = clazz.getDeclaredMethod("values");
unsafe.putBoolean(v, override, true);
T[] values = (T[]) v.invoke(null);
// Return object if it already exists
for(T u2 : values) if(u2.name().equals(name)) return u2;
// Generate enum parameter definition
Class[] paramList = new Class[def.params.size()+2];
paramList[0] = String.class; // Name
paramList[1] = int.class; // Ordinal
int iterator = paramList.length;
for(Class c : def.params.values()) paramList[--iterator] = c; // Shit's fucking reversed
// Get enum constructor (inherited from Enum.class)
Constructor c = clazz.getDeclaredConstructor(paramList);
unsafe.putBoolean(c, override, true);
// Get constructor accessor since Constructor.newInstance throws an exception because Enums are "immutable"
Object access = aConAccess.invoke(c);
Object[] parameters = new Object[def.params.size()+2];
parameters[0] = name;
parameters[1] = values.length;
iterator = parameters.length;
for(Object o : def.params.keySet()) parameters[--iterator] = o;
// Create new instance of enum with valid name and ordinal
u = (T) newInstance.invoke(access, (Object) parameters);
// Get the final name field from Enum.class and make it temporarily modifiable
Field f = Enum.class.getDeclaredField("name");
f.setAccessible(true);
// Rename the newly created enum to the requested name
f.set(u, name);
if(!addToValuesArray) return u; // Stops here if
// Get the current values field from Enum (a bitch to modify)
f = clazz.getDeclaredField("$VALUES");
f.setAccessible(true);
T[] $VALUES = (T[]) Array.newInstance(clazz, values.length+1);
System.arraycopy(values, 0, $VALUES, 0, values.length); // Copy over values from old array
$VALUES[values.length] = u; // Add out custom enum to our local array
// Start doing magic by getting an instance of sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.class
// Class is package-local so we can't reference it by anything other than Object
Object UQSOFAImpl = gFieldAccess.invoke(f, u);
// Get "isReadOnly" flag ($VALUES is always read-only even if Field.setAccessible(true) is called)
// Flag is located in superclass (sun.reflect.UnsafeQualifiedStaticFieldAccessorImpl.class (also fucking package-local))
// Set flag to 'false' to allow for modification against Java's will
ro.setBoolean(UQSOFAImpl, false);
// Invoke set() method on UnsafeQualifiedStaticObjectFieldAccessorImpl object which sets the
// private field $VALUES to our new array
System.out.println(UQSOFAImpl.getClass());
fieldAccess.invoke(UQSOFAImpl, f, $VALUES);
} catch (Exception wrongParams) { throw new RuntimeException(wrongParams); }
return u;
}
/**
* Create a new object without the hassle of having to construct it. WARNING: Not usually a good idea.
* @param clazz Class to instantiate.
* @return An object instance of the supplied class that hasn't been constructed.
* @throws InstantiationException Thrown if instantiating the object fails for some reason.
*/
public static <T> T createNewObject(Class<T> clazz) throws InstantiationException{
//noinspection unchecked
return (T) unsafe.allocateInstance(clazz);
}
/**
* A definition for custom enum creation.
*/
public static final class EnumDefinition {
HashMap<Object, Class> params = new HashMap<Object, Class>(); // Assign a specific type to each parameter
public EnumDefinition(){}
public EnumDefinition(Object... params){ for(Object o : params) putObject(o); }
/**
* Put an object in the parameter list.
* @param value The parameter to supply. (Type is derived automatically)
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putObject(Object value){
params.put(value, value.getClass());
return this;
}
/**
* Put a primitive value in the parameter list.
* @param autoBoxed An autoboxed version of the parameter. (For example putPrimitive(5) will automatically become Integer)
* @return A reference to the EnumDefinition object this method was called on (for chaining).
* @throws NotAutoBoxedException Thrown if a value expected to be autoboxed isn't autoboxed.
*/
public EnumDefinition putPrimitive(Object autoBoxed) throws NotAutoBoxedException{
// All autoboxed versions of primitives have a reference to their boxed primitive
try {
params.put(autoBoxed, autoBoxed.getClass().getDeclaredField("value").getType());
}catch(Exception e){ throw new NotAutoBoxedException(); }
return this;
}
/**
* Safe wrapper of {@link #putPrimitive(Object)} for boolean.
* @param b Boolean parameter.
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putBoolean(boolean b){
try { return putPrimitive(b); } catch (NotAutoBoxedException e) { }
return this;
}
/**
* Safe wrapper of {@link #putPrimitive(Object)} for byte.
* @param b Byte parameter.
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putByte(byte b){
try { return putPrimitive(b); } catch (NotAutoBoxedException e) { }
return this;
}
/**
* Safe wrapper of {@link #putPrimitive(Object)} for char.
* @param c Character parameter.
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putChar(char c){
try { return putPrimitive(c); } catch (NotAutoBoxedException e) { }
return this;
}
/**
* Safe wrapper of {@link #putPrimitive(Object)} for short.
* @param s Short parameter.
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putShort(short s){
try { return putPrimitive(s); } catch (NotAutoBoxedException e) { }
return this;
}
/**
* Safe wrapper of {@link #putPrimitive(Object)} for int.
* @param i Integer parameter.
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putInt(int i){
try { return putPrimitive(i); } catch (NotAutoBoxedException e) { }
return this;
}
/**
* Safe wrapper of {@link #putPrimitive(Object)} for long.
* @param l Long parameter.
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putLong(long l){
try { return putPrimitive(l); } catch (NotAutoBoxedException e) { }
return this;
}
/**
* Safe wrapper of {@link #putPrimitive(Object)} for float.
* @param f Floating point parameter.
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putFloat(float f){
try { return putPrimitive(f); } catch (NotAutoBoxedException e) { }
return this;
}
/**
* Safe wrapper of {@link #putPrimitive(Object)} for double.
* @param d Double parameter.
* @return A reference to the EnumDefinition object this method was called on (for chaining).
*/
public EnumDefinition putDouble(double d){
try { return putPrimitive(d); } catch (NotAutoBoxedException e) { }
return this;
}
}
public static Class<?> getCallerClass(){
ArrayList<StackTraceElement> s = new ArrayList<StackTraceElement>();
StackTraceElement[] s1 = new Exception().getStackTrace();
Collections.addAll(s, s1);
s.remove(0);
Iterator<StackTraceElement> i = s.iterator();
String s2;
while(i.hasNext()){
if((s2=i.next().toString()).contains("java.lang.reflect.Method.invoke")
|| s2.contains("sun.reflect.NativeMethodAccessorImpl.invoke")
|| s2.contains("sun.reflect.DelegatingMethodAccessorImpl.invoke"))
i.remove();
}
try { return Class.forName(s.get(s.size()==1?0:1).getClassName()); } catch (ClassNotFoundException e) { }
assert false:"Unreachable code reached";
return null;
}
}