/*
 * NativeOperation.c
 * Implementation of native methods in class BR/unicamp/Guarana/NativeOperation
 *
 * Copyright 1997,1998 Alexandre Oliva <oliva@dcc.unicamp.br>
 */

/* guarana.unicamp.br/APIguarana/lib */

#include "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(0, "java/lang/NoSuchMethodError", "object's class does not define this method");
    if (nop->op_id.method->accflags & ACC_ABSTRACT)
      SignalError(0, "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(0, "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(0, "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();
  }
  if (nop->kind & op_wrapped) {
    if (nop->kind & op_invocation) {
      va_list args;
      HArrayOfObject *parameters;
      struct Hjava_lang_Object **arguments = nop->op_arg.argvec->args;
      struct Hjava_lang_Class *returnType;
      jint i, len;
      ARGLIST_T newargs;

      len = obj_length(parameters);
      if (len != nop->op_arg.argvec->len)
	SignalError(0, "java/lang/IllegalArgumentException", "incorrect argument count");
      newargs = ALLOC_ARGLIST_FOR(METHOD_SIGNATURE(nop->op_id.method));
      args = ARGLIST_TO_VA_LIST(METHOD_SIGNATURE(nop->op_id.method), nop->op_arg.args);

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

      SOFT_ADDREFERENCE(nop, nop->op_arg.args);
      nop->op_arg.args = 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.object_value);
      else
	guarana_unwrap(nop->op_id.field_id.field->type, &nop->op_arg, nop->op_arg.object_value);
    }
    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_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: {
    HArrayOfObject *parameters;
    struct Hjava_lang_Class *returnType;
    parameters = parseSignature2Classes(this->op_id.method->signature->data,
					&returnType);
    return returnType;
  }

  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_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;
  HArrayOfObject *parameters;
  struct Hjava_lang_Class *returnType;

  if ((this->kind & op_kind_mask) != op_method_invocation)
    return 0;
  
  meth = this->op_id.method;
  parameters = parseSignature2Classes(meth->signature->data, &returnType);
  return makeMethod(meth->class, meth - meth->class->methods, parameters, returnType);
}

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;
  HArrayOfObject *parameters;
  struct Hjava_lang_Class *returnType;

  if ((this->kind & op_kind_mask) != op_constructor_invocation)
    return 0;
  
  meth = this->op_id.method;
  parameters = parseSignature2Classes(meth->signature->data, &returnType);
  return makeConstructor(meth->class, meth - meth->class->methods, parameters);
}

HArrayOfObject* BR_unicamp_Guarana_Operation_getArguments(struct HBR_unicamp_Guarana_Operation *this_) {
  NativeOperation *this = (NativeOperation*)this_;
  char *signature;
  HArrayOfObject *parameters, *arguments;
  struct Hjava_lang_Class *returnType;
  int i, len;
  va_list args;

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

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

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

    args = ARGLIST_TO_VA_LIST(METHOD_SIGNATURE(this->op_id.method), this->op_arg.args);

    for(i = 0; i < len; ++i)
      arguments->data[0].body[i] = guarana_wrap_from_va_list((struct Hjava_lang_Class*)parameters->data[0].body[i], &args);
  }
  
  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 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.object_value;
    else
      return guarana_wrap(this->op_id.field_id.field->type, this->op_arg);

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

  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;
}
