/*
 * NativeOperation.c
 * Implementation of native methods in class BR/unicamp/Guarana/NativeOperation
 *
 * Copyright 1997,1998 Alexandre Oliva <oliva@dcc.unicamp.br>
 *
 * This file is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "guarana/GuaraNative.h"

void BR_unicamp_Guarana_Operation_validate(struct HBR_unicamp_Guarana_Operation *op) {
  NativeOperation* nop = (NativeOperation *)op;
  if (nop->kind & op_invocation) {
    if ((nop->op_id.method->accflags & ACC_STATIC)
	? (jobject)nop->op_id.method->class != (jobject)nop->object
	: !instanceof(nop->op_id.method->class, OBJECT_CLASS(nop->object)))
      SignalError("java/lang/NoSuchMethodError", "object's class does not define this method");
    if (nop->op_id.method->accflags & ACC_ABSTRACT)
      SignalError("java/lang/AbstractMethodError", "cannot invoke abstract method");
  } else if (nop->kind & op_field) {
    if ((nop->op_id.field_id.field->accflags & ACC_STATIC)
	? (jobject)nop->op_id.field_id.clazz != (jobject)nop->object
	: !instanceof(nop->op_id.field_id.clazz, OBJECT_CLASS(nop->object)))
      SignalError("java/lang/NoSuchMethodError", "object's class does not define this method");
  } else if (nop->kind & op_array) {
    if (!CLASS_IS_ARRAY(OBJECT_CLASS(nop->object)))
      SignalError("java/lang/IllegalArgumentException", "target object must be an array");
    if ((nop->kind & op_kind_mask) != op_array_length &&
	(nop->op_id.index < 0 ||
	 nop->op_id.index >= ARRAY_SIZE(nop->object)))
      soft_badarrayindex();
  } else if ((nop->kind & op_kind_mask) == op_nop ||
	     (nop->kind & op_synchronization) != 0)
    return;
  else
    SignalError("java/lang/IllegalArgumentException",
		"unknown operation type");
  if (nop->kind & op_wrapped) {
    if (nop->kind & op_invocation) {
      jvalue *args, *newargs;
      HArrayOfObject *parameters;
      struct Hjava_lang_Object **arguments =
	JREF_TO_ARGVEC(nop->op_arg.l)->args;
      jint i, len;
      char *sig;

      parameters = p_makeParameters(nop->op_id.method);
      len = obj_length(parameters);
      if (len != JREF_TO_ARGVEC(nop->op_arg.l)->len)
	SignalError("java/lang/IllegalArgumentException", "incorrect argument count");
      sig = nop->op_id.method->signature->data;
      args = newargs = ALLOC_ARGVEC_FOR(sizeofSig(&sig, true));

      for(i = 0; i < len; ++i)
	args += guarana_unwrap((struct Hjava_lang_Class*)parameters->data[0].body[i], args, arguments[i]);

      SOFT_ADDREFERENCE(nop, nop->op_arg.l);
      nop->op_arg.l = newargs;
    } else if (nop->kind & op_write) {
      if (nop->kind & op_array)
	guarana_unwrap(CLASS_ELEMENT_TYPE(OBJECT_CLASS(nop->object)), &nop->op_arg, nop->op_arg.l);
      else
	guarana_unwrap(nop->op_id.field_id.field->type, &nop->op_arg, nop->op_arg.l);
    }
    nop->kind &= ~op_wrapped;
  }
}

struct Hjava_lang_Object* BR_unicamp_Guarana_Operation_getObject(struct HBR_unicamp_Guarana_Operation *this) {
  return ((NativeOperation*)this)->object;
}

struct Hjava_lang_Thread* BR_unicamp_Guarana_Operation_getThread(struct HBR_unicamp_Guarana_Operation *this) {
  return ((NativeOperation*)this)->thread;
}

struct Hjava_lang_Class* BR_unicamp_Guarana_Operation_getType(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;

  switch(this->kind & op_kind_mask) {
  case op_nop:
  case op_constructor_invocation:
  case op_monitor_enter:
  case op_monitor_exit:
  case op_field_write:
  case op_array_write:
    return voidClass;
    
  case op_array_length:
    return intClass;

  case op_method_invocation: {
    return p_makeReturn(this->op_id.method);
  }

  case op_field_read:
    return this->op_id.field_id.field->type;

  case op_array_read:
    return CLASS_ELEMENT_TYPE(OBJECT_CLASS(this->object));

  default:
    unimp("Operation.getType: unknown operation type");
  }
}

jbool BR_unicamp_Guarana_Operation_isClassOperation(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;

  switch(this->kind & op_kind_mask) {
  case op_nop:
  case op_constructor_invocation:
  case op_monitor_enter:
  case op_monitor_exit:
  case op_array_read:
  case op_array_write:
  case op_array_length:
    return false;

  case op_method_invocation:
    return (this->op_id.method->accflags & ACC_STATIC) != 0;
    
  case op_field_read:
  case op_field_write:
    return (this->op_id.field_id.field->accflags & ACC_STATIC) != 0;

  default:
    unimp("Operation.isClassOperation: unknown operation type");
  }
}

jint BR_unicamp_Guarana_Operation_getOpType(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_kind_mask);
}

jbool BR_unicamp_Guarana_Operation_isMethodInvocation(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_kind_mask) == op_method_invocation;
}

struct Hjava_lang_reflect_Method* BR_unicamp_Guarana_Operation_getMethod(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;
  methods *meth;

  if ((this->kind & op_kind_mask) != op_method_invocation)
    return 0;
  
  meth = this->op_id.method;
  return p_makeMethod(meth->class, meth - meth->class->methods);
}

jbool BR_unicamp_Guarana_Operation_isConstructorInvocation(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_kind_mask) == op_constructor_invocation;
}

struct Hjava_lang_reflect_Constructor* BR_unicamp_Guarana_Operation_getConstructor(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;
  methods *meth;

  if ((this->kind & op_kind_mask) != op_constructor_invocation)
    return 0;
  
  meth = this->op_id.method;
  return p_makeConstructor(meth->class, meth - meth->class->methods);
}

HArrayOfObject* BR_unicamp_Guarana_Operation_getArguments(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;
  HArrayOfObject *arguments;
  int i, len;
  jvalue *args;

  if ((this->kind & op_invocation) == 0)
    return 0;

  if (this->kind & op_wrapped) {
    len = JREF_TO_ARGVEC(this->op_arg.l)->len;
    arguments = (HArrayOfObject*)AllocObjectArray(len, "Ljava/lang/Object;");

    for(i = 0; i < len; ++i)
      arguments->data[0].body[i] = JREF_TO_ARGVEC(this->op_arg.l)->args[i];
  } else {
    HArrayOfObject *parameters = p_makeParameters(this->op_id.method);
    len = obj_length(parameters);
    arguments = (HArrayOfObject*)AllocObjectArray(len, "Ljava/lang/Object;");

    args = (jvalue*)this->op_arg.l;

    for(i = 0; i < len; ++i) {
      int size;
      arguments->data[0].body[i] = guarana_wrap((struct Hjava_lang_Class*)parameters->data[0].body[i], *args, &size);
      args += size;
    }
  }
  
  return arguments;
}

jbool BR_unicamp_Guarana_Operation_isSynchronization(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_synchronization) != 0;
}

jbool BR_unicamp_Guarana_Operation_isMonitorEnter(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_kind_mask) == op_monitor_enter;
}

jbool BR_unicamp_Guarana_Operation_isMonitorExit(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_kind_mask) == op_monitor_exit;
}

jbool BR_unicamp_Guarana_Operation_isReadOperation(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_read_write_mask) == op_read;
}

jbool BR_unicamp_Guarana_Operation_isWriteOperation(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_read_write_mask) == op_write;
}

jbool BR_unicamp_Guarana_Operation_isFieldOperation(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_field) != 0;
}

struct Hjava_lang_reflect_Field* BR_unicamp_Guarana_Operation_getField(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;
  Field *field;
  struct Hjava_lang_Class *clazz;

  if (!(this->kind & op_field))
    return 0;
  
  field = this->op_id.field_id.field;
  clazz = this->op_id.field_id.clazz;
  return p_makeField(clazz, field - clazz->fields);
}

jbool BR_unicamp_Guarana_Operation_isArrayOperation(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_array) != 0;
}

jbool BR_unicamp_Guarana_Operation_isArrayLengthOperation(struct HBR_unicamp_Guarana_Operation *this) {
  return (((NativeOperation*)this)->kind & op_kind_mask) == op_array_length;
}

jint BR_unicamp_Guarana_Operation_getArrayIndex(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;
  if ((this->kind & op_array_mask) != op_array)
    return -1;
  return this->op_id.index;
}

struct Hjava_lang_Object* BR_unicamp_Guarana_Operation_getValue(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;

  switch(this->kind & op_kind_mask) {
  case op_method_invocation:
  case op_constructor_invocation:
  case op_monitor_enter:
  case op_monitor_exit:
  case op_field_read:
  case op_array_read:    
  case op_array_length:
    return 0;

  case op_field_write:
    if (this->kind & op_wrapped)
      return this->op_arg.l;
    else
      return guarana_wrap(this->op_id.field_id.field->type, this->op_arg, 0);

  case op_array_write:
    if (this->kind & op_wrapped)
      return this->op_arg.l;
    else
      return guarana_wrap(CLASS_ELEMENT_TYPE(OBJECT_CLASS(this->object)),
			  this->op_arg, 0);

  default:
    unimp("Operation.getValue: unknown operation type");
  }
}

jbool BR_unicamp_Guarana_Operation_isReplacement(struct HBR_unicamp_Guarana_Operation *this) {
  return ((NativeOperation*)this)->replaced != 0;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_Operation_getReplaced(struct HBR_unicamp_Guarana_Operation *this) {
  return ((NativeOperation*)this)->replaced;
}
