use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.
the class ConstructorReflection method getDeclaredConstructor.
public static Constructor<?> getDeclaredConstructor(Class<?> clazz, Class<?>... parameters) throws NoSuchMethodException {
ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
if (cd == null || !cd.isReplaceable()) {
return clazz.getDeclaredConstructor(parameters);
}
String args = '(' + DescriptorUtils.classArrayToDescriptorString(parameters) + ')';
MethodData md = cd.getMethodData("<init>", args);
if (md == null) {
return clazz.getDeclaredConstructor(parameters);
}
switch(md.getType()) {
case NORMAL:
return clazz.getDeclaredConstructor(parameters);
case FAKE_CONSTRUCTOR:
try {
Class<?> c = clazz.getClassLoader().loadClass(md.getClassName());
return c.getDeclaredConstructor(parameters);
} catch (NoSuchMethodException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
throw new NoSuchMethodException();
}
use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.
the class ConstructorReflection method getDeclaredConstructors.
public static Constructor<?>[] getDeclaredConstructors(Class<?> clazz) {
try {
ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
if (cd == null || !cd.isReplaceable()) {
return clazz.getDeclaredConstructors();
}
Constructor<?>[] meth = clazz.getDeclaredConstructors();
List<Constructor<?>> visible = new ArrayList<>(meth.length);
for (int i = 0; i < meth.length; ++i) {
if (meth[i].getParameterTypes().length != 3 || !meth[i].getParameterTypes()[2].equals(ConstructorArgument.class)) {
visible.add(meth[i]);
}
}
for (MethodData i : cd.getMethods()) {
if (i.getType() == MemberType.FAKE_CONSTRUCTOR) {
Class<?> c = clazz.getClassLoader().loadClass(i.getClassName());
visible.add(i.getConstructor(c));
} else if (i.getType() == MemberType.REMOVED && i.getMethodName().equals("<init>")) {
Class<?> c = clazz.getClassLoader().loadClass(i.getClassName());
visible.remove(i.getConstructor(c));
}
}
Constructor<?>[] ret = new Constructor[visible.size()];
for (int i = 0; i < visible.size(); ++i) {
ret[i] = visible.get(i);
}
return ret;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.
the class FakeMethodCallManipulator method transformClass.
public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
if (!Fakereplace.isRetransformationStarted()) {
return false;
}
final Map<String, Set<Data>> knownFakeMethods = data.getManipulationData(loader);
// methods that are known to need a rewrite to a generated static method
final Map<Integer, Data> knownFakeMethodCallLocations = new HashMap<>();
// methods that may need a rewrite to a generated static method
final Map<Integer, AddedMethodInfo> potentialFakeMethodCallLocations = new HashMap<>();
// first we need to scan the constant pool looking for
// CONSTANT_method_info_ref structures
ConstPool pool = file.getConstPool();
for (int i = 1; i < pool.getSize(); ++i) {
// we have a method call
if (pool.getTag(i) == ConstPool.CONST_Methodref || pool.getTag(i) == ConstPool.CONST_InterfaceMethodref) {
String className, methodDesc, methodName;
if (pool.getTag(i) == ConstPool.CONST_Methodref) {
className = pool.getMethodrefClassName(i);
methodDesc = pool.getMethodrefType(i);
methodName = pool.getMethodrefName(i);
} else {
className = pool.getInterfaceMethodrefClassName(i);
methodDesc = pool.getInterfaceMethodrefType(i);
methodName = pool.getInterfaceMethodrefName(i);
}
if (methodName.equals("<clinit>") || methodName.equals("<init>")) {
continue;
}
boolean handled = false;
if (knownFakeMethods.containsKey(className)) {
for (Data data : knownFakeMethods.get(className)) {
if (methodName.equals(data.getMethodName()) && methodDesc.equals(data.getMethodDesc())) {
// store the location in the const pool of the method ref
knownFakeMethodCallLocations.put(i, data);
// we have found a method call
// now lets replace it
handled = true;
break;
}
}
}
if (loader != null && !handled && !className.equals(file.getName()) && Fakereplace.isClassReplaceable(className, loader)) {
// may be an added method
// if the field does not actually exist yet we just assume it is about to come into existence
// and rewrite it anyway
BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
if (data != null) {
boolean noClassData = false;
MethodData method = null;
try {
Class<?> mainClass = loader.loadClass(className);
Set<Class> allClasses = new HashSet<>();
addToAllClasses(mainClass, allClasses);
for (Class clazz : allClasses) {
data = ClassDataStore.instance().getBaseClassData(clazz.getClassLoader(), clazz.getName());
if (data == null) {
noClassData = true;
break;
}
method = data.getMethodOrConstructor(methodName, methodDesc);
if (method != null) {
break;
}
}
} catch (ClassNotFoundException e) {
noClassData = true;
}
if (!noClassData) {
if (method == null) {
// this is a new method
// lets deal with it
int methodNo = MethodIdentifierStore.instance().getMethodNumber(methodName, methodDesc);
potentialFakeMethodCallLocations.put(i, new AddedMethodInfo(methodNo, className, methodName, methodDesc));
} else if (!Modifier.isPublic(method.getAccessFlags())) {
boolean requiresVisibilityUpgrade = false;
if (Modifier.isPrivate(method.getAccessFlags())) {
requiresVisibilityUpgrade = true;
} else if (!Modifier.isProtected(method.getAccessFlags())) {
// we can't handle protected properly, because we need to know the class heirachy
// this is package local, so we check the package names
boolean thisDefault = !file.getName().contains(".");
boolean thatDefault = !className.contains(".");
if (thisDefault && !thatDefault) {
requiresVisibilityUpgrade = true;
} else if (thatDefault && !thisDefault) {
requiresVisibilityUpgrade = true;
} else if (!thatDefault) {
String thatPackage = className.substring(0, className.lastIndexOf("."));
String thisPackage = file.getName().substring(0, file.getName().lastIndexOf("."));
if (!thisPackage.equals(thatPackage)) {
requiresVisibilityUpgrade = true;
}
}
}
if (requiresVisibilityUpgrade) {
int methodNo = MethodIdentifierStore.instance().getMethodNumber(methodName, methodDesc);
potentialFakeMethodCallLocations.put(i, new AddedMethodInfo(methodNo, className, methodName, methodDesc));
}
}
}
}
}
}
}
// through the methods and replace instances of the call
if (!knownFakeMethodCallLocations.isEmpty() || !potentialFakeMethodCallLocations.isEmpty()) {
handleLambdas(file, knownFakeMethodCallLocations, pool);
List<MethodInfo> methods = file.getMethods();
for (MethodInfo m : methods) {
try {
// ignore abstract methods
if (m.getCodeAttribute() == null) {
continue;
}
CodeIterator it = m.getCodeAttribute().iterator();
while (it.hasNext()) {
// loop through the bytecode
int index = it.next();
int op = it.byteAt(index);
// if the bytecode is a method invocation
if (op == CodeIterator.INVOKEVIRTUAL || op == CodeIterator.INVOKESTATIC || op == CodeIterator.INVOKEINTERFACE || op == CodeIterator.INVOKESPECIAL) {
int val = it.s16bitAt(index + 1);
// replacing
if (potentialFakeMethodCallLocations.containsKey(val)) {
AddedMethodInfo methodInfo = potentialFakeMethodCallLocations.get(val);
Data data = new Data(methodInfo.className, methodInfo.name, methodInfo.desc, op == Opcode.INVOKESTATIC ? Type.STATIC : op == Opcode.INVOKEINTERFACE ? Type.INTERFACE : Type.VIRTUAL, loader, methodInfo.number, null);
handleFakeMethodCall(file, modifiedMethods, m, it, index, op, data);
} else if (knownFakeMethodCallLocations.containsKey(val)) {
Data data = knownFakeMethodCallLocations.get(val);
handleFakeMethodCall(file, modifiedMethods, m, it, index, op, data);
}
}
}
modifiedMethods.add(m);
m.getCodeAttribute().computeMaxStack();
} catch (Exception e) {
log.error("Bad byte code transforming " + file.getName(), e);
e.printStackTrace();
}
}
return true;
} else {
return false;
}
}
use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.
the class ConstructorReflection method newInstance.
@SuppressWarnings("restriction")
public static Object newInstance(Constructor<?> method, Object... args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
final MethodData data = ClassDataStore.instance().getMethodInformation(method.getDeclaringClass().getName());
final Class<?> info = ClassDataStore.instance().getRealClassFromProxyName(method.getDeclaringClass().getName());
try {
final Constructor<?> invoke = info.getConstructor(int.class, Object[].class, ConstructorArgument.class);
Object ar = args;
if (ar == null) {
ar = new Object[0];
}
if (!Modifier.isPublic(method.getModifiers()) && !method.isAccessible()) {
Class<?> caller = AccessVerification.getCallerClass(2);
AccessVerification.ensureMemberAccess(caller, method.getDeclaringClass(), method.getModifiers());
}
return invoke.newInstance(data.getMethodNo(), ar, null);
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.
the class ConstructorInvocationManipulator method transformClass.
public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
Map<String, Set<ConstructorRewriteData>> constructorRewrites = new HashMap<>(data.getManipulationData(loader));
Map<Integer, ConstructorRewriteData> methodCallLocations = new HashMap<>();
// first we need to scan the constant pool looking for
// CONSTANT_method_info_ref structures
ConstPool pool = file.getConstPool();
for (int i = 1; i < pool.getSize(); ++i) {
// we have a method call
if (pool.getTag(i) == ConstPool.CONST_Methodref) {
boolean handled = false;
String className = pool.getMethodrefClassName(i);
String methodDesc = pool.getMethodrefType(i);
String methodName = pool.getMethodrefName(i);
if (methodName.equals("<init>")) {
if (constructorRewrites.containsKey(className)) {
for (ConstructorRewriteData data : constructorRewrites.get(className)) {
if (methodDesc.equals(data.getMethodDesc())) {
// store the location in the const pool of the method ref
methodCallLocations.put(i, data);
// we have found a method call
// now lets replace it
handled = true;
break;
}
}
}
if (!handled && Fakereplace.isClassReplaceable(className, loader)) {
// may be an added field
// if the field does not actually exist yet we just assume it is about to come into existence
// and rewrite it anyway
BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
if (data != null) {
MethodData method = data.getMethodOrConstructor("<init>", methodDesc);
if (method == null) {
// this is a new method
// lets deal with it
int methodNo = MethodIdentifierStore.instance().getMethodNumber("<init>", methodDesc);
methodCallLocations.put(i, new ConstructorRewriteData(className, methodDesc, methodNo, loader));
}
}
}
}
}
}
// through the methods and replace instances of the call
if (!methodCallLocations.isEmpty()) {
List<MethodInfo> methods = file.getMethods();
for (MethodInfo m : methods) {
try {
// ignore abstract methods
if (m.getCodeAttribute() == null) {
continue;
}
CodeIterator it = m.getCodeAttribute().iterator();
while (it.hasNext()) {
// loop through the bytecode
int index = it.next();
int op = it.byteAt(index);
// if the bytecode is a method invocation
if (op == CodeIterator.INVOKESPECIAL) {
int val = it.s16bitAt(index + 1);
// replacing
if (methodCallLocations.containsKey(val)) {
ConstructorRewriteData data = methodCallLocations.get(val);
// so we currently have all the arguments sitting on the
// stack, and we need to jigger them into
// an array and then call our method. First thing to do
// is scribble over the existing
// instructions:
it.writeByte(CodeIterator.NOP, index);
it.writeByte(CodeIterator.NOP, index + 1);
it.writeByte(CodeIterator.NOP, index + 2);
Bytecode bc = new Bytecode(file.getConstPool());
ManipulationUtils.pushParametersIntoArray(bc, data.getMethodDesc());
// so now our stack looks like unconstructed instance : array
// we need unconstructed instance : int : array : null
bc.addIconst(data.getMethodNo());
bc.add(Opcode.SWAP);
bc.add(Opcode.ACONST_NULL);
bc.addInvokespecial(data.getClazz(), "<init>", Constants.ADDED_CONSTRUCTOR_DESCRIPTOR);
// and we have our bytecode
it.insert(bc.get());
modifiedMethods.add(m);
}
}
}
} catch (Exception e) {
log.error("Bad byte code transforming " + file.getName(), e);
}
}
return true;
} else {
return false;
}
}
Aggregations