Package org.springsource.loaded.ri

Source Code of org.springsource.loaded.ri.ReflectiveInterceptor

/*
* Copyright 2010-2012 VMware and contributors
*
* 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.springsource.loaded.ri;

import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.ref.WeakReference;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.springsource.loaded.C;
import org.springsource.loaded.Constants;
import org.springsource.loaded.CurrentLiveVersion;
import org.springsource.loaded.FieldMember;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.MethodMember;
import org.springsource.loaded.ReloadException;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.Utils;
import org.springsource.loaded.infra.UsedByGeneratedCode;
import org.springsource.loaded.jvm.JVM;


/**
* The reflective interceptor is called to rewrite any reflective calls that are found in the bytecode. Intercepting the calls means
* we can delegate to the SpringLoaded infrastructure.
*
* @author Andy Clement
* @author Kris De Volder
* @since 0.5.0
*/
public class ReflectiveInterceptor {

  public static Logger log = Logger.getLogger(ReflectiveInterceptor.class.getName());

  private static Map<Class<?>, WeakReference<ReloadableType>> classToRType = null;

  static {
    boolean synchronize = false;
    try {
      String prop = System.getProperty("springloaded.synchronize", "false");
      if (prop.equalsIgnoreCase("true")) {
        synchronize = true;
      }
    } catch (Throwable t) {
      // likely security manager
    }
    if (synchronize) {
      classToRType = Collections.synchronizedMap(new WeakHashMap<Class<?>, WeakReference<ReloadableType>>());
    } else {
      classToRType = new WeakHashMap<Class<?>, WeakReference<ReloadableType>>();
    }
  }
 
  @UsedByGeneratedCode
  public static boolean jlosHasStaticInitializer(Class<?> clazz) {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Exception tells the caller to use the 'old way' to determine if there is a static initializer
      throw new IllegalStateException();
    }
    return rtype.hasStaticInitializer();
  }

  /*
   * Implementation of java.lang.class.getDeclaredMethod(String name, Class... params).
   */
  @UsedByGeneratedCode
  public static Method jlClassGetDeclaredMethod(Class<?> clazz, String name, Class<?>... params) throws SecurityException,
      NoSuchMethodException {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable...
      return clazz.getDeclaredMethod(name, params);
    } else {
      // Reloadable
      MethodProvider methods = MethodProvider.create(rtype);
      Invoker method = methods.getDeclaredMethod(name, params);
      if (method == null) {
        throw Exceptions.noSuchMethodException(clazz, name, params);
      } else {
        return method.createJavaMethod();
      }
    }
  }

  /*
   * Implementation of java.lang.class.getMethod(String name, Class... params).
   */
  @UsedByGeneratedCode
  public static Method jlClassGetMethod(Class<?> clazz, String name, Class<?>... params) throws SecurityException,
      NoSuchMethodException {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable...
      return clazz.getMethod(name, params);
    } else {
      MethodProvider methods = MethodProvider.create(rtype);
      Invoker method = methods.getMethod(name, params);
      if (method == null) {
        throw Exceptions.noSuchMethodException(clazz, name, params);
      } else {
        return method.createJavaMethod();
      }
    }
  }

  public static Method[] jlClassGetDeclaredMethods(Class<?> clazz) {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable...
      return clazz.getDeclaredMethods();
    } else {
      MethodProvider methods = MethodProvider.create(rtype);
      List<Invoker> invokers = methods.getDeclaredMethods();
      Method[] javaMethods = new Method[invokers.size()];
      for (int i = 0; i < javaMethods.length; i++) {
        javaMethods[i] = invokers.get(i).createJavaMethod();
      }
      return javaMethods;
    }
  }

  public static Method[] jlClassGetMethods(Class<?> clazz) {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable...
      return clazz.getMethods();
    } else {
      MethodProvider methods = MethodProvider.create(rtype);
      Collection<Invoker> invokers = methods.getMethods();
      Method[] javaMethods = new Method[invokers.size()];
      int i = 0;
      for (Invoker invoker : invokers) {
        javaMethods[i++] = invoker.createJavaMethod();
      }
      return javaMethods;
    }
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  static String toParamString(Class<?>[] params) {
    if (params == null || params.length == 0) {
      return "()";
    }
    StringBuilder s = new StringBuilder();
    s.append('(');
    for (int i = 0, max = params.length; i < max; i++) {
      if (i > 0) {
        s.append(", ");
      }
      if (params[i] == null) {
        s.append("null");
      }
      else {
        s.append(params[i].getName());
      }
    }
    s.append(')');
    return s.toString();
  }

  private static int depth = 4;
  /*
   * Get the Class that declares the method calling interceptor method that called this method.
   */
  @SuppressWarnings("deprecation")
  public static Class<?> getCallerClass() {
    //0 = sun.reflect.Reflection.getCallerClass
    //1 = this method's frame
    //2 = caller of 'getCallerClass' = asAccesibleMethod
    //3 = caller of 'asAccesibleMethod' = jlrInvoke
    //4 = caller we are interested in...
   
    // In jdk17u25 there is an extra frame inserted:
    // "This also fixes a regression introduced in 7u25 in which
    // getCallerClass(int) is now a Java method that adds an additional frame
    // that wasn't taken into account." in http://permalink.gmane.org/gmane.comp.java.openjdk.jdk7u.devel/6573
    Class<?> caller = sun.reflect.Reflection.getCallerClass(depth);
    if (caller==ReflectiveInterceptor.class) {
      // If this is true we have that extra frame on the stack
      depth=5;
      caller = sun.reflect.Reflection.getCallerClass(depth);
    }

    String callerClassName = caller.getName();

    Matcher matcher = Constants.executorClassNamePattern.matcher(callerClassName);
    if (matcher.find()) {
      // Complication... the caller may in fact be an executor method...
      // in this case the caller will be an executor class.

      ClassLoader loader = caller.getClassLoader();
      try {
        return Class.forName(callerClassName.substring(0, matcher.start()), false, loader);
      } catch (ClassNotFoundException e) {
        //Supposedly it wasn't an executor class after all...
        log.log(Level.INFO, "Potential trouble determining caller of reflective method", e);
      }
    }
    return caller;
  }

  /**
   * Called to satisfy an invocation of java.lang.Class.getDeclaredAnnotations().
   *
   * @param clazz the class upon which the original call was being invoked
   * @return array of annotations on the class
   */
  public static Annotation[] jlClassGetDeclaredAnnotations(Class<?> clazz) {
    if (TypeRegistry.nothingReloaded) {
      return clazz.getDeclaredAnnotations();
    }
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(clazz);
    if (rtype == null) {
      return clazz.getDeclaredAnnotations();
    }
    CurrentLiveVersion clv = rtype.getLiveVersion();
    return clv.getExecutorClass().getDeclaredAnnotations();
  }

  /*
   * Called to satisfy an invocation of java.lang.Class.getDeclaredAnnotations().
   *
   * @param clazz the class upon which the original call was being invoked
   */
  public static Annotation[] jlClassGetAnnotations(Class<?> clazz) {
    if (TypeRegistry.nothingReloaded) {
      return clazz.getAnnotations();
    }
    ReloadableType rtype = getRType(clazz);
    //Note: even if class has not been reloaded, it's superclass may have been and this may affect
    //  the inherited annotations, so we must *not* use 'getReloadableTypeIfHasBeenReloaded' above!
    if (rtype == null) {
      return clazz.getAnnotations();
    }

    Class<?> superClass = clazz.getSuperclass();
    if (superClass == null) {
      return jlClassGetDeclaredAnnotations(clazz); //Nothing to inherit so it's ok to call this
    }
    Map<Class<? extends Annotation>, Annotation> combinedAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();

    Annotation[] annotationsToAdd = jlClassGetAnnotations(superClass);
    for (Annotation annotation : annotationsToAdd) {
      if (isInheritable(annotation)) {
        combinedAnnotations.put(annotation.annotationType(), annotation);
      }
    }

    annotationsToAdd = jlClassGetDeclaredAnnotations(clazz);
    for (Annotation annotation : annotationsToAdd) {
      combinedAnnotations.put(annotation.annotationType(), annotation);
    }

    return combinedAnnotations.values().toArray(new Annotation[combinedAnnotations.size()]);
  }

  public static Annotation jlClassGetAnnotation(Class<?> clazz, Class<? extends Annotation> annoType) {
    ReloadableType rtype = getRType(clazz);
    //Note: even if class has not been reloaded, it's superclass may have been and this may affect
    //  the inherited annotations, so we must *not* use 'getReloadableTypeIfHasBeenReloaded' above!

    if (rtype == null) {
      return clazz.getAnnotation(annoType);
    }

    if (annoType == null) {
      throw new NullPointerException();
    }

    for (Annotation localAnnot : jlClassGetDeclaredAnnotations(clazz)) {
      if (localAnnot.annotationType() == annoType) {
        return localAnnot;
      }
    }

    if (annoType.isAnnotationPresent(Inherited.class)) {
      Class<?> superClass = clazz.getSuperclass();
      if (superClass != null) {
        return jlClassGetAnnotation(superClass, annoType);
      }
    }
    return null;
  }

  public static boolean jlClassIsAnnotationPresent(Class<?> clazz, Class<? extends Annotation> annoType) {
    ReloadableType rtype = getRType(clazz);
    //Note: even if class has not been reloaded, it's superclass may have been and this may affect
    //  the inherited annotations, so we must *not* use 'getReloadableTypeIfHasBeenReloaded' above!

    if (rtype == null) {
      return clazz.isAnnotationPresent(annoType);
    }
    return jlClassGetAnnotation(clazz, annoType) != null;
  }

  public static Constructor<?>[] jlClassGetDeclaredConstructors(Class<?> clazz) {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Non reloadable type
      Constructor<?>[] cs = clazz.getDeclaredConstructors();
      return cs;
    } else if (!rtype.hasBeenReloaded()) {
      // Reloadable but not yet reloaded
      Constructor<?>[] cs = clazz.getDeclaredConstructors();
      int i = 0;
      for (Constructor<?> c : cs) {
        if (isMetaConstructor(clazz, c)) {
          // We must remove the 'special' constructor added by SpringLoaded
          continue;
        }
        // SpringLoaded changes modifiers, so must fix them
        fixModifier(rtype, c);
        cs[i++] = c;
      }
      return Utils.arrayCopyOf(cs, i);
    } else {
      CurrentLiveVersion liveVersion = rtype.getLiveVersion();
      // Reloaded type
      Constructor<?>[] clazzCs = null;
      TypeDescriptor desc = rtype.getLatestTypeDescriptor();
      MethodMember[] members = desc.getConstructors();
      Constructor<?>[] cs = new Constructor<?>[members.length];
      for (int i = 0; i < cs.length; i++) {
        MethodMember m = members[i];
        if (!liveVersion.hasConstructorChanged(m)) {
          if (clazzCs == null) {
            clazzCs = clazz.getDeclaredConstructors();
          }
          cs[i] = findConstructor(clazzCs, m);
          //           SpringLoaded changes modifiers, so must fix them
          fixModifier(rtype, cs[i]);
        } else {
          cs[i] = newConstructor(rtype, m);
        }
      }
      return cs;
    }
  }

  private static Constructor<?> findConstructor(Constructor<?>[] constructors, MethodMember searchFor) {
    String paramDescriptor = searchFor.getDescriptor();
    for (int i = 0, max = constructors.length; i < max; i++) {
      String candidateDescriptor = Utils.toConstructorDescriptor(constructors[i].getParameterTypes());
      if (candidateDescriptor.equals(paramDescriptor)) {
        return constructors[i];
      }
    }
    return null;
  }

  private static boolean isMetaConstructor(Class<?> clazz, Constructor<?> c) {
    Class<?>[] params = c.getParameterTypes();
    if (clazz.isEnum()) {
      return params.length > 2 && params[2].getName().equals(Constants.magicDescriptorForGeneratedCtors);
    } else if (clazz.getSuperclass() != null && clazz.getSuperclass().getName().equals("groovy.lang.Closure")) {
      return params.length > 2 && params[2].getName().equals(Constants.magicDescriptorForGeneratedCtors);
    } else {
      return params.length > 0 && params[0].getName().equals(Constants.magicDescriptorForGeneratedCtors);
    }
  }

  private static Constructor<?> newConstructor(ReloadableType rtype, MethodMember m) {
    ClassLoader classLoader = rtype.getTypeRegistry().getClassLoader();
    try {
      return JVM.newConstructor(Utils.toClass(rtype), //declaring
          Utils.toParamClasses(m.getDescriptor(), classLoader), // params
          Utils.slashedNamesToClasses(m.getExceptions(), classLoader), //exceptions
          m.getModifiers(), //modifiers
          m.getGenericSignature() //signature
          );
    } catch (ClassNotFoundException e) {
      throw new IllegalStateException("Couldn't create j.l.Constructor for " + m, e);
    }
  }

  private static void fixModifiers(ReloadableType rtype, Field[] fields) {
    TypeDescriptor typeDesc = rtype.getLatestTypeDescriptor();
    for (Field field : fields) {
      fixModifier(typeDesc, field);
    }
  }

  static void fixModifier(TypeDescriptor typeDesc, Field field) {
    int mods = typeDesc.getField(field.getName()).getModifiers();
    if (mods != field.getModifiers()) {
      JVM.setFieldModifiers(field, mods);
    }
  }

  protected static void fixModifier(ReloadableType rtype, Constructor<?> constructor) {
    String desc = Type.getConstructorDescriptor(constructor);
    MethodMember rCons = rtype.getCurrentConstructor(desc);
    if (constructor.getModifiers() != rCons.getModifiers()) {
      JVM.setConstructorModifiers(constructor, rCons.getModifiers());
    }
  }

  public static Constructor<?>[] jlClassGetConstructors(Class<?> clazz) {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      return clazz.getConstructors();
    } else {
      Constructor<?>[] candidates = jlClassGetDeclaredConstructors(clazz);
      //We need to throw away any non-public constructors.
      List<Constructor<?>> keep = new ArrayList<Constructor<?>>(candidates.length);
      for (Constructor<?> candidate : candidates) {
        if (Modifier.isPublic(candidate.getModifiers())) {
          keep.add(candidate);
        }
      }
      return keep.toArray(new Constructor<?>[keep.size()]);
    }
  }

  public static Constructor<?> jlClassGetDeclaredConstructor(Class<?> clazz, Class<?>... params) throws SecurityException,
      NoSuchMethodException {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Non reloadable type
      Constructor<?> c = clazz.getDeclaredConstructor(params);
      return c;
    } else if (!rtype.hasBeenReloaded()) {
      // Reloadable but not yet reloaded
      Constructor<?> c = clazz.getDeclaredConstructor(params);
      if (isMetaConstructor(clazz, c)) {
        // not a real constructor !
        throw Exceptions.noSuchConstructorException(clazz, params);
      }
      // SpringLoaded changes modifiers, so must fix them
      fixModifier(rtype, c);
      return c;
    } else {

      // This would be the right thing to do but makes getDeclaredConstructors() very messy
      CurrentLiveVersion clv = rtype.getLiveVersion();
      boolean b = clv.hasConstructorChanged(Utils.toConstructorDescriptor(params));
      if (!b) {
        Constructor<?> c = clazz.getDeclaredConstructor(params);
        if (isMetaConstructor(clazz, c)) {
          // not a real constructor !
          throw Exceptions.noSuchConstructorException(clazz, params);
        }
        // SpringLoaded changes modifiers, so must fix them
        fixModifier(rtype, c);
        return c;
      } else {
        // Reloaded type
        TypeDescriptor desc = rtype.getLatestTypeDescriptor();
        MethodMember[] members = desc.getConstructors();
        String searchFor = Utils.toConstructorDescriptor(params);
        for (MethodMember m : members) {
          if (m.getDescriptor().equals(searchFor)) {
            return newConstructor(rtype, m);
          }
        }
        throw Exceptions.noSuchConstructorException(clazz, params);
      }
    }
  }

  public static Constructor<?> jlClassGetConstructor(Class<?> clazz, Class<?>... params) throws SecurityException,
      NoSuchMethodException {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      return clazz.getConstructor(params);
    } else {
      Constructor<?> c = jlClassGetDeclaredConstructor(clazz, params);
      if (Modifier.isPublic(c.getModifiers())) {
        return c;
      } else {
        throw Exceptions.noSuchMethodException(clazz, "<init>", params);
      }
    }
  }

  private static boolean isInheritable(Annotation annotation) {
    return annotation.annotationType().isAnnotationPresent(Inherited.class);
  }

  /**
   * Performs access checks and returns a (potential) copy of the method with accessibility flag set if this necessary for the
   * invoke to succeed.
   * <p>
   * Also checks for deleted methods.
   * <p>
   * If any checks fail, an appropriate exception is raised.
   */
  private static Method asAccessibleMethod(ReloadableType methodDeclaringTypeReloadableType, Method method, Object target,
      boolean makeAccessibleCopy) throws IllegalAccessException {
    if (methodDeclaringTypeReloadableType != null && isDeleted(methodDeclaringTypeReloadableType, method)) {
      throw Exceptions.noSuchMethodError(method);
    }

    if (method.isAccessible()) {
      //More expensive check not required / copy not required
    } else {
      Class<?> clazz = method.getDeclaringClass();
      int mods = method.getModifiers();
      int classmods;

      //    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(clazz);
      if (methodDeclaringTypeReloadableType == null || !methodDeclaringTypeReloadableType.hasBeenReloaded()) {
        classmods = clazz.getModifiers();
      } else {
        //Note: the "super bit" may be set in class modifiers but we should block it out, it
        //shouldn't be shown to users of the reflection API.
        classmods = methodDeclaringTypeReloadableType.getLatestTypeDescriptor().getModifiers() & ~Opcodes.ACC_SUPER;
      }
      if (Modifier.isPublic(mods & classmods/*jlClassGetModifiers(clazz)*/)) {
        //More expensive check not required / copy not required
      } else {
        //More expensive check required
        Class<?> callerClass = getCallerClass();
        JVM.ensureMemberAccess(callerClass, clazz, target, mods);
        if (makeAccessibleCopy) {
          method = JVM.copyMethod(method); // copy: we must not change accessible flag on original method!
          method.setAccessible(true);
        }
      }
    }
    return makeAccessibleCopy ? method : null;
  }

  private static Constructor<?> asAccessibleConstructor(Constructor<?> c, boolean makeAccessibleCopy)
      throws NoSuchMethodException, IllegalAccessException {
    if (isDeleted(c)) {
      throw Exceptions.noSuchConstructorError(c);
    }
    Class<?> clazz = c.getDeclaringClass();
    int mods = c.getModifiers();
    if (c.isAccessible() || Modifier.isPublic(mods & jlClassGetModifiers(clazz))) {
      //More expensive check not required / copy not required
    } else {
      //More expensive check required
      Class<?> callerClass = getCallerClass();
      JVM.ensureMemberAccess(callerClass, clazz, null, mods);
      if (makeAccessibleCopy) {
        c = JVM.copyConstructor(c); // copy: we must not change accessible flag on original method!
        c.setAccessible(true);
      }
    }
    return makeAccessibleCopy ? c : null;
  }

  /**
   * Performs access checks and returns a (potential) copy of the field with accessibility flag set if this necessary for the
   * acces operation to succeed.
   * <p>
   * If any checks fail, an appropriate exception is raised.
   *
   * Warning this method is sensitive to stack depth! Should expects to be called DIRECTLY from a jlr redicriction method only!
   */
  private static Field asAccessibleField(Field field, Object target, boolean makeAccessibleCopy) throws IllegalAccessException {
    if (isDeleted(field)) {
      throw Exceptions.noSuchFieldError(field);
    }
    Class<?> clazz = field.getDeclaringClass();
    int mods = field.getModifiers();
    if (field.isAccessible() || Modifier.isPublic(mods & jlClassGetModifiers(clazz))) {
      //More expensive check not required / copy not required
    } else {
      //More expensive check required
      Class<?> callerClass = getCallerClass();
      JVM.ensureMemberAccess(callerClass, clazz, target, mods);
      if (makeAccessibleCopy) {
        //TODO: This code is not covered by a test. It needs a non-reloadable type with non-public
        //  field, being accessed reflectively from a context that is "priviliged" to access it without setting the access flag.

        field = JVM.copyField(field); // copy: we must not change accessible flag on original method!
        field.setAccessible(true);
      }
    }
    return makeAccessibleCopy ? field : null;
  }

  /**
   * Performs all necessary checks that need to be done before a field set should be allowed.
   *
   * @throws IllegalAccessException
   */
  private static Field asSetableField(Field field, Object target, Class<?> valueType, Object value, boolean makeAccessibleCopy)
      throws IllegalAccessException {
    // Must do the checks exactly in the same order as JVM if we want identical error messages.

    // JVM doesn't do this, since it cannot happen without reloading, we do it first of all.
    if (isDeleted(field)) {
      throw Exceptions.noSuchFieldError(field);
    }

    Class<?> clazz = field.getDeclaringClass();
    int mods = field.getModifiers();
    if (field.isAccessible() || Modifier.isPublic(mods & jlClassGetModifiers(clazz))) {
      //More expensive check not required / copy not required
    } else {
      //More expensive check required
      Class<?> callerClass = getCallerClass();
      JVM.ensureMemberAccess(callerClass, clazz, target, mods);
      if (makeAccessibleCopy) {
        //TODO: This code is not covered by a test. It needs a non-reloadable type with non-public
        //  field, being accessed reflectively from a context that is "priviliged" to access it without setting the access flag.

        field = JVM.copyField(field); // copy: we must not change accessible flag on original field!
        field.setAccessible(true);
      }
    }
    if (isPrimitive(valueType)) {
      //It seems for primitive types, the order of the checks (in Sun JVM) is different!
      typeCheckFieldSet(field, valueType, value);
      if (!field.isAccessible() && Modifier.isFinal(mods)) {
        throw Exceptions.illegalSetFinalFieldException(field, field.getType(), coerce(value, field.getType()));
      }
    } else {
      if (!field.isAccessible() && Modifier.isFinal(mods)) {
        throw Exceptions.illegalSetFinalFieldException(field, valueType, value);
      }
      typeCheckFieldSet(field, valueType, value);
    }
    return makeAccessibleCopy ? field : null;
  }

  private static Object coerce(Object value, Class<?> toType) {
    //Warning: this method's implementation is not for general use, it's only intended use is to
    //  ensure correctness of error messages, so it doesn't need to cover all 'coercable' cases,
    //  only those cases where the coerced value print out differently, and which are reachable
    //  from 'asSetableField'.
    Class<? extends Object> fromType = value.getClass();
    if (Integer.class.equals(fromType)) {
      if (float.class.equals(toType)) {
        return (float) (Integer) value;
      } else if (double.class.equals(toType)) {
        return (double) (Integer) value;
      }
    } else if (Byte.class.equals(fromType)) {
      if (float.class.equals(toType)) {
        return (float) (Byte) value;
      } else if (double.class.equals(toType)) {
        return (double) (Byte) value;
      }
    } else if (Character.class.equals(fromType)) {
      if (int.class.equals(toType)) {
        return (int) (Character) value;
      } else if (long.class.equals(toType)) {
        return (long) (Character) value;
      } else if (float.class.equals(toType)) {
        return (float) (Character) value;
      } else if (double.class.equals(toType)) {
        return (double) (Character) value;
      }
    } else if (Short.class.equals(fromType)) {
      if (float.class.equals(toType)) {
        return (float) (Short) value;
      } else if (double.class.equals(toType)) {
        return (double) (Short) value;
      }
    } else if (Long.class.equals(fromType)) {
      if (float.class.equals(toType)) {
        return (float) (Long) value;
      } else if (double.class.equals(toType)) {
        return (double) (Long) value;
      }
    } else if (Float.class.equals(fromType)) {
      if (double.class.equals(toType)) {
        return (double) (Float) value;
      }
    }
    return value;
  }

  /**
   * Perform a dynamic type check needed when setting a field value onto a field. Raises the appropriate exception when the check
   * fails and returns normally otherwise. This method should only be called for object types. For primitive types call the three
   * parameter variant instead.
   *
   * @throws IllegalAccessException
   */
  private static void typeCheckFieldSet(Field field, Object value) throws IllegalAccessException {
    Class<?> fieldType = field.getType();
    if (value == null) {
      if (fieldType.isPrimitive()) {
        throw Exceptions.illegalSetFieldTypeException(field, null, value);
      }
    } else {
      if (fieldType.isPrimitive()) {
        fieldType = boxTypeFor(fieldType);
      }
      Class<?> valueType = value.getClass();
      if (!Utils.isConvertableFrom(fieldType, valueType)) {
        throw Exceptions.illegalSetFieldTypeException(field, valueType, value);
      }
    }
  }

  /**
   * Perform a dynamic type check needed when setting a field value onto a field. Raises the appropriate exception when the check
   * fails and returns normally otherwise.
   *
   * @throws IllegalAccessException
   */
  private static void typeCheckFieldSet(Field field, Class<?> valueType, Object value) throws IllegalAccessException {
    if (!isPrimitive(valueType)) {
      //Call the version of this method that considers autoboxing
      typeCheckFieldSet(field, value);
    } else {
      //Value type is primitive.
      //  Note: In this case value was a primitive value that became boxed, so it can't be null.
      Class<?> fieldType = field.getType();
      if (!Utils.isConvertableFrom(fieldType, valueType)) {
        throw Exceptions.illegalSetFieldTypeException(field, valueType, value);
      }
    }
  }

  /**
   * Checks whether given 'valueType' is a primitive type, considering that we use 'null' as the type for 'null' (to distinguish
   * it from the type 'Object' which is not the same!)
   */
  private static boolean isPrimitive(Class<?> valueType) {
    return valueType != null && valueType.isPrimitive();
  }

  /**
   * Determine a "valueType" from a given value object. Note that this should really only be used for values that are
   * non-primitive, otherwise it will be impossible to distinguish between a primitive value and its boxed representation.
   * <p>
   * In a context where you have a primitive value that gets boxed up, its valueType should be passed in explicitly as a class
   * like, for example, int.class.
   */
  private static Class<?> valueType(Object value) {
    if (value == null) {
      return null;
    } else {
      return value.getClass();
    }
  }

  /**
   * Retrieve modifiers for a Java class, which might or might not be reloadable or reloaded.
   *
   * @param clazz the class for which to discover modifiers
   * @return the modifiers
   */
  public static int jlClassGetModifiers(Class<?> clazz) {
    //    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(clazz);
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      return clazz.getModifiers();
    } else {
      //Note: the "super bit" may be set in class modifiers but we should block it out, it
      //shouldn't be shown to users of the reflection API.
      return rtype.getLatestTypeDescriptor().getModifiers() & ~Opcodes.ACC_SUPER;
    }
  }

  private static boolean isDeleted(ReloadableType rtype, Method method) {
    //    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(method.getDeclaringClass());
    if (rtype == null || !rtype.hasBeenReloaded()) {
      return false;
    } else {
      MethodMember currentMethod = rtype.getCurrentMethod(method.getName(), Type.getMethodDescriptor(method));
      if (currentMethod == null) {
        return true; // Method not there, consider it deleted
      } else {
        return MethodMember.isDeleted(currentMethod); // Deleted bit is set consider deleted
      }
    }
  }

  private static boolean isDeleted(Constructor<?> c) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(c.getDeclaringClass());
    if (rtype == null) {
      return false;
    } else {
      TypeDescriptor desc = rtype.getLatestTypeDescriptor();
      MethodMember currentConstructor = desc.getConstructor(Type.getConstructorDescriptor(c));
      if (currentConstructor == null) {
        //TODO: test case with a deleted constructor
        return true; // Method not there, consider it deleted
      } else {
        return false;
      }
    }
  }

  private static boolean isDeleted(Field field) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(field.getDeclaringClass());
    if (rtype == null) {
      return false;
    } else {
      TypeDescriptor desc = rtype.getLatestTypeDescriptor();
      FieldMember currentField = desc.getField(field.getName());
      if (currentField == null) {
        return true; // Method not there, consider it deleted
      } else {
        return false;
      }
      // Fields don't have deleted bits now, but maybe they get them in the future?
      //      } else {
      //        return FieldMember.isDeleted(currentField); // Deleted bit is set consider deleted
      //      }
    }
  }

  /**
   * If clazz is reloadable <b>and</b> has been reloaded at least once then return the ReloadableType instance for it, otherwise
   * return null.
   *
   * @param clazz the type which may or may not be reloadable
   * @return the reloadable type or null
   */
  private static ReloadableType getReloadableTypeIfHasBeenReloaded(Class<?> clazz) {
    if (TypeRegistry.nothingReloaded) {
      return null;
    }
    ReloadableType rtype = getRType(clazz);
    if (rtype != null && rtype.hasBeenReloaded()) {
      return rtype;
    } else {
      return null;
    }
  }

  private final static boolean theOldWay = false;

  /**
   * Access and return the ReloadableType field on a specified class.
   *
   * @param clazz the class for which to discover the reloadable type
   * @return the reloadable type for the class, or null if not reloadable
   */
  public static ReloadableType getRType(Class<?> clazz) {
    //    ReloadableType rtype = null;
    WeakReference<ReloadableType> ref = classToRType.get(clazz);
    ReloadableType rtype = null;
    if (ref != null) {
      rtype = ref.get();
    }
    if (rtype == null) {

      if (!theOldWay) {
        // 'theOldWay' attempts to grab the field from the type via reflection.  This usually works except
        // in cases where the class is not resolved yet since it can cause the class to resolve and its
        // static initializer to run.  This was happening on a grails compile where the compiler is
        // loading dependencies (but not initializing them).  Instead we can use this route of
        // discovering the type registry and locating the reloadable type.  This does some map lookups
        // which may be a problem, but once discovered, it is cached in the weak ref so that shouldn't
        // be an ongoing perf problem.

        // TODO testcases for something that is reloaded without having been resolved
        ClassLoader cl = clazz.getClassLoader();
        TypeRegistry tr = TypeRegistry.getTypeRegistryFor(cl);
        if (tr == null) {
          classToRType.put(clazz, ReloadableType.NOT_RELOADABLE_TYPE_REF);
        } else {
          rtype = tr.getReloadableType(clazz.getName().replace('.', '/'));
          if (rtype == null) {
            classToRType.put(clazz, ReloadableType.NOT_RELOADABLE_TYPE_REF);
          } else {
            classToRType.put(clazz, new WeakReference<ReloadableType>(rtype));
          }
        }
      } else {
        // need to work it out
        Field rtypeField;
        try {
          //        System.out.println("discovering field for " + clazz.getName());
          // TODO cache somewhere - will need a clazz>Field cache
          rtypeField = clazz.getDeclaredField(Constants.fReloadableTypeFieldName);
        } catch (NoSuchFieldException nsfe) {
          classToRType.put(clazz, ReloadableType.NOT_RELOADABLE_TYPE_REF);
          // expensive if constantly discovering this
          return null;
        }
        try {
          rtypeField.setAccessible(true);
          rtype = (ReloadableType) rtypeField.get(null);
          if (rtype == null) {
            classToRType.put(clazz, ReloadableType.NOT_RELOADABLE_TYPE_REF);
            throw new ReloadException("ReloadableType field '" + Constants.fReloadableTypeFieldName
                + "' is 'null' on type " + clazz.getName());
          } else {
            classToRType.put(clazz, new WeakReference<ReloadableType>(rtype));
          }
        } catch (Exception e) {
          throw new ReloadException("Unable to access ReloadableType field '" + Constants.fReloadableTypeFieldName
              + "' on type " + clazz.getName(), e);
        }
      }
    } else if (rtype == ReloadableType.NOT_RELOADABLE_TYPE) {
      return null;
    }
    return rtype;
  }

  public static Annotation[] jlrMethodGetDeclaredAnnotations(Method method) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(method.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return method.getDeclaredAnnotations();
    } else {
      // Method could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      MethodMember methodMember = rtype.getCurrentMethod(method.getName(), Type.getMethodDescriptor(method));
      if (MethodMember.isCatcher(methodMember)) {
        if (clv.getExecutorMethod(methodMember) != null) {
          throw new IllegalStateException();
        }
        return method.getDeclaredAnnotations();
      }
      Method executor = clv.getExecutorMethod(methodMember);
      return executor.getAnnotations();
    }
  }

  public static Annotation[][] jlrMethodGetParameterAnnotations(Method method) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(method.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return method.getParameterAnnotations();
    } else {
      // Method could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      MethodMember currentMethod = rtype.getCurrentMethod(method.getName(), Type.getMethodDescriptor(method));
      Method executor = clv.getExecutorMethod(currentMethod);
      Annotation[][] result = executor.getParameterAnnotations();
      if (!currentMethod.isStatic()) {
        //Non=static methods have an extra param.
        //Though extra param is added to front...
        //Annotations aren't being moved so we have to actually drop the *last* array element
        result = Utils.arrayCopyOf(result, result.length - 1);
      }
      return result;
    }
  }

  public static Object jlClassNewInstance(Class<?> clazz) throws SecurityException, NoSuchMethodException,
      IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
    // Note: no special case for non-reloadable types here, because access checks:
    //    access checks depend on stack depth and springloaded rewriting changes that even for non-reloadable types!

    // TODO: This implementation doesn't check access modifiers on the class. So may allow
    //   instantiations that wouldn't be allowed by the JVM (e.g if constructor is public, but class is private)

    // TODO: what about trying to instantiate an abstract class? should produce an error, does it?

    Constructor<?> c;
    try {
      c = jlClassGetDeclaredConstructor(clazz);
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
      throw Exceptions.instantiation(clazz);
    }
    c = asAccessibleConstructor(c, true);
    return jlrConstructorNewInstance(c);
  }

  public static Object jlrConstructorNewInstance(Constructor<?> c, Object... params) throws InstantiationException,
      IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchMethodException {
    //Note: unlike for methods we don't need to handle the reloadable but not reloaded case specially, that is because there
    // is no inheritance on constructors, so reloaded superclasses can affect method lookup in the same way.

    Class<?> clazz = c.getDeclaringClass();
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(clazz);
    if (rtype == null) {
      c = asAccessibleConstructor(c, true);
      //Nothing special to be done
      return c.newInstance(params);
    } else {
      // Constructor may have changed...
      // this is the right thing to do but makes a mess of getDeclaredConstructors (and affects getDeclaredConstructor)
      //      // TODO  should check about constructor changing
      //      rtype.getTypeDescriptor().getConstructor("").
      boolean ctorChanged = rtype.getLiveVersion()
          .hasConstructorChanged(Utils.toConstructorDescriptor(c.getParameterTypes()));
      if (!ctorChanged) {
        // if we let the getDeclaredConstructor(s) code run as is, it may create invalid ctors, if we want to run the real one we should discover it here and use it.
        // would it be cheaper to fix up getDeclaredConstructor to always return valid ones if we are going to use them, or should we intercept here? probably the former...

        c = asAccessibleConstructor(c, true);
        return c.newInstance(params);
      }
      asAccessibleConstructor(c, false);
      CurrentLiveVersion clv = rtype.getLiveVersion();
      Method executor = clv.getExecutorMethod(rtype.getCurrentConstructor(Type.getConstructorDescriptor(c)));
      Constructor<?> magicConstructor = clazz.getConstructor(C.class);
      Object instance = magicConstructor.newInstance((Object) null);

      Object[] instanceAndParams;
      if (params == null || params.length == 0) {
        instanceAndParams = new Object[] { instance };
      } else {
        //Must add instance as first param: executor is a static method.
        instanceAndParams = new Object[params.length + 1];
        instanceAndParams[0] = instance;
        System.arraycopy(params, 0, instanceAndParams, 1, params.length);
      }
      executor.invoke(null, instanceAndParams);
      return instance;
    }
  }

  //  private static String toString(Object... params) {
  //    if (params == null) {
  //      return "null";
  //    }
  //    StringBuilder s = new StringBuilder();
  //    for (Object param : params) {
  //      s.append(param).append(" ");
  //    }
  //    return "[" + s.toString().trim() + "]";
  //  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  public static Object jlrMethodInvoke(Method method, Object target, Object... params) throws IllegalArgumentException,
      IllegalAccessException, InvocationTargetException {
    //    System.out.println("> jlrMethodInvoke:method=" + method + " target=" + target + " params=" + toString(params));
    Class declaringClass = method.getDeclaringClass();
    if (declaringClass == Class.class) {
      String mname = method.getName();
      try {
        if (mname.equals("getFields")) {
          return jlClassGetFields((Class) target);
        } else if (mname.equals("getDeclaredFields")) {
          return jlClassGetDeclaredFields((Class) target);
        } else if (mname.equals("getDeclaredField")) {
          return jlClassGetDeclaredField((Class) target, (String) params[0]);
        } else if (mname.equals("getField")) {
          return jlClassGetField((Class) target, (String) params[0]);
        } else if (mname.equals("getConstructors")) {
          return jlClassGetConstructors((Class) target);
        } else if (mname.equals("getDeclaredConstructors")) {
          return jlClassGetDeclaredConstructors((Class) target);
        } else if (mname.equals("getDeclaredMethod")) {
          return jlClassGetDeclaredMethod((Class) target, (String) params[0], (Class[]) params[1]);
        } else if (mname.equals("getDeclaredMethods")) {
          return jlClassGetDeclaredMethods((Class) target);
        } else if (mname.equals("getMethod")) {
          return jlClassGetMethod((Class) target, (String) params[0], (Class[]) params[1]);
        } else if (mname.equals("getMethods")) {
          return jlClassGetMethods((Class) target);
        } else if (mname.equals("getConstructor")) {
          return jlClassGetConstructor((Class) target, (Class[]) params[0]);
        } else if (mname.equals("getDeclaredConstructor")) {
          return jlClassGetDeclaredConstructor((Class) target, (Class[]) params[0]);
        } else if (mname.equals("getModifiers")) {
          return jlClassGetModifiers((Class) target);
        } else if (mname.equals("isAnnotationPresent")) {
          return jlClassIsAnnotationPresent((Class) target, (Class<? extends Annotation>) params[0]);
        } else if (mname.equals("newInstance")) {
          return jlClassNewInstance((Class) target);
        } else if (mname.equals("getDeclaredAnnotations")) {
          return jlClassGetDeclaredAnnotations((Class) target);
        } else if (mname.equals("getAnnotation")) {
          return jlClassGetAnnotation((Class) target, (Class) params[0]);
        } else if (mname.equals("getAnnotations")) {
          return jlClassGetAnnotations((Class) target);
        }
      } catch (NoSuchMethodException nsme) {
        throw new InvocationTargetException(nsme);
      } catch (NoSuchFieldException nsfe) {
        throw new InvocationTargetException(nsfe);
      } catch (InstantiationException ie) {
        throw new InvocationTargetException(ie);
      }
    } else if (declaringClass == Method.class) {
      String mname = method.getName();
      if (mname.equals("invoke")) {
        return jlrMethodInvoke((Method) target, params[0], (Object[]) params[1]);
      } else if (mname.equals("getAnnotation")) {
        return jlrMethodGetAnnotation((Method) target, (Class) params[0]);
      } else if (mname.equals("getAnnotations")) {
        return jlrMethodGetAnnotations((Method) target);
      } else if (mname.equals("getDeclaredAnnotations")) {
        return jlrMethodGetDeclaredAnnotations((Method) target);
      } else if (mname.equals("getParameterAnnotations")) {
        return jlrMethodGetParameterAnnotations((Method) target);
      } else if (mname.equals("isAnnotationPresent")) {
        return jlrMethodIsAnnotationPresent((Method) target, (Class) params[0]);
      }
    } else if (declaringClass == Constructor.class) {
      String mname = method.getName();
      try {
        if (mname.equals("getAnnotation")) {
          return jlrConstructorGetAnnotation((Constructor) target, (Class) params[0]);
        } else if (mname.equals("newInstance")) {
          return jlrConstructorNewInstance((Constructor) target, (Object[]) params[0]);
        } else if (mname.equals("getAnnotations")) {
          return jlrConstructorGetAnnotations((Constructor) target);
        } else if (mname.equals("getDeclaredAnnotations")) {
          return jlrConstructorGetDeclaredAnnotations((Constructor) target);
        } else if (mname.equals("isAnnotationPresent")) {
          return jlrConstructorIsAnnotationPresent((Constructor) target, (Class) params[0]);
        } else if (mname.equals("getParameterAnnotations")) {
          return jlrConstructorGetParameterAnnotations((Constructor) target);
        }
      } catch (InstantiationException ie) {
        throw new InvocationTargetException(ie);
      } catch (NoSuchMethodException nsme) {
        throw new InvocationTargetException(nsme);
      }
    } else if (declaringClass == Field.class) {
      String mname = method.getName();
      if (mname.equals("set")) {
        jlrFieldSet((Field) target, params[0], params[1]);
        return null;
      } else if (mname.equals("setBoolean")) {
        jlrFieldSetBoolean((Field) target, params[0], (Boolean) params[1]);
        return null;
      } else if (mname.equals("setByte")) {
        jlrFieldSetByte((Field) target, params[0], (Byte) params[1]);
        return null;
      } else if (mname.equals("setChar")) {
        jlrFieldSetChar((Field) target, params[0], (Character) params[1]);
        return null;
      } else if (mname.equals("setFloat")) {
        jlrFieldSetFloat((Field) target, params[0], (Float) params[1]);
        return null;
      } else if (mname.equals("setShort")) {
        jlrFieldSetShort((Field) target, params[0], (Short) params[1]);
        return null;
      } else if (mname.equals("setLong")) {
        jlrFieldSetLong((Field) target, params[0], (Long) params[1]);
        return null;
      } else if (mname.equals("setDouble")) {
        jlrFieldSetDouble((Field) target, params[0], (Double) params[1]);
        return null;
      } else if (mname.equals("setInt")) {
        jlrFieldSetInt((Field) target, params[0], (Integer) params[1]);
        return null;
      } else if (mname.equals("get")) {
        return jlrFieldGet((Field) target, params[0]);
      } else if (mname.equals("getByte")) {
        return jlrFieldGetByte((Field) target, params[0]);
      } else if (mname.equals("getChar")) {
        return jlrFieldGetChar((Field) target, params[0]);
      } else if (mname.equals("getDouble")) {
        return jlrFieldGetDouble((Field) target, params[0]);
      } else if (mname.equals("getBoolean")) {
        return jlrFieldGetBoolean((Field) target, params[0]);
      } else if (mname.equals("getLong")) {
        return jlrFieldGetLong((Field) target, params[0]);
      } else if (mname.equals("getFloat")) {
        return jlrFieldGetFloat((Field) target, params[0]);
      } else if (mname.equals("getInt")) {
        return jlrFieldGetInt((Field) target, params[0]);
      } else if (mname.equals("getShort")) {
        return jlrFieldGetShort((Field) target, params[0]);
      } else if (mname.equals("getAnnotations")) {
        return jlrFieldGetAnnotations((Field) target);
      } else if (mname.equals("getDeclaredAnnotations")) {
        return jlrFieldGetDeclaredAnnotations((Field) target);
      } else if (mname.equals("isAnnotationPresent")) {
        return jlrFieldIsAnnotationPresent((Field) target, (Class) params[0]);
      } else if (mname.equals("getAnnotation")) {
        return jlrFieldGetAnnotation((Field) target, (Class) params[0]);
      }
    } else if (declaringClass == AccessibleObject.class) {
      String mname = method.getName();
      if (mname.equals("isAnnotationPresent")) {
        if (target instanceof Constructor) {
          // TODO what about null target - how should things go bang?
          return jlrConstructorIsAnnotationPresent((Constructor) target, (Class) params[0]);
        } else if (target instanceof Method) {
          return jlrMethodIsAnnotationPresent((Method) target, (Class) params[0]);
        } else if (target instanceof Field) {
          return jlrFieldIsAnnotationPresent((Field) target, (Class) params[0]);
        }
      } else if (mname.equals("getAnnotations")) {
        if (target instanceof Constructor) {
          return jlrConstructorGetAnnotations((Constructor) target);
        } else if (target instanceof Method) {
          return jlrMethodGetAnnotations((Method) target);
        } else if (target instanceof Field) {
          return jlrFieldGetAnnotations((Field) target);
        }
      } else if (mname.equals("getDeclaredAnnotations")) {
        if (target instanceof Constructor) {
          return jlrConstructorGetDeclaredAnnotations((Constructor) target);
        } else if (target instanceof Method) {
          return jlrMethodGetDeclaredAnnotations((Method) target);
        } else if (target instanceof Field) {
          return jlrFieldGetDeclaredAnnotations((Field) target);
        }
      } else if (mname.equals("getAnnotation")) {
        if (target instanceof Constructor) {
          return jlrConstructorGetAnnotation((Constructor) target, (Class) params[0]);
        } else if (target instanceof Method) {
          return jlrMethodGetAnnotation((Method) target, (Class) params[0]);
        } else if (target instanceof Field) {
          return jlrFieldGetAnnotation((Field) target, (Class) params[0]);
        }
      }
    } else if (declaringClass == AnnotatedElement.class) {
      String mname = method.getName();
      if (mname.equals("isAnnotationPresent")) {
        if (target instanceof Constructor) {
          // TODO what about null target - how should things go bang?
          return jlrConstructorIsAnnotationPresent((Constructor) target, (Class) params[0]);
        } else if (target instanceof Method) {
          return jlrMethodIsAnnotationPresent((Method) target, (Class) params[0]);
        } else if (target instanceof Field) {
          return jlrFieldIsAnnotationPresent((Field) target, (Class) params[0]);
        }
      } else if (mname.equals("getAnnotations")) {
        if (target instanceof Constructor) {
          return jlrConstructorGetAnnotations((Constructor) target);
        } else if (target instanceof Method) {
          return jlrMethodGetAnnotations((Method) target);
        } else if (target instanceof Field) {
          return jlrFieldGetAnnotations((Field) target);
        }
      } else if (mname.equals("getDeclaredAnnotations")) {
        if (target instanceof Constructor) {
          return jlrConstructorGetDeclaredAnnotations((Constructor) target);
        } else if (target instanceof Method) {
          return jlrMethodGetDeclaredAnnotations((Method) target);
        } else if (target instanceof Field) {
          return jlrFieldGetDeclaredAnnotations((Field) target);
        }
      } else if (mname.equals("getAnnotation")) {
        if (target instanceof Constructor) {
          return jlrConstructorGetAnnotation((Constructor) target, (Class) params[0]);
        } else if (target instanceof Method) {
          return jlrMethodGetAnnotation((Method) target, (Class) params[0]);
        } else if (target instanceof Field) {
          return jlrFieldGetAnnotation((Field) target, (Class) params[0]);
        }
      }
    }

    // Even though we tinker with the visibility of methods, we don't damage private ones (which would really cause chaos if we tried
    // to allow the JVM to do the dispatch).  That means this should be OK:
    if (TypeRegistry.nothingReloaded) {
      method = asAccessibleMethod(null, method, target, true);
      return method.invoke(target, params);
    }
    ReloadableType declaringType = getRType(declaringClass);
    if (declaringType == null) {
      //Not reloadable...
      method = asAccessibleMethod(declaringType, method, target, true);
      return method.invoke(target, params);
    } else {
      //Reloadable...
      asAccessibleMethod(declaringType, method, target, false);
      int mods = method.getModifiers();
      Invoker invoker;
      if ((mods & (Modifier.STATIC | Modifier.PRIVATE)) != 0) {
        //These methods are dispatched statically
        MethodProvider methods = MethodProvider.create(declaringType);
        invoker = methods.staticLookup(mods, method.getName(), Type.getMethodDescriptor(method));
      } else {
        //These methods are dispatched dynamically
        ReloadableType targetType = getRType(target.getClass()); //NPE possible but is what should happen here!
        if (targetType == null) {
          System.out.println("GRAILS-7799: Subtype '" + target.getClass().getName() + "' of reloadable type "
              + method.getDeclaringClass().getName()
              + " is not reloadable: may not see changes reloaded in this hierarchy (please comment on that jira)");
          method = asAccessibleMethod(declaringType, method, target, true);
          return method.invoke(target, params);
        }
        MethodProvider methods = MethodProvider.create(targetType); //use target not declaring type for Dynamic lookkup
        invoker = methods.dynamicLookup(mods, method.getName(), Type.getMethodDescriptor(method));
      }
      return invoker.invoke(target, params);
    }
  }

  public static boolean jlrMethodIsAnnotationPresent(Method method, Class<? extends Annotation> annotClass) {
    return jlrMethodGetAnnotation(method, annotClass) != null;
  }

  public static Annotation jlrMethodGetAnnotation(Method method, Class<? extends Annotation> annotClass) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(method.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return method.getAnnotation(annotClass);
    } else {
      if (annotClass == null) {
        throw new NullPointerException();
      }
      // Method could have changed...
      Annotation[] annots = jlrMethodGetDeclaredAnnotations(method);
      for (Annotation annotation : annots) {
        if (annotClass.equals(annotation.annotationType())) {
          return annotation;
        }
      }
      return null;
    }
  }

  public static Annotation[] jlrAnnotatedElementGetAnnotations(AnnotatedElement elem) {
    if (elem instanceof Class<?>) {
      return jlClassGetAnnotations((Class<?>) elem);
    } else if (elem instanceof AccessibleObject) {
      return jlrAccessibleObjectGetAnnotations((AccessibleObject) elem);
    } else {
      //Don't know what it is... not something we handle anyway
      return elem.getAnnotations();
    }
  }

  public static Annotation[] jlrAnnotatedElementGetDeclaredAnnotations(AnnotatedElement elem) {
    if (elem instanceof Class<?>) {
      return jlClassGetDeclaredAnnotations((Class<?>) elem);
    } else if (elem instanceof AccessibleObject) {
      return jlrAccessibleObjectGetDeclaredAnnotations((AccessibleObject) elem);
    } else {
      //Don't know what it is... not something we handle anyway
      return elem.getDeclaredAnnotations();
    }
  }

  public static Annotation[] jlrAccessibleObjectGetDeclaredAnnotations(AccessibleObject obj) {
    if (obj instanceof Method) {
      return jlrMethodGetDeclaredAnnotations((Method) obj);
    } else if (obj instanceof Field) {
      return jlrFieldGetDeclaredAnnotations((Field) obj);
    } else if (obj instanceof Constructor<?>) {
      return jlrConstructorGetDeclaredAnnotations((Constructor<?>) obj);
    } else {
      //Some other type of member which we don't support reloading...
      return obj.getDeclaredAnnotations();
    }
  }

  public static Annotation[] jlrFieldGetDeclaredAnnotations(Field field) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(field.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return field.getDeclaredAnnotations();
    } else {
      // Field could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      Field executor;
      try {
        executor = clv.getExecutorField(field.getName());
        return executor.getAnnotations();
      } catch (Exception e) {
        throw new IllegalStateException(e);
      }
    }
  }

  public static boolean jlrFieldIsAnnotationPresent(Field field, Class<? extends Annotation> annotType) {
    if (annotType == null) {
      throw new NullPointerException();
    }
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(field.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return field.isAnnotationPresent(annotType);
    } else {
      // Field could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      try {
        Field executor = clv.getExecutorField(field.getName());
        return executor.isAnnotationPresent(annotType);
      } catch (Exception e) {
        throw new IllegalStateException(e);
      }
    }
  }

  public static Annotation[] jlrFieldGetAnnotations(Field field) {
    //Fields do not inherit annotations so we can just call...
    return jlrFieldGetDeclaredAnnotations(field);
  }

  public static Annotation[] jlrAccessibleObjectGetAnnotations(AccessibleObject obj) {
    if (obj instanceof Method) {
      return jlrMethodGetAnnotations((Method) obj);
    } else if (obj instanceof Field) {
      return jlrFieldGetAnnotations((Field) obj);
    } else if (obj instanceof Constructor<?>) {
      return jlrConstructorGetAnnotations((Constructor<?>) obj);
    } else {
      //Some other type of member which we don't support reloading...
      // (actually there are really no other cases any more!)
      return obj.getAnnotations();
    }
  }

  public static Annotation[] jlrConstructorGetAnnotations(Constructor<?> c) {
    return jlrConstructorGetDeclaredAnnotations(c);
  }

  public static Annotation[] jlrConstructorGetDeclaredAnnotations(Constructor<?> c) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(c.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return c.getDeclaredAnnotations();
    } else {
      // Constructor could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      Method executor = clv.getExecutorMethod(rtype.getCurrentConstructor(Type.getConstructorDescriptor(c)));
      return executor.getAnnotations();
    }
  }

  public static Annotation jlrConstructorGetAnnotation(Constructor<?> c, Class<? extends Annotation> annotType) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(c.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return c.getAnnotation(annotType);
    } else {
      // Constructor could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      Method executor = clv.getExecutorMethod(rtype.getCurrentConstructor(Type.getConstructorDescriptor(c)));
      return executor.getAnnotation(annotType);
    }
  }

  public static Annotation[][] jlrConstructorGetParameterAnnotations(Constructor<?> c) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(c.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return c.getParameterAnnotations();
    } else {
      // Method could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      MethodMember currentConstructor = rtype.getCurrentConstructor(Type.getConstructorDescriptor(c));
      Method executor = clv.getExecutorMethod(currentConstructor);
      Annotation[][] result = executor.getParameterAnnotations();
      //Constructor executor methods have an extra param.
      //Though extra param is added to front... annotations aren't being moved so we have to actually drop
      //the *last* array element
      result = Utils.arrayCopyOf(result, result.length - 1);
      return result;
    }
  }

  public static boolean jlrConstructorIsAnnotationPresent(Constructor<?> c, Class<? extends Annotation> annotType) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(c.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return c.isAnnotationPresent(annotType);
    } else {
      // Constructor could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      Method executor = clv.getExecutorMethod(rtype.getCurrentConstructor(Type.getConstructorDescriptor(c)));
      return executor.isAnnotationPresent(annotType);
    }
  }

  public static Annotation jlrFieldGetAnnotation(Field field, Class<? extends Annotation> annotType) {
    if (annotType == null) {
      throw new NullPointerException();
    }
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(field.getDeclaringClass());
    if (rtype == null) {
      //Nothing special to be done
      return field.getAnnotation(annotType);
    } else {
      // Field could have changed...
      CurrentLiveVersion clv = rtype.getLiveVersion();
      try {
        Field executor = clv.getExecutorField(field.getName());
        return executor.getAnnotation(annotType);
      } catch (Exception e) {
        throw new IllegalStateException(e);
      }
    }
  }

  public static Annotation[] jlrMethodGetAnnotations(Method method) {
    return jlrMethodGetDeclaredAnnotations(method);
  }

  public static boolean jlrAnnotatedElementIsAnnotationPresent(AnnotatedElement elem, Class<? extends Annotation> annotType) {
    if (elem instanceof Class<?>) {
      return jlClassIsAnnotationPresent((Class<?>) elem, annotType);
    } else if (elem instanceof AccessibleObject) {
      return jlrAccessibleObjectIsAnnotationPresent((AccessibleObject) elem, annotType);
    } else {
      //Don't know what it is... not something we handle anyway
      return elem.isAnnotationPresent(annotType);
    }
  }

  public static boolean jlrAccessibleObjectIsAnnotationPresent(AccessibleObject obj, Class<? extends Annotation> annotType) {
    if (obj instanceof Method) {
      return jlrMethodIsAnnotationPresent((Method) obj, annotType);
    } else if (obj instanceof Field) {
      return jlrFieldIsAnnotationPresent((Field) obj, annotType);
    } else if (obj instanceof Constructor) {
      return jlrConstructorIsAnnotationPresent((Constructor<?>) obj, annotType);
    } else {
      //Some other type of member which we don't support reloading...
      return obj.isAnnotationPresent(annotType);
    }
  }

  public static Annotation jlrAnnotatedElementGetAnnotation(AnnotatedElement elem, Class<? extends Annotation> annotType) {
    if (elem instanceof Class<?>) {
      return jlClassGetAnnotation((Class<?>) elem, annotType);
    } else if (elem instanceof AccessibleObject) {
      return jlrAccessibleObjectGetAnnotation((AccessibleObject) elem, annotType);
    } else {
      //Don't know what it is... not something we handle anyway
      // Note: only thing it can be is probably java.lang.Package
      return elem.getAnnotation(annotType);
    }
  }

  public static Annotation jlrAccessibleObjectGetAnnotation(AccessibleObject obj, Class<? extends Annotation> annotType) {
    if (obj instanceof Method) {
      return jlrMethodGetAnnotation((Method) obj, annotType);
    } else if (obj instanceof Field) {
      return jlrFieldGetAnnotation((Field) obj, annotType);
    } else if (obj instanceof Constructor<?>) {
      return jlrConstructorGetAnnotation((Constructor<?>) obj, annotType);
    } else {
      //Some other type of member which we don't support reloading...
      return obj.getAnnotation(annotType);
    }
  }

  public static Field jlClassGetField(Class<?> clazz, String name) throws SecurityException, NoSuchFieldException {
    ReloadableType rtype = getRType(clazz);
    if (name.startsWith(Constants.PREFIX)) {
      throw Exceptions.noSuchFieldException(name);
    }
    if (rtype == null) {
      //Not reloadable
      return clazz.getField(name);
    } else {
      //Reloadable
      Field f = GetFieldLookup.lookup(rtype, name);
      if (f != null) {
        return f;
      }
      throw Exceptions.noSuchFieldException(name);
    }
  }

  public static Field jlClassGetDeclaredField(Class<?> clazz, String name) throws SecurityException, NoSuchFieldException {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      return clazz.getDeclaredField(name);
    } else if (name.startsWith(Constants.PREFIX)) {
      throw Exceptions.noSuchFieldException(name);
    } else if (!rtype.hasBeenReloaded()) {
      Field f = clazz.getDeclaredField(name);
      fixModifier(rtype.getLatestTypeDescriptor(), f);
      return f;
    } else {
      Field f = GetDeclaredFieldLookup.lookup(rtype, name);
      if (f == null) {
        throw Exceptions.noSuchFieldException(name);
      } else {
        return f;
      }
    }
  }

  public static Field[] jlClassGetDeclaredFields(Class<?> clazz) {
    Field[] fields = clazz.getDeclaredFields();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      return fields;
    } else {
      if (!rtype.hasBeenReloaded()) {
        //Not reloaded yet...
        fields = removeMetaFields(fields);
        fixModifiers(rtype, fields);
        return fields;
      } else {
        // Was reloaded, it's up to us to create the field objects
        TypeDescriptor typeDesc = rtype.getLatestTypeDescriptor();
        FieldMember[] members = typeDesc.getFields();
        fields = new Field[members.length];
        int i = 0;
        for (FieldMember f : members) {
          String fieldTypeDescriptor = f.getDescriptor();
          Class<?> type;
          try {
            type = Utils.toClass(Type.getType(fieldTypeDescriptor), rtype.typeRegistry.getClassLoader());
          } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
          }
          fields[i++] = JVM.newField(clazz, type, f.getModifiers(), f.getName(), f.getGenericSignature());
        }
        if (GlobalConfiguration.assertsMode) {
          Utils.assertTrue(i == fields.length, "Bug: unexpected number of fields");
        }
        return fields;
      }
    }
  }

  /**
   * Given a list of fields filter out those fields that are created by springloaded (leaving only the "genuine" fields)
   */
  private static Field[] removeMetaFields(Field[] fields) {
    Field[] realFields = new Field[fields.length - 1];
    //We'll delete at least one, sometimes more than one field (because there's at least the r$type field).
    int i = 0;
    for (Field field : fields) {
      if (!field.getName().startsWith(Constants.PREFIX)) {
        realFields[i++] = field;
      }
    }
    if (i < realFields.length) {
      realFields = Utils.arrayCopyOf(realFields, i);
    }
    if (GlobalConfiguration.assertsMode) {
      Utils.assertTrue(i == realFields.length, "Bug in removeMetaFields, created array of wrong length");
    }
    return realFields;
  }

  /**
   * Although fields are not reloadable, we have to intercept this because otherwise we'll return the r$type field as a result
   * here.
   * @param clazz the class for which to retrieve the fields
   * @return array of fields in the class
   */
  public static Field[] jlClassGetFields(Class<?> clazz) {
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      return clazz.getFields();
    } else {
      List<Field> allFields = new ArrayList<Field>();
      gatherFields(clazz, allFields, new HashSet<Class<?>>());
      return allFields.toArray(new Field[allFields.size()]);
    }
  }

  /**
   * Gather up all (public) fields in an interface and all its super interfaces recursively.
   * @param clazz the class for which to collect up fields
   * @param collected a collector that has fields added to it as this method runs (recursively)
   * @param visited a set recording which types have already been visited
   */
  private static void gatherFields(Class<?> clazz, List<Field> collected, HashSet<Class<?>> visited) {
    if (visited.contains(clazz)) {
      return;
    }
    visited.add(clazz);
    Field[] fields = jlClassGetDeclaredFields(clazz);
    for (Field f : fields) {
      if (Modifier.isPublic(f.getModifiers())) {
        collected.add(f);
      }
    }
    if (!clazz.isInterface()) {
      Class<?> supr = clazz.getSuperclass();
      if (supr != null) {
        gatherFields(supr, collected, visited);
      }
    }
    for (Class<?> itf : clazz.getInterfaces()) {
      gatherFields(itf, collected, visited);
    }
  }

  public static Object jlrFieldGet(Field field, Object target) throws IllegalArgumentException, IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.get(target);
    } else {
      asAccessibleField(field, target, false);
      return rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
    }
  }

  public static int jlrFieldGetInt(Field field, Object target) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.getInt(target);
    } else {
      asAccessibleField(field, target, false);
      typeCheckFieldGet(field, int.class);
      Object value = rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
      if (value instanceof Character) {
        return ((Character) value).charValue();
      } else {
        return ((Number) value).intValue();
      }
    }
  }

  public static byte jlrFieldGetByte(Field field, Object target) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.getByte(target);
    } else {
      asAccessibleField(field, target, false);
      typeCheckFieldGet(field, byte.class);
      Object value = rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
      return ((Number) value).byteValue();
    }
  }

  public static char jlrFieldGetChar(Field field, Object target) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.getChar(target);
    } else {
      asAccessibleField(field, target, false);
      typeCheckFieldGet(field, char.class);
      Object value = rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
      return ((Character) value).charValue();
    }
  }

  public static short jlrFieldGetShort(Field field, Object target) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.getShort(target);
    } else {
      asAccessibleField(field, target, false);
      typeCheckFieldGet(field, short.class);
      Object value = rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
      if (value instanceof Character) {
        return (short) ((Character) value).charValue();
      } else {
        return ((Number) value).shortValue();
      }
    }
  }

  public static double jlrFieldGetDouble(Field field, Object target) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.getDouble(target);
    } else {
      asAccessibleField(field, target, false);
      typeCheckFieldGet(field, double.class);
      Object value = rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
      if (value instanceof Character) {
        return ((Character) value).charValue();
      } else {
        return ((Number) value).doubleValue();
      }
    }
  }

  public static float jlrFieldGetFloat(Field field, Object target) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.getFloat(target);
    } else {
      asAccessibleField(field, target, false);
      typeCheckFieldGet(field, float.class);
      Object value = rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
      if (value instanceof Character) {
        return ((Character) value).charValue();
      } else {
        return ((Number) value).floatValue();
      }
    }
  }

  public static boolean jlrFieldGetBoolean(Field field, Object target) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.getBoolean(target);
    } else {
      asAccessibleField(field, target, false);
      typeCheckFieldGet(field, boolean.class);
      Object value = rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
      return ((Boolean) value).booleanValue();
    }
  }

  public static long jlrFieldGetLong(Field field, Object target) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      field = asAccessibleField(field, target, true);
      return field.getLong(target);
    } else {
      asAccessibleField(field, target, false);
      typeCheckFieldGet(field, long.class);
      Object value = rtype.getField(target, field.getName(), Modifier.isStatic(field.getModifiers()));
      if (value instanceof Character) {
        return ((Character) value).charValue();
      } else {
        return ((Number) value).longValue();
      }
    }
  }

  private static void typeCheckFieldGet(Field field, Class<?> returnType) {
    Class<?> fieldType = field.getType();
    if (!Utils.isConvertableFrom(returnType, fieldType)) {
      throw Exceptions.illegalGetFieldType(field, returnType);
    }
  }

  public static void jlrFieldSet(Field field, Object target, Object value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, valueType(value), value, true);
      field.set(target, value);
    } else {
      asSetableField(field, target, valueType(value), value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  public static void jlrFieldSetInt(Field field, Object target, int value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, int.class, value, true);
      field.setInt(target, value);
    } else {
      asSetableField(field, target, int.class, value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  public static void jlrFieldSetByte(Field field, Object target, byte value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, byte.class, value, true);
      field.setByte(target, value);
    } else {
      asSetableField(field, target, byte.class, value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  public static void jlrFieldSetChar(Field field, Object target, char value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, char.class, value, true);
      field.setChar(target, value);
    } else {
      asSetableField(field, target, char.class, value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  public static void jlrFieldSetShort(Field field, Object target, short value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, short.class, value, true);
      field.setShort(target, value);
    } else {
      asSetableField(field, target, short.class, value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  public static void jlrFieldSetDouble(Field field, Object target, double value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, double.class, value, true);
      field.setDouble(target, value);
    } else {
      asSetableField(field, target, double.class, value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  public static void jlrFieldSetFloat(Field field, Object target, float value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, float.class, value, true);
      field.setFloat(target, value);
    } else {
      asSetableField(field, target, float.class, value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  public static void jlrFieldSetLong(Field field, Object target, long value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, long.class, value, true);
      field.setLong(target, value);
    } else {
      asSetableField(field, target, long.class, value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  public static void jlrFieldSetBoolean(Field field, Object target, boolean value) throws IllegalAccessException {
    Class<?> clazz = field.getDeclaringClass();
    ReloadableType rtype = getRType(clazz);
    if (rtype == null) {
      // Not reloadable
      field = asSetableField(field, target, boolean.class, value, true);
      field.setBoolean(target, value);
    } else {
      asSetableField(field, target, boolean.class, value, false);
      rtype.setField(target, field.getName(), Modifier.isStatic(field.getModifiers()), value);
    }
  }

  /**
   * What's the "boxed" version of a given primtive type.
   */
  private static Class<?> boxTypeFor(Class<?> primType) {
    if (primType == int.class) {
      return Integer.class;
    } else if (primType == boolean.class) {
      return Boolean.class;
    } else if (primType == byte.class) {
      return Byte.class;
    } else if (primType == char.class) {
      return Character.class;
    } else if (primType == double.class) {
      return Double.class;
    } else if (primType == float.class) {
      return Float.class;
    } else if (primType == long.class) {
      return Long.class;
    } else if (primType == short.class) {
      return Short.class;
    }
    throw new IllegalStateException("Forgotten a case in this method?");
  }

}
TOP

Related Classes of org.springsource.loaded.ri.ReflectiveInterceptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.