/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package java.lang.reflect;

import org.jikesrvm.VM;
import org.jikesrvm.classloader.RVMArray;
import org.jikesrvm.classloader.RVMClass;
import org.jikesrvm.classloader.RVMField;
import org.jikesrvm.classloader.RVMMember;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.objectmodel.ObjectModel;
import org.jikesrvm.runtime.RuntimeEntrypoints;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.runtime.Reflection;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.pragma.Pure;

/**
 * Common utilities for Jikes RVM implementations of the java.lang.reflect API
 */
final class VMCommonLibrarySupport {
  /* ---- Non-inlined Exception Throwing Methods --- */
  /**
   * Method just to throw an illegal argument exception without being inlined
   */
  @NoInline
  private static void throwNewIllegalArgumentException() {
    throw new IllegalArgumentException();
  }

  /**
   * Method just to throw an illegal argument exception without being inlined
   */
  @NoInline
  private static void throwNewIllegalArgumentException(String str) {
    throw new IllegalArgumentException(str);
  }
  /**
   * Method just to throw an illegal access exception without being inlined
   */
  @NoInline
  private static void throwNewIllegalAccessException() throws IllegalAccessException{
    throw new IllegalAccessException();
  }
  /**
   * Method just to throw an illegal access exception without being inlined
   */
  @NoInline
  private static void throwNewIllegalAccessException(RVMMember member, RVMClass accessingClass) throws IllegalAccessException{
    throw new IllegalAccessException("Access to " + member + " is denied to " + accessingClass);
  }
  /**
   * Method just to throw an instantiation exception without being inlined
   */
  @NoInline
  private static void throwNewInstantiationException(String str) throws InstantiationException{
    throw new InstantiationException(str);
  }
  /**
   * Method just to throw a negative array size exception without being inlined
   */
  @NoInline
  private static void throwNewNegativeArraySizeException() {
    throw new NegativeArraySizeException();
  }
  /**
   * Method just to throw an null pointer exception without being inlined
   */
  @NoInline
  private static void throwNewNullPointerException() {
    throw new NullPointerException();
  }
  /* ---- Array Methods ---- */
  /**
   * Dynamically create an array of objects.
   *
   * @param cls guaranteed to be a valid object type
   * @param length the length of the array
   * @return the new array
   * @throws NegativeArraySizeException if dim is negative
   * @throws OutOfMemoryError if memory allocation fails
   */
  static Object createArray(Class<?> cls, int length)
    throws OutOfMemoryError, NegativeArraySizeException {

    if (cls == Void.TYPE)
      throwNewIllegalArgumentException("Cannot create new array instance for the specified arguments");

    // will raise NPE
    RVMArray arrayType = java.lang.JikesRVMSupport.getTypeForClass(cls).getArrayTypeForElementType();
    if (!arrayType.isInitialized()) {
      arrayType.resolve();
      arrayType.instantiate();
      arrayType.initialize();
    }
    // will check -ve array size
    return RuntimeEntrypoints.resolvedNewArray(length, arrayType);
  }

  /**
   * Dynamically create a multi-dimensional array of objects.
   *
   * @param cls guaranteed to be a valid object type
   * @param dimensions the lengths for the dimensions of the array
   * @return the new array
   * @throws NegativeArraySizeException if any dimensions is negative
   * @throws OutOfMemoryError if memory allocation fails
   */
  static Object createArray(Class<?> cls, int[] dimensions)
    throws OutOfMemoryError, NegativeArraySizeException {

    if ((dimensions.length == 0)||(cls == Void.TYPE));
      throwNewIllegalArgumentException("Cannot create new array instance for the specified arguments");

    // will raise NPE
    RVMArray arrayType = java.lang.JikesRVMSupport.getTypeForClass(cls).getArrayTypeForElementType();
    for (int i=1; i < dimensions.length; i++) {
      arrayType = arrayType.getArrayTypeForElementType();
    }
    // will check -ve array sizes
    return RuntimeEntrypoints.buildMDAHelper(null, dimensions, 0, arrayType);
  }

  /* ---- General Reflection Support ---- */
  /**
   * Check to see if a method declared by the accessingClass
   * should be allowed to access the argument RVMMember.
   * Assumption: member is not public.  This trivial case should
   * be approved by the caller without needing to call this method.
   */
  private static void checkAccess(RVMMember member, RVMClass accessingClass) throws IllegalAccessException {
    RVMClass declaringClass = member.getDeclaringClass();
    if (member.isPrivate()) {
      // access from the declaringClass is allowed
      if (accessingClass == declaringClass) return;
    } else if (member.isProtected()) {
      // access within the package is allowed.
      if (declaringClass.getClassLoader() == accessingClass.getClassLoader() && declaringClass.getPackageName().equals(accessingClass.getPackageName())) return;

      // access by subclasses is allowed.
      for (RVMClass cls = accessingClass; cls != null; cls = cls.getSuperClass()) {
        if (accessingClass == declaringClass) return;
      }
    } else {
      // default: access within package is allowed
      if (declaringClass.getClassLoader() == accessingClass.getClassLoader() && declaringClass.getPackageName().equals(accessingClass.getPackageName())) return;
    }
    throwNewIllegalAccessException(member, accessingClass);
  }
  /* ---- Reflective Method Invocation Support ---- */
  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static Object invoke(Object receiver, Object[] args, RVMMethod method, Method jlrMethod, RVMClass accessingClass)
      throws IllegalAccessException, IllegalArgumentException,
      ExceptionInInitializerError, InvocationTargetException {
    // validate number and types of arguments
    if (checkArguments(args, method)) {
      if (method.isStatic()) {
        return invokeStatic(receiver, args, method, jlrMethod, accessingClass);
      } else {
        return invokeVirtual(receiver, args, method, jlrMethod, accessingClass);
      }
    } else {
      Object[] compatibleArgs = makeArgumentsCompatible(args, method);
      if (method.isStatic()) {
        return invokeStatic(receiver, compatibleArgs, method, jlrMethod, accessingClass);
      } else {
        return invokeVirtual(receiver, compatibleArgs, method, jlrMethod, accessingClass);
      }
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static Object invokeStatic(Object receiver, Object[] args, RVMMethod method, Method jlrMethod, RVMClass accessingClass)
      throws IllegalAccessException, IllegalArgumentException,
      ExceptionInInitializerError, InvocationTargetException {
    // Accessibility checks
    if (!method.isPublic() && !jlrMethod.isAccessible()) {
      checkAccess(method, accessingClass);
    }

    // Forces initialization of declaring class
    RVMClass declaringClass = method.getDeclaringClass();
    if (!declaringClass.isInitialized()) {
      runClassInitializer(declaringClass);
    }

    // Invoke method
    try {
      return Reflection.invoke(method, receiver, args);
    } catch (Throwable t) {
      throw new InvocationTargetException(t);
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static Object invokeVirtual(Object receiver, Object[] args, RVMMethod method, Method jlrMethod, RVMClass accessingClass)
      throws IllegalAccessException, IllegalArgumentException,
      ExceptionInInitializerError, InvocationTargetException {
    // validate "this" argument
    if (receiver == null) {
      throwNewNullPointerException();
    }
    RVMClass declaringClass = method.getDeclaringClass();
    if (!isArgumentCompatible(declaringClass, receiver)) {
      throwNewIllegalArgumentException();
    }

    // Accessibility checks
    if (!method.isPublic() && !jlrMethod.isAccessible()) {
      checkAccess(method, accessingClass);
    }

    // find the right method to call
    RVMClass C = Magic.getObjectType(receiver).asClass();
    method = C.findVirtualMethod(method.getName(), method.getDescriptor());

    // Invoke method
    try {
      return Reflection.invoke(method, receiver, args);
    } catch (Throwable t) {
      throw new InvocationTargetException(t);
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  private static boolean checkArguments(Object[] args, RVMMethod method) throws IllegalArgumentException {
    TypeReference[] parameterTypes = method.getParameterTypes();
    if (((args == null) && (parameterTypes.length != 0)) ||
        ((args != null) && (args.length != parameterTypes.length))) {
      throwNewIllegalArgumentException("argument count mismatch");
    }
    switch (parameterTypes.length) {
    case 6:
      if (!isArgumentCompatible(parameterTypes[5].resolve(), args[5])) {
        return false;
      }
    case 5:
      if (!isArgumentCompatible(parameterTypes[4].resolve(), args[4])) {
        return false;
      }
    case 4:
      if (!isArgumentCompatible(parameterTypes[3].resolve(), args[3])) {
        return false;
      }
    case 3:
      if (!isArgumentCompatible(parameterTypes[2].resolve(), args[2])) {
        return false;
      }
    case 2:
      if (!isArgumentCompatible(parameterTypes[1].resolve(), args[1])) {
        return false;
      }
    case 1:
      if (!isArgumentCompatible(parameterTypes[0].resolve(), args[0])) {
        return false;
      }
    case 0:
      return true;
    default:
      for (int i=0, n = parameterTypes.length; i < n; ++i) {
        if (!isArgumentCompatible(parameterTypes[i].resolve(), args[i])) {
          return false;
        }
      }
      return true;
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  private static Object[] makeArgumentsCompatible(Object[] args, RVMMethod method) {
    TypeReference[] parameterTypes = method.getParameterTypes();
    int length = parameterTypes.length;
    Object[] newArgs = new Object[length];
    switch(length) {
    case 6: newArgs[5] = makeArgumentCompatible(parameterTypes[5].peekType(), args[5]);
    case 5: newArgs[4] = makeArgumentCompatible(parameterTypes[4].peekType(), args[4]);
    case 4: newArgs[3] = makeArgumentCompatible(parameterTypes[3].peekType(), args[3]);
    case 3: newArgs[2] = makeArgumentCompatible(parameterTypes[2].peekType(), args[2]);
    case 2: newArgs[1] = makeArgumentCompatible(parameterTypes[1].peekType(), args[1]);
    case 1: newArgs[0] = makeArgumentCompatible(parameterTypes[0].peekType(), args[0]);
    case 0: break;
    default: {
      for (int i = 0; i < length; ++i) {
        newArgs[i] = makeArgumentCompatible(parameterTypes[i].peekType(), args[i]);
      }
      break;
    }
    }
    return newArgs;
  }

  @NoInline
  private static void runClassInitializer(RVMClass declaringClass) throws ExceptionInInitializerError {
    try {
      RuntimeEntrypoints.initializeClassForDynamicLink(declaringClass);
    } catch (Throwable e) {
      ExceptionInInitializerError ex = new ExceptionInInitializerError();
      ex.initCause(e);
      throw ex;
    }
  }

  /**
   * Make possibly wrapped method argument compatible with expected type
   */
  @SuppressWarnings({"UnnecessaryBoxing","PMD.IntegerInstantiation"})
  @NoInline
  @Pure
  private static Object makeArgumentCompatible(RVMType expectedType, Object arg) {
    if (expectedType.isPrimitiveType()) {
      if (arg instanceof java.lang.Byte) {
        if (expectedType.isByteType()) return arg;
        if (expectedType.isShortType()) return Short.valueOf((Byte) arg);
        if (expectedType.isIntType()) return Integer.valueOf((Byte) arg);
        if (expectedType.isLongType()) return Long.valueOf((Byte) arg);
      } else if (arg instanceof java.lang.Short) {
        if (expectedType.isShortType()) return arg;
        if (expectedType.isIntType()) return Integer.valueOf((Short) arg);
        if (expectedType.isLongType()) return Long.valueOf((Short) arg);
      } else if (arg instanceof java.lang.Character) {
        if (expectedType.isCharType()) return arg;
        if (expectedType.isIntType()) return Integer.valueOf((Character) arg);
        if (expectedType.isLongType()) return Long.valueOf((Character) arg);
      } else if (arg instanceof java.lang.Integer) {
        if (expectedType.isIntType()) return arg;
        if (expectedType.isLongType()) return Long.valueOf((Integer) arg);
      } else if (arg instanceof java.lang.Float) {
        if (expectedType.isDoubleType()) return Double.valueOf((Float) arg);
      }
    }
    return arg;
  }

  /**
   * Are the 2 arguments compatible? Throw IllegalArgumentException if they
   * can't be made compatible.
   */
  @Pure
  @Inline
  private static boolean isArgumentCompatible(RVMType expectedType, Object arg) {
    if (expectedType.isPrimitiveType()) {
      return isPrimitiveArgumentCompatible(expectedType, arg);
    } else {
      if (arg == null) return true; // null is always ok
      RVMType actualType = ObjectModel.getObjectType(arg);
      if (expectedType == actualType ||
          expectedType == RVMType.JavaLangObjectType ||
          RuntimeEntrypoints.isAssignableWith(expectedType, actualType)) {
        return true;
      } else {
        throwNewIllegalArgumentException();
        return false;
      }
    }
  }

  /**
   * Is a boxed primitive argument compatible with the expected type
   */
  @Pure
  @Inline
  private static boolean isPrimitiveArgumentCompatible(RVMType expectedType, Object arg) {
    if (arg instanceof java.lang.Void) {
      if (expectedType.isVoidType()) return true;
    } else if (arg instanceof java.lang.Boolean) {
      if (expectedType.isBooleanType()) return true;
    } else if (arg instanceof java.lang.Byte) {
      if (expectedType.isByteType()) return true;
      if (expectedType.isShortType()) return false;
      if (expectedType.isIntType()) return false;
      if (expectedType.isLongType()) return false;
    } else if (arg instanceof java.lang.Short) {
      if (expectedType.isShortType()) return true;
      if (expectedType.isIntType()) return false;
      if (expectedType.isLongType()) return false;
    } else if (arg instanceof java.lang.Character) {
      if (expectedType.isCharType()) return true;
      if (expectedType.isIntType()) return false;
      if (expectedType.isLongType()) return false;
    } else if (arg instanceof java.lang.Integer) {
      if (expectedType.isIntType()) return true;
      if (expectedType.isLongType()) return false;
    } else if (arg instanceof java.lang.Long) {
      if (expectedType.isLongType()) return true;
    } else if (arg instanceof java.lang.Float) {
      if (expectedType.isFloatType()) return true;
      if (expectedType.isDoubleType()) return false;
    } else if (arg instanceof java.lang.Double) {
      if (expectedType.isDoubleType()) return true;
    }
    throwNewIllegalArgumentException();
    return false;
  }

  /* ---- Constructor Support ---- */
  /**
   * Construct an object from the given constructor args, called from the accessing class
   */
  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={0})
  static Object construct(RVMMethod constructor, Constructor<?> cons, Object[] args, RVMClass accessingClass)
  throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    // Check accessibility
    if (!constructor.isPublic() && !cons.isAccessible()) {
      checkAccess(constructor, accessingClass);
    }
    // validate number and types of arguments to constructor
    if (!checkArguments(args, constructor)) {
      args = makeArgumentsCompatible(args, constructor);
    }
    RVMClass cls = constructor.getDeclaringClass();
    if (cls.isAbstract()) {
      throwNewInstantiationException("Abstract class");
    }
    // Ensure that the class is initialized
    if (!cls.isInitialized()) {
      runClassInitializer(cls);
    }
    // Allocate an uninitialized instance;
    Object obj = RuntimeEntrypoints.resolvedNewScalar(cls);
    // Run the constructor on the instance.
    try {
      Reflection.invoke(constructor, obj, args);
    } catch (Throwable e) {
      throw new InvocationTargetException(e);
    }
    return obj;
  }
  /* ---- Constructor/Method Support ---- */
  /**
   * Convert from "vm" type system to "jdk" type system.
   */
  static Class<?>[] typesToClasses(TypeReference[] types) {
    Class<?>[] classes = new Class[types.length];
    for (int i = 0; i < types.length; i++) {
      classes[i] = types[i].resolve().getClassForType();
    }
    return classes;
  }
  /* ---- Field Support ---- */
  /**
   * Check the field in the given object can be read
   */
  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  private static void checkReadAccess(Object obj, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException,
  IllegalArgumentException,
  ExceptionInInitializerError {
    RVMClass declaringClass = field.getDeclaringClass();
    if (!field.isStatic()) {
      if (obj == null) {
        throwNewNullPointerException();
      }

      RVMType objType = ObjectModel.getObjectType(obj);
      if (objType != declaringClass && !RuntimeEntrypoints.isAssignableWith(declaringClass, objType)) {
        throwNewIllegalArgumentException();
      }
    }

    if (!field.isPublic() && !jlrField.isAccessible()) {
      checkAccess(field, accessingClass);
    }

    if (field.isStatic() && !declaringClass.isInitialized()) {
      runClassInitializer(declaringClass);
    }
  }

  /**
   * Check the field in the given object can be written to
   */
  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  private static void checkWriteAccess(Object obj, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException,
  IllegalArgumentException,
  ExceptionInInitializerError {

    RVMClass declaringClass = field.getDeclaringClass();
    if (!field.isStatic()) {
      if (obj == null) {
        throwNewNullPointerException();
      }

      RVMType objType = ObjectModel.getObjectType(obj);
      if (objType != declaringClass && !RuntimeEntrypoints.isAssignableWith(declaringClass, objType)) {
        throwNewIllegalArgumentException();
      }
    }

    if (!field.isPublic() && !jlrField.isAccessible()) {
      checkAccess(field, accessingClass);
    }

    if (field.isFinal() && (!jlrField.isAccessible() || field.isStatic()))
      throwNewIllegalAccessException();

    if (field.isStatic() && !declaringClass.isInitialized()) {
      runClassInitializer(declaringClass);
    }
  }

  /**
   * Read a field from an object
   * @param object to read from
   * @param field to be read
   * @param jlrField API version of field
   * @param accessingClass class performing access
   * @return the field's value
   * @throws IllegalAccessException
   * @throws IllegalArgumentException
   */
  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static Object get(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);

    if (field.isReferenceType()) {
      return field.getObjectValueUnchecked(object);
    }
    TypeReference type = field.getType();
    if (type.isIntType()) {
      return field.getIntValueUnchecked(object);
    } else if (type.isCharType()) {
      return field.getCharValueUnchecked(object);
    } else if (type.isShortType()) {
      return field.getShortValueUnchecked(object);
    } else if (type.isLongType()) {
      return field.getLongValueUnchecked(object);
    } else if (type.isByteType()) {
      return field.getByteValueUnchecked(object);
    } else if (type.isBooleanType()) {
      return field.getBooleanValueUnchecked(object);
    } else if (type.isDoubleType()) {
      return field.getDoubleValueUnchecked(object);
    } else {
      if (VM.VerifyAssertions) VM._assert(type.isFloatType());
      return field.getFloatValueUnchecked(object);
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static boolean getBoolean(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);
    TypeReference type = field.getType();
    if (!type.isBooleanType()) throwNewIllegalArgumentException("field type mismatch");
    return field.getBooleanValueUnchecked(object);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static byte getByte(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);
    TypeReference type = field.getType();
    if (!type.isByteType()) throwNewIllegalArgumentException("field type mismatch");
    return field.getByteValueUnchecked(object);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static char getChar(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);
    TypeReference type = field.getType();
    if (!type.isCharType()) throwNewIllegalArgumentException("field type mismatch");
    return field.getCharValueUnchecked(object);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static double getDouble(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);
    TypeReference type = field.getType();
    if (type.isDoubleType()) {
      return field.getDoubleValueUnchecked(object);
    } else if (type.isFloatType()) {
      return (double)field.getFloatValueUnchecked(object);
    } else if (type.isLongType()) {
      return (double)field.getLongValueUnchecked(object);
    } else if (type.isIntType()) {
      return (double)field.getIntValueUnchecked(object);
    } else if (type.isShortType()) {
      return (double)field.getShortValueUnchecked(object);
    } else if (type.isCharType()) {
      return (double)field.getCharValueUnchecked(object);
    } else if (type.isByteType()) {
      return (double)field.getByteValueUnchecked(object);
    } else {
      throwNewIllegalArgumentException("field type mismatch");
      return 0.0d;
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static float getFloat(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);
    TypeReference type = field.getType();
    if (type.isFloatType()) {
      return field.getFloatValueUnchecked(object);
    } else if (type.isLongType()) {
      return (float)field.getLongValueUnchecked(object);
    } else if (type.isIntType()) {
      return (float)field.getIntValueUnchecked(object);
    } else if (type.isShortType()) {
      return (float)field.getShortValueUnchecked(object);
    } else if (type.isCharType()) {
      return (float)field.getCharValueUnchecked(object);
    } else if (type.isByteType()) {
      return (float)field.getByteValueUnchecked(object);
    } else {
      throwNewIllegalArgumentException("field type mismatch");
      return 0.0f;
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static int getInt(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);
    TypeReference type = field.getType();
    if (type.isIntType()) {
      return field.getIntValueUnchecked(object);
    } else if (type.isShortType()) {
      return (int)field.getShortValueUnchecked(object);
    } else if (type.isCharType()) {
      return (int)field.getCharValueUnchecked(object);
    } else if (type.isByteType()) {
      return (int)field.getByteValueUnchecked(object);
    } else {
      throwNewIllegalArgumentException("field type mismatch");
      return 0;
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static long getLong(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);
    TypeReference type = field.getType();
    if (type.isLongType()) {
      return field.getLongValueUnchecked(object);
    } else if (type.isIntType()) {
      return (long)field.getIntValueUnchecked(object);
    } else if (type.isShortType()) {
      return (long)field.getShortValueUnchecked(object);
    } else if (type.isCharType()) {
      return (long)field.getCharValueUnchecked(object);
    } else if (type.isByteType()) {
      return (long)field.getByteValueUnchecked(object);
    } else {
      throwNewIllegalArgumentException("field type mismatch");
      return 0L;
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={1})
  static short getShort(Object object, RVMField field, Field jlrField, RVMClass accessingClass) throws IllegalAccessException, IllegalArgumentException {
    checkReadAccess(object, field, jlrField, accessingClass);
    TypeReference type = field.getType();
    if (type.isShortType()) {
      return field.getShortValueUnchecked(object);
    } else if (type.isByteType()) {
      return (short)field.getByteValueUnchecked(object);
    } else {
      throwNewIllegalArgumentException("field type mismatch");
      return 0;
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void set(Object object, Object value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException     {
    checkWriteAccess(object, field, jlrField, accessingClass);

    if (field.isReferenceType()) {
      if (value != null) {
        RVMType valueType = ObjectModel.getObjectType(value);
        RVMType fieldType = null;
        try {
          fieldType = field.getType().resolve();
        } catch (NoClassDefFoundError e) {
          throwNewIllegalArgumentException("field type mismatch");
        }
        if (fieldType != valueType &&
            !RuntimeEntrypoints.isAssignableWith(fieldType, valueType)) {
          throwNewIllegalArgumentException("field type mismatch");
        }
      }
      field.setObjectValueUnchecked(object, value);
    } else if (value instanceof Character) {
      setCharInternal(object, (Character) value, field);
    } else if (value instanceof Double) {
      setDoubleInternal(object, (Double) value, field);
    } else if (value instanceof Float) {
      setFloatInternal(object, (Float) value, field);
    } else if (value instanceof Long) {
      setLongInternal(object, (Long) value, field);
    } else if (value instanceof Integer) {
      setIntInternal(object, (Integer) value, field);
    } else if (value instanceof Short) {
      setShortInternal(object, (Short) value, field);
    } else if (value instanceof Byte) {
      setByteInternal(object, (Byte) value, field);
    } else if (value instanceof Boolean) {
      setBooleanInternal(object, (Boolean) value, field);
    } else {
      throw new IllegalArgumentException("field type mismatch");
    }
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void setBoolean(Object object, boolean value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException    {
    checkWriteAccess(object, field, jlrField, accessingClass);
    setBooleanInternal(object, value, field);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void setByte(Object object, byte value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException    {
    checkWriteAccess(object, field, jlrField, accessingClass);
    setByteInternal(object, value, field);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void setChar(Object object, char value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException    {
    checkWriteAccess(object, field, jlrField, accessingClass);
    setCharInternal(object, value, field);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void setDouble(Object object, double value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException    {
    checkWriteAccess(object, field, jlrField, accessingClass);
    setDoubleInternal(object, value, field);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void setFloat(Object object, float value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException    {
    checkWriteAccess(object, field, jlrField, accessingClass);
    setFloatInternal(object, value, field);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void setInt(Object object, int value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException    {
    checkWriteAccess(object, field, jlrField, accessingClass);
    setIntInternal(object, value, field);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void setLong(Object object, long value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException    {
    checkWriteAccess(object, field, jlrField, accessingClass);
    setLongInternal(object, value, field);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  static void setShort(Object object, short value, RVMField field, Field jlrField, RVMClass accessingClass)
  throws IllegalAccessException, IllegalArgumentException   {
    checkWriteAccess(object, field, jlrField, accessingClass);
    setShortInternal(object, value, field);
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static void setBooleanInternal(Object object, boolean value, RVMField field)
  throws IllegalArgumentException {
    TypeReference type = field.getType();
    if (type.isBooleanType())
      field.setBooleanValueUnchecked(object, value);
    else
      throwNewIllegalArgumentException("field type mismatch");
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static void setByteInternal(Object object, byte value, RVMField field) throws IllegalArgumentException {
    TypeReference type = field.getType();
    if (type.isByteType())
      field.setByteValueUnchecked(object, value);
    else if (type.isLongType())
      field.setLongValueUnchecked(object, (long)value);
    else if (type.isIntType())
      field.setIntValueUnchecked(object, (int)value);
    else if (type.isShortType())
      field.setShortValueUnchecked(object, (short)value);
    else if (type.isCharType())
      field.setCharValueUnchecked(object, (char)value);
    else if (type.isDoubleType())
      field.setDoubleValueUnchecked(object, (double)value);
    else if (type.isFloatType())
      field.setFloatValueUnchecked(object, (float)value);
    else
      throwNewIllegalArgumentException("field type mismatch");
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static void setCharInternal(Object object, char value, RVMField field) throws IllegalArgumentException {
    TypeReference type = field.getType();
    if (type.isCharType())
      field.setCharValueUnchecked(object, value);
    else if (type.isLongType())
      field.setLongValueUnchecked(object, (long)value);
    else if (type.isIntType())
      field.setIntValueUnchecked(object, (int)value);
    else if (type.isShortType())
      field.setShortValueUnchecked(object, (short)value);
    else if (type.isDoubleType())
      field.setDoubleValueUnchecked(object, (double)value);
    else if (type.isFloatType())
      field.setFloatValueUnchecked(object, (float)value);
    else
      throwNewIllegalArgumentException("field type mismatch");
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static void setDoubleInternal(Object object, double value, RVMField field) throws IllegalArgumentException {
    TypeReference type = field.getType();
    if (type.isDoubleType())
      field.setDoubleValueUnchecked(object, value);
    else
      throwNewIllegalArgumentException("field type mismatch");
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static void setFloatInternal(Object object, float value, RVMField field) throws IllegalArgumentException {
    TypeReference type = field.getType();
    if (type.isFloatType())
      field.setFloatValueUnchecked(object, value);
    else if (type.isDoubleType())
      field.setDoubleValueUnchecked(object, (double)value);
    else
      throwNewIllegalArgumentException("field type mismatch");
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static void setIntInternal(Object object, int value, RVMField field) throws IllegalArgumentException {
    TypeReference type = field.getType();
    if (type.isIntType())
      field.setIntValueUnchecked(object, value);
    else if (type.isLongType())
      field.setLongValueUnchecked(object, (long) value);
    else if (type.isDoubleType())
      field.setDoubleValueUnchecked(object, (double)value);
    else if (type.isFloatType())
      field.setFloatValueUnchecked(object, (float)value);
    else
      throwNewIllegalArgumentException("field type mismatch");
  }

  @Inline(value=Inline.When.ArgumentsAreConstant, arguments={2})
  private static void setLongInternal(Object object, long value, RVMField field) throws IllegalArgumentException {
    TypeReference type = field.getType();
    if (type.isLongType())
      field.setLongValueUnchecked(object, value);
    else if (type.isDoubleType())
      field.setDoubleValueUnchecked(object, (double)value);
    else if (type.isFloatType())
      field.setFloatValueUnchecked(object, (float)value);
    else
      throwNewIllegalArgumentException("field type mismatch");
  }

  private static void setShortInternal(Object object, short value, RVMField field) throws IllegalArgumentException {
    TypeReference type = field.getType();
    if (type.isShortType())
      field.setShortValueUnchecked(object, value);
    else if (type.isLongType())
      field.setLongValueUnchecked(object, (long)value);
    else if (type.isIntType())
      field.setIntValueUnchecked(object, (int)value);
    else if (type.isDoubleType())
      field.setDoubleValueUnchecked(object, (double)value);
    else if (type.isFloatType())
      field.setFloatValueUnchecked(object, (float)value);
    else
      throwNewIllegalArgumentException("field type mismatch");
  }
}
