/*
 * 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?");
	}

}