/*
 * OperationFactoryInternal.c
 * Implementation of native methods in class
 * BR.unicamp.Guarana.OperationFactoryInternal
 *
 * 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"

#define checkTarget(obj, replaced) do { \
  if ((replaced) && (obj) != ((NativeOperation*)(replaced))->object) \
    SignalError("java/lang/IllegalAccessException", \
		"target Object changed"); \
} while (0)

#define checkCompat(clazz, op) do { \
  if (op && (((NativeOperation *)op)->kind & op_performed) == 0) \
      SignalError("java/lang/IllegalArgumentException", "cannot replace unhandled operation"); \
  if (op && (((NativeOperation *)op)->kind & op_kind_mask) != op_nop) { \
    Hjava_lang_Class *orig = BR_unicamp_Guarana_Operation_getType(op); \
    if (!do_execute_java_method((jobject)orig, "isAssignableFrom", "(Ljava/lang/Class;)Z", 0, 0, clazz).i) \
      SignalError("java/lang/IllegalArgumentException", "incompatible result types"); \
  } \
} while (0)

#define checkExcept(meth_, op_) do if (op_) { \
  NativeOperation *op = (NativeOperation*)op_; \
  Hjava_lang_Class *mclass = meth_->class; \
  constIndex *excepts = meth_->declared_exceptions; \
  int i, n = meth_->ndeclared_exceptions; \
  for(i = 0; i < n; ++i) { \
    static Hjava_lang_Class *javaLangError = 0; \
    static Hjava_lang_Class *javaLangRuntimeException = 0; \
    Hjava_lang_Class *xclass = getClass(excepts[i], mclass); \
    if (javaLangError == 0) \
      javaLangError = lookupClass("java/lang/Error"); \
    if (instanceof(javaLangError, xclass)) \
      continue; \
    if (javaLangRuntimeException == 0) \
      javaLangRuntimeException = lookupClass("java/lang/RuntimeException"); \
    if (instanceof(javaLangRuntimeException, xclass)) \
      continue; \
    switch (op->kind & op_kind_mask) { \
    case op_nop: \
      break; \
       \
    case op_method_invocation: \
    case op_constructor_invocation: \
      { \
	methods *meth = op->op_id.method; \
	Hjava_lang_Class *decclass = meth->class; \
	constIndex *excepts = meth->declared_exceptions; \
	int j, nr = meth->ndeclared_exceptions; \
	for (j = 0; j < nr; ++j) { \
	  if (instanceof(getClass(excepts[j], decclass), xclass)) \
	    break; \
	} \
	if (j < nr) \
	  break; \
      } \
      /* do not break */ \
	 \
    default: \
      SignalError("java/lang/IllegalArgumentException", "invalid exception type"); \
    } \
  } \
} while (0)

#define checkMobj(this) do { \
  if (this->obj->meta_object != this->mobj) \
    SignalError("java/lang/IllegalAccessException", "MetaObject changed"); \
} while (0)

struct Hjava_lang_Object* BR_unicamp_Guarana_OperationFactoryInternal_getObject(struct HBR_unicamp_Guarana_OperationFactoryInternal* this_) {
  OperationFactory *this = (OperationFactory*)this_;
  return this->obj;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_nop(struct HBR_unicamp_Guarana_OperationFactoryInternal* this_) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  op = guarana_new_native_operation(this->obj, getCurrentThread(), 0);
  op->kind |= op_nop;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_invoke(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, struct Hjava_lang_reflect_Method *meth_, HArrayOfObject *arguments, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  jint i, len;
  struct wrapArgVec *argvec;
  methods *meth = CLASS_METHODS(unhand(meth_)->clazz) + unhand(meth_)->slot;
  
  checkMobj(this);
  checkCompat(unhand(meth_)->returnType, repl);
  checkTarget(this->obj, repl);
  checkExcept(meth, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_method_invocation | op_wrapped;
  op->op_id.method = meth;

  /* FIXME: access array reflexively? */
  len = obj_length(arguments);
  argvec = gc_malloc(sizeof(*argvec) + (len-1)*sizeof(argvec->args), GC_ALLOC_REFARRAY);
  argvec->len = len;

  for(i = 0; i < len; ++i)
    argvec->args[i] = arguments->data[i].body[0];
  
  SOFT_ADDREFERENCE(op, argvec);
  op->op_arg.l = argvec;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_construct(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, struct Hjava_lang_reflect_Constructor *constr, HArrayOfObject *arguments, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  int i, len;
  struct wrapArgVec *argvec;
  methods *meth = CLASS_METHODS(unhand(constr)->clazz) + unhand(constr)->slot;
  
  checkMobj(this);
  checkCompat(&voidClass, repl);
  checkTarget(this->obj, repl);
  checkExcept(meth, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_constructor_invocation | op_wrapped;
  op->op_id.method = meth;

  /* FIXME: access array reflexively? */
  len = obj_length(arguments);
  argvec = gc_malloc(sizeof(*argvec) + (len-1)*sizeof(argvec->args), GC_ALLOC_REFARRAY);
  argvec->len = len;
  
  for(i = 0; i < len; ++i)
    argvec->args[i] = arguments->data[i].body[0];
  
  SOFT_ADDREFERENCE(op, argvec);
  op->op_arg.l = argvec;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_monitorEnter(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  
  checkMobj(this);
  checkCompat(&voidClass, repl);
  checkTarget(this->obj, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_monitor_enter;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_monitorExit(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  
  checkMobj(this);
  checkCompat(&voidClass, repl);
  checkTarget(this->obj, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_monitor_exit;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_read(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, struct Hjava_lang_reflect_Field *field, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  
  checkMobj(this);
  checkCompat(unhand(field)->type, repl);
  checkTarget(this->obj, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_field_read;
  op->op_id.field_id.clazz = unhand(field)->clazz;
  op->op_id.field_id.field = unhand(field)->clazz->fields + unhand(field)->slot;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_write(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, struct Hjava_lang_reflect_Field *field, struct Hjava_lang_Object *value, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  
  checkMobj(this);
  checkCompat(&voidClass, repl);
  checkTarget(this->obj, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_field_read | op_wrapped;
  op->op_id.field_id.clazz = unhand(field)->clazz;
  op->op_id.field_id.field = unhand(field)->clazz->fields + unhand(field)->slot;
  op->op_arg.l = value;
  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_length(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  
  checkMobj(this);
  checkCompat(&intClass, repl);
  checkTarget(this->obj, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_array_length;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_readElement(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, jint elmt, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  
  checkMobj(this);
  checkCompat(CLASS_ELEMENT_TYPE(OBJECT_CLASS(this->obj)), repl);
  checkTarget(this->obj, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_array_read;
  op->op_id.index = elmt;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_OperationFactoryInternal_writeElement(struct HBR_unicamp_Guarana_OperationFactoryInternal *this_, jint elmt, struct Hjava_lang_Object *value, struct HBR_unicamp_Guarana_Operation *repl) {
  OperationFactory *this = (OperationFactory*)this_;
  NativeOperation *op;
  
  checkMobj(this);
  checkCompat(&voidClass, repl);
  checkTarget(this->obj, repl);
  op = guarana_new_native_operation(this->obj, getCurrentThread(), repl);
  op->kind |= op_array_write | op_wrapped;
  op->op_id.index = elmt;
  op->op_arg.l = value;

  return (struct HBR_unicamp_Guarana_Operation*)op;
}
