/* -*- Mode: C; c-file-style: "gnu" -*-
   operand_stack.h --
   Created: Chris Toshok <toshok@hungry.com>, 10-Aug-1997
 */
/*
  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) 1997, 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
*/
#ifndef _op_stack_h
#define _op_stack_h

#include <stdlib.h>

#include "jni.h"
#include "sig.h"
#include "interp.h"
#include "exceptions.h"
#include "log.h"
#include <assert.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifdef PROFILING
# ifdef WANT_FUNCTION_BODIES
#  define OPSTACK_PREFIX
# else
#  define OPSTACK_PREFIX extern
# endif
#else
# define WANT_FUNCTION_BODIES
# define OPSTACK_PREFIX static inline
#endif

#define OPSTACK_SIZE 16384 /* 16k operand stacks */

#define OPSTACK_LOG "OpStack"

#define op_stack_check_bottomspace(env, s, space_needed) \
do { \
  if ((s)->stack_top - space_needed < (s)->stack_bottom) \
    { \
      throw_Exception((env), "java/lang/VirtualMachineError", "Unexpected stack underflow."); \
      return; \
    } \
} while(0)

#define op_stack_check_topspace(env, s, space_needed) \
do { \
  if ((s)->stack_top + space_needed >= (s)->high_water) \
    { \
      throw_Exception((env), "java/lang/StackOverflowError", NULL); \
      return; \
    } \
} while(0)

  OPSTACK_PREFIX OpStack *
  op_stack_allocate(int size)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    OpStack *new_stack = (OpStack*)malloc(sizeof(OpStack));
    if ( NULL == new_stack )
      {
	return NULL;
      }

    new_stack->stack_top = (void**)malloc(sizeof(void*) * size);
    if ( NULL == new_stack->stack_top )
      {
	free(new_stack);
	return NULL;
      }
    new_stack->stack_bottom = new_stack->stack_top;
    new_stack->high_water = new_stack->stack_bottom + size;

    return new_stack;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_deallocate(OpStack *stack)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    free(stack->stack_top);
    free(stack);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_byte(JNIEnv *env, OpStack *s,
		     jbyte value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG2(OPSTACK_LOG, 1, "    Pushing byte %d onto operand stack %p\n", value, s);

    *(s->stack_top++) = (void*)(jint)value;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_char(JNIEnv *env, OpStack *s,
		     jchar value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG3(OPSTACK_LOG, 1, "    Pushing char %d (%c) onto operand stack %p\n", 
	      value, (char)(value & 0xff), s);

    *(s->stack_top++) = (void*)(jint)value;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_short(JNIEnv *env, OpStack *s,
		      jshort value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG2(OPSTACK_LOG, 1, "    Pushing short %d onto operand stack %p\n", value, s);

    *(s->stack_top++) = (void*)(jint)value;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_boolean(JNIEnv *env, OpStack *s,
			jboolean value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG2(OPSTACK_LOG, 1, "    Pushing boolean %s onto operand stack %p\n", 
	      value == 0 ? "False" : "True", s);

    *(s->stack_top++) = (void*)(jint)value;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_int(JNIEnv *env, OpStack *s,
		    jint value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG2(OPSTACK_LOG, 1, "    Pushing int %ld onto operand stack %p\n", value, s);

    *(s->stack_top++) = (void*)value;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_long(JNIEnv *env, OpStack *s,
		     jlong value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG2(OPSTACK_LOG, 1, "    Pushing long %ld onto operand stack %p\n", value, s);

    *(s->stack_top++) = (void*)(jint)((value >> 32) & 0xffffffff);
    *(s->stack_top++) = (void*)(jint)(value & 0xffffffff);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_long_w(JNIEnv *env, OpStack *s,
		       jint high_word,
		       jint low_word)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG3(OPSTACK_LOG, 1, "    Pushing long 0x%8x:0x%08x onto operand stack %p\n",
	      high_word, low_word, s);

    *(s->stack_top++) = (void*)high_word;
    *(s->stack_top++) = (void*)low_word;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_float(JNIEnv *env, OpStack *s,
		      jfloat value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jvalue newvalue;

    assert(s != NULL);

    JAVARLOG2(OPSTACK_LOG, 1, "    Pushing float %f onto operand stack %p\n", value, s);

    newvalue.f = value;

    *(s->stack_top++) = (void*)newvalue.i;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_double(JNIEnv *env, OpStack *s,
		       jdouble value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jint stack_value;
    jvalue val;

    assert(s != NULL);

    JAVARLOG2(OPSTACK_LOG, 1, "    Pushing double %lf onto operand stack %p\n", value, s);

    val.d = value;

    stack_value = (jint)((val.j >> 32) & 0xffffffff);
    *(s->stack_top++) = (void*)stack_value;

    stack_value = (jint)(val.j & 0xffffffff);
    *(s->stack_top++) = (void*)stack_value;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_double_w(JNIEnv *env, OpStack *s,
			 jint word1,
			 jint word2)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG3(OPSTACK_LOG, 1, "    Pushing double 0x%08x:0x%08x onto operand stack %p\n", word1, word2, s);

    *(s->stack_top++) = (void*)word1;
    *(s->stack_top++) = (void*)word2;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_object(JNIEnv *env, OpStack *s,
		       jobject o)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    JAVARLOG2(OPSTACK_LOG, 1, "    Pushing objectref %p onto operand stack %p\n", 
	      o, s);

    *(s->stack_top++) = (void*)o;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_byte(JNIEnv *env, OpStack *s,
		    jbyte *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jvalue stack_value;

    assert (s != NULL);

    stack_value.i = (jint)*(--s->stack_top);

    /*  assert(item.tag == SIG_JBYTE);*/

    *value = (jbyte)stack_value.i; /* was pushes as jint */

    JAVARLOG2(OPSTACK_LOG, 1, "    Popping byte off operand stack %p, value is %d\n", s, *value);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_char(JNIEnv *env, OpStack *s,
		    jchar *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jvalue stack_value;

    assert (s != NULL);

    stack_value.i = (jint)*(--s->stack_top);

    /*  assert(item.tag == SIG_JCHAR);*/

    *value = (jchar)stack_value.i; /* was pushed as jint */

    JAVARLOG3(OPSTACK_LOG, 1, "    Popping char off operand stack %p, value is %d (%c)\n",
	      s,
	      *value, (char)(*value & 0xff));
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_short(JNIEnv *env, OpStack *s,
		     jshort *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jvalue stack_value;

    assert (s != NULL);

    stack_value.i = (jint)*(--s->stack_top);

    /*  assert(item.tag == SIG_JSHORT);*/

    *value = (jshort)stack_value.i; /* was pushed as jint */

    JAVARLOG2(OPSTACK_LOG, 1, "    Popping short off operand stack %p, value is %d\n", s, *value);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_boolean(JNIEnv *env, OpStack *s,
		       jboolean *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jvalue stack_value;

    assert (s != NULL);

    stack_value.i = (jint)*(--s->stack_top);

    /*  assert(item.tag == SIG_JBOOLEAN);*/

    *value = (jint)stack_value.i; /* was pushed as jint */

    JAVARLOG2(OPSTACK_LOG, 1, "    Popping boolean off operand stack %p, value is %s\n", 
	      s, *value == 0 ? "False" : "True");
  }
#endif

  OPSTACK_PREFIX jint
  promote_to_jint(JNIEnv *env, SigPrimType t, jvalue *v)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    switch (t)
      {
      case SIG_JINT:
	return v->i;
      case SIG_JCHAR:
	return (jint)v->c;
      case SIG_JBYTE:
	return (jint)v->b;
      case SIG_JSHORT:
	return (jint)v->s;
      case SIG_JBOOLEAN:
	return (jint)v->b;
      default:
	(*env)->FatalError(env, "illegal type attempting to convert to integer.");
	return 0;
      }
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_int(JNIEnv *env, OpStack *s,
		   jint *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert (s != NULL);

    *value = (jint)*(--s->stack_top);

    JAVARLOG2(OPSTACK_LOG, 1, "    Popping int off operand stack %p, value is %ld\n", s, *value);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_long(JNIEnv *env, OpStack *s,
		    jlong *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jvalue val1, val2;

    assert (s != NULL);

    val2.i = (juint)*(--s->stack_top);
    val1.i = (juint)*(--s->stack_top);

    *value = (jlong)(juint) val1.i << 32;
    *value |= (jlong)(juint) val2.i;

    JAVARLOG2(OPSTACK_LOG, 1, "    Popping long off operand stack %p, value is %ld\n", s, *value);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_long_w(JNIEnv *env, OpStack *s,
		      jint *word1,
		      jint *word2)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert (s != NULL);

    *word2 = (jint)*(--s->stack_top);
    *word1 = (jint)*(--s->stack_top);

    JAVARLOG3(OPSTACK_LOG, 1, "    Popping long off operand stack %p, value is 0x%08x:0x%08x\n",
	      s, *word1, *word2);;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_float(JNIEnv *env, OpStack *s,
		     jfloat *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jvalue stack_value;

    assert (s != NULL);

    stack_value.i = (jint)*(--s->stack_top);

    *value = stack_value.f;

    JAVARLOG2(OPSTACK_LOG, 1, "    Popping float off operand stack %p, value is %f\n", s, *value);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_double(JNIEnv *env, OpStack *s,
		      jdouble *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    jvalue val;
    jvalue val1, val2;

    assert (s != NULL);

    val.j = 0;

    val2.i = (juint)*(--s->stack_top);
    val1.i = (juint)*(--s->stack_top);

    val.j = (jlong)(juint) val1.i << 32;
    val.j |= (jlong)(juint) val2.i;

    *value = val.d;

    JAVARLOG2(OPSTACK_LOG, 1, "    Popping double off operand stack %p, value is %lf\n", s, *value);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_double_w(JNIEnv *env, OpStack *s,
			jint *word1,
			jint *word2)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert (s != NULL);

    *word2 = (jint)*(--s->stack_top);
    *word1 = (jint)*(--s->stack_top);

    JAVARLOG3(OPSTACK_LOG, 1, "    Popping double off operand stack %p, value is 0x%08x:0x%08x\n",
	      s, *word1, *word2);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_object(JNIEnv *env, OpStack *s,
		      jobject *obj)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert (s != NULL);

    *obj = (jobject)*(--s->stack_top);

    JAVARLOG2(OPSTACK_LOG, 1, "  Popping objectref off operand stack %p, value is %p\n",
	      s, *obj);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_any(JNIEnv *env, OpStack *s,
		    void **any)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    *(s->stack_top++) = *any;
  }
#endif

  OPSTACK_PREFIX void
  op_stack_push_value(JNIEnv *env, OpStack *s,
		      SigPrimType tag,
		      jvalue *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert(s != NULL);

    if (tag == SIG_JDOUBLE)
      {
	op_stack_check_topspace(env, s, 2);
	op_stack_push_double(env, s, value->d);
      }
    else if (tag == SIG_JLONG)
      {
	op_stack_check_topspace(env, s, 2);
	op_stack_push_long(env, s, value->j);
      }
    else if (tag == SIG_JINT
	     || tag == SIG_JCHAR
	     || tag == SIG_JBYTE
	     || tag == SIG_JSHORT
	     || tag == SIG_JBOOLEAN)
      {
	op_stack_check_topspace(env, s, 1);
	op_stack_push_int(env, s, promote_to_jint(env, tag, value));
      }
    else
      {
	op_stack_check_topspace(env, s, 1);
	*(s->stack_top++) = (void*)value->i;
      }
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_any(JNIEnv *env, OpStack *s,
		   void **any)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert (s != NULL);

    *any = *(--s->stack_top);
  }
#endif

  OPSTACK_PREFIX void
  op_stack_pop_value(JNIEnv *env, OpStack *s,
		     SigPrimType tag,
		     jvalue *value)
#ifndef WANT_FUNCTION_BODIES
       ;
#else
  {
    assert (s != NULL);

    if (tag == SIG_JDOUBLE)
      {
	op_stack_pop_double(env, s, &value->d);
      }
    else if (tag == SIG_JLONG)
      {
	op_stack_pop_long(env, s, &value->j);
      }
    else if (tag == SIG_JBOOLEAN)
      {
	op_stack_pop_boolean(env, s, &value->z);
      }
    else if (tag == SIG_JBYTE)
      {
	op_stack_pop_byte(env, s, &value->b);
      }
    else if (tag == SIG_JCHAR)
      {
	op_stack_pop_char(env, s, &value->c);
      }
    else if (tag == SIG_JSHORT)
      {
	op_stack_pop_short(env, s, &value->s);
      }
    else
      {
	value->i = (jint)*(--s->stack_top);
      }
  }
#endif

#undef OPSTACK_PREFIX

#ifdef __cplusplus
};
#endif

#endif /* _op_stack_h */
