/* * 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.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; import org.fakereplace.core.Constants; import org.fakereplace.core.ConstructorArgument; import org.fakereplace.data.ClassData; import org.fakereplace.data.ClassDataStore; import org.fakereplace.data.MemberType; import org.fakereplace.data.MethodData; import org.fakereplace.util.DescriptorUtils; import sun.reflect.Reflection; public class ConstructorReflection { @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 = sun.reflect.Reflection.getCallerClass(2); Reflection.ensureMemberAccess(caller, method.getDeclaringClass(), null, method.getModifiers()); } return invoke.newInstance(data.getMethodNo(), ar, null); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (SecurityException e) { throw new RuntimeException(e); } } 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<Constructor<?>>(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); } } public static Constructor<?>[] getConstructors(Class<?> clazz) { try { ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName())); if (cd == null || !cd.isReplaceable()) { return clazz.getConstructors(); } Constructor<?>[] meth = clazz.getConstructors(); List<Constructor<?>> visible = new ArrayList<Constructor<?>>(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]); } } ClassData cta = cd; while (cta != null) { for (MethodData i : cta.getMethods()) { if (i.isConstructor()) { if (i.getType() == MemberType.FAKE_CONSTRUCTOR && AccessFlag.isPublic(i.getAccessFlags())) { 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)); } } } cta = cta.getSuperClassInformation(); } 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); } } public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameters) throws NoSuchMethodException { ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName())); if (cd == null || !cd.isReplaceable()) { Constructor<?> meth = clazz.getConstructor(parameters); return meth; } String args = '(' + DescriptorUtils.classArrayToDescriptorString(parameters) + ')'; MethodData md = cd.getMethodData("<init>", args); if (md == null) { Constructor<?> meth = clazz.getConstructor(parameters); return meth; } switch (md.getType()) { case NORMAL: Constructor<?> meth = clazz.getConstructor(parameters); return meth; case FAKE_CONSTRUCTOR: try { Class<?> c = clazz.getClassLoader().loadClass(md.getClassName()); meth = c.getConstructor(parameters); return meth; } catch (NoSuchMethodException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } throw new NoSuchMethodException(); } 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()) { Constructor<?> meth = clazz.getDeclaredConstructor(parameters); return meth; } String args = '(' + DescriptorUtils.classArrayToDescriptorString(parameters) + ')'; MethodData md = cd.getMethodData("<init>", args); if (md == null) { Constructor<?> meth = clazz.getDeclaredConstructor(parameters); return meth; } switch (md.getType()) { case NORMAL: Constructor<?> meth = clazz.getDeclaredConstructor(parameters); return meth; case FAKE_CONSTRUCTOR: try { Class<?> c = clazz.getClassLoader().loadClass(md.getClassName()); meth = c.getDeclaredConstructor(parameters); return meth; } catch (NoSuchMethodException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } throw new NoSuchMethodException(); } public static Class<?> getDeclaringClass(Constructor<?> f) { Class<?> c = f.getDeclaringClass(); if (c.getName().startsWith(Constants.GENERATED_CLASS_PACKAGE)) { return ClassDataStore.instance().getRealClassFromProxyName(c.getName()); } return c; } public static boolean fakeCallRequired(Constructor<?> method) { return method.getDeclaringClass().getName().startsWith(Constants.GENERATED_CLASS_PACKAGE); } }