/* -*- Mode: C; c-file-style: "gnu" -*-
   field.c -- native methods for java/lang/reflect/Field
   Created: Chris Toshok <toshok@hungry.com>, 12-Feb-1998.
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1998, 1999 The Hungry Programmers

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "jni.h"
#include "ClazzFile.h"
#include "objects.h"
#include "interp.h"
#include "exceptions.h"

static void
get_fld(FieldStruct *f, jobject obj, jvalue *value)
{
  if (f->access_flags & ACC_STATIC) {
    get_static_field(f->clazz, f, value);
  }
  else {
    get_instance_field(obj, f, value);
  }
}

static void
set_fld(FieldStruct *f, jobject obj, jvalue value)
{
  if (f->access_flags & ACC_STATIC) {
    set_static_field(f->clazz, f, value);
  }
  else {
    set_instance_field(obj, f, value);
  }
}

JNIEXPORT jint JNICALL
Java_java_lang_reflect_Field_getModifiers(JNIEnv *env,
					  jobject field)
{
  return (*env)->FromReflectedField(env, field)->access_flags;
}

static void
assign_double(int expected_type, jvalue *value, jdouble v)
{
  switch (expected_type) {
  case SIG_JFLOAT:
    value->f = (jfloat) v;
    break;
  case SIG_JDOUBLE:
    value->d = v;
    break;
  }
}

static void
assign_long(int expected_type, jvalue *value, jlong v)
{
  switch (expected_type) {
  case SIG_JBYTE:
    value->b = (jbyte) v;
    break;
  case SIG_JCHAR:
    value->c = (jchar) v;
    break;
  case SIG_JSHORT:
    value->s = (jshort) v;
    break;
  case SIG_JINT:
    value->i = (jint) v;
    break;
  case SIG_JLONG:
    value->j = v;
    break;
  }
}

static void
widen(JNIEnv *env, FieldStruct *field, int expected_type, jvalue *value)
{
  switch (field->java_type) {
  case SIG_JBOOLEAN:
  case SIG_JOBJECT:
    if (expected_type != field->java_type) {
      break;
    }
    return;
  case SIG_JBYTE:
    switch (expected_type) {
    case SIG_JBYTE:
    case SIG_JCHAR:
    case SIG_JSHORT:
    case SIG_JINT:
    case SIG_JLONG:
    case SIG_JFLOAT:
    case SIG_JDOUBLE:
      assign_long(expected_type, value, value->b);
      return;
    default:
      break;
    }
    break;
  case SIG_JCHAR:
    switch (expected_type) {
    case SIG_JCHAR:
    case SIG_JSHORT:
    case SIG_JINT:
    case SIG_JLONG:
    case SIG_JFLOAT:
    case SIG_JDOUBLE:
      assign_long(expected_type, value, value->c);
      return;
    default:
      break;
    }
    break;
  case SIG_JSHORT:
    switch (expected_type) {
    case SIG_JSHORT:
    case SIG_JINT:
    case SIG_JLONG:
    case SIG_JFLOAT:
    case SIG_JDOUBLE:
      assign_long(expected_type, value, value->s);
      return;
    default:
      break;
    }
    break;
  case SIG_JINT:
    switch (expected_type) {
    case SIG_JINT:
    case SIG_JLONG:
    case SIG_JFLOAT:
    case SIG_JDOUBLE:
      assign_long(expected_type, value, value->i);
      return;
    default:
      break;
    }
    break;
  case SIG_JLONG:
    switch (expected_type) {
    case SIG_JLONG:
    case SIG_JFLOAT:
    case SIG_JDOUBLE:
      assign_long(expected_type, value, value->j);
      return;
    default:
      break;
    }
    break;
  case SIG_JFLOAT:
    switch (expected_type) {
    case SIG_JFLOAT:
    case SIG_JDOUBLE:
      assign_double(expected_type, value, value->f);
      return;
    default:
      break;
    }
    break;
  case SIG_JDOUBLE:
    if (expected_type == SIG_JDOUBLE) {
      assign_double(expected_type, value, value->d);
      return;
    }
    break;
  default:
    break;
  }
  throw_Exception(env, "java/lang/IllegalArgumentException",
		  "field type mismatch in java/lang/reflect/Field.getXxx()");
}

JNIEXPORT jobject JNICALL
Java_java_lang_reflect_Field_get(JNIEnv *env,
				 jobject field,
				 jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  return value.l;
}

JNIEXPORT jboolean JNICALL
Java_java_lang_reflect_Field_getBoolean(JNIEnv *env,
					jobject field,
					jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  return value.z;
}

JNIEXPORT jbyte JNICALL
Java_java_lang_reflect_Field_getByte(JNIEnv *env,
				     jobject field,
				     jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  widen(env, f, SIG_JBYTE, &value);
  return value.b;
}

JNIEXPORT jchar JNICALL
Java_java_lang_reflect_Field_getChar(JNIEnv *env,
				     jobject field,
				     jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  widen(env, f, SIG_JCHAR, &value);
  return value.c;
}

JNIEXPORT jshort JNICALL
Java_java_lang_reflect_Field_getShort(JNIEnv *env,
				      jobject field,
				      jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  widen(env, f, SIG_JSHORT, &value);
  return value.s;
}

JNIEXPORT jint JNICALL
Java_java_lang_reflect_Field_getInt(JNIEnv *env,
				    jobject field,
				    jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  widen(env, f, SIG_JINT, &value);
  return value.i;
}

JNIEXPORT jlong JNICALL
Java_java_lang_reflect_Field_getLong(JNIEnv *env,
				     jobject field,
				     jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  widen(env, f, SIG_JLONG, &value);
  return value.j;
}

JNIEXPORT jfloat JNICALL
Java_java_lang_reflect_Field_getFloat(JNIEnv *env,
				      jobject field,
				      jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  widen(env, f, SIG_JFLOAT, &value);
  return value.f;
}

JNIEXPORT jdouble JNICALL
Java_java_lang_reflect_Field_getDouble(JNIEnv *env,
				       jobject field,
				       jobject obj)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue value;
  get_fld(f, obj, &value);
  widen(env, f, SIG_JDOUBLE, &value);
  return value.d;
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_set(JNIEnv *env,
				 jobject field,
				 jobject obj,
				 jobject value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setBoolean()");
      return;
    }

  val.l = value;
  set_fld(f, obj, val);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_setBoolean(JNIEnv *env,
					jobject field,
					jobject obj,
					jboolean value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setBoolean()");
      return;
    }

  val.z = value;
  set_fld(f, obj, val);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_setByte(JNIEnv *env,
				     jobject field,
				     jobject obj,
				     jbyte value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setByte()");
      return;
    }

  val.b = value;
  set_fld(f, obj, val);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_setChar(JNIEnv *env,
				     jobject field,
				     jobject obj,
				     jchar value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setChar()");
      return;
    }

  val.c = value;
  set_fld(f, obj, val);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_setShort(JNIEnv *env,
				      jobject field,
				      jobject obj,
				      jshort value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setShort()");
      return;
    }

  val.s = value;
  set_fld(f, obj, val);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_setInt(JNIEnv *env,
				    jobject field,
				    jobject obj,
				    jint value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setInt()");
      return;
    }

  val.i = value;
  set_fld(f, obj, val);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_setLong(JNIEnv *env,
				     jobject field,
				     jobject obj,
				     jlong value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setLong()");
      return;
    }

  val.j = value;
  set_fld(f, obj, val);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_setFloat(JNIEnv *env,
				      jobject field,
				      jobject obj,
				      jfloat value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setFloat()");
      return;
    }

  val.f = value;
  set_fld(f, obj, val);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Field_setDouble(JNIEnv *env,
				       jobject field,
				       jobject obj,
				       jdouble value)
{
  FieldStruct *f = (*env)->FromReflectedField(env, field);
  jvalue val;

  if (f->access_flags & ACC_FINAL)
    {
      throw_Exception(env, "java/lang/IllegalAccessException",
		      "in java/lang/reflect/Field.setDouble()");
      return;
    }

  val.d = value;
  set_fld(f, obj, val);
}
