/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode:nil -*-
   interploop.c -- the main interpreter loop.
   Created: Chris Toshok <toshok@hungry.com>, 13-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
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "runtimeint.h"
#include "op_stack.h"
#include "compat.h"
#include "arch.h"
#include "jvmdiint.h"

extern PRLogModuleInfo* interpLm;
extern PRLogModuleInfo* exceptionLm;

static inline PRUint8
get_next_u1(StackFrame *f)
{
  return f->method->code[ f->pc ++ ];
}

/*
 * (byte1 << 8) | byte2
 */
static inline PRUint16
get_next_u2(StackFrame *f)
{
  PRUint16 value;
  value = ((PRUint16)f->method->code[ f->pc ++ ]) << 8;
  value |= f->method->code[ f->pc ++ ];
  return value;
}

/*
 * (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4
 */
static inline PRUint32
get_next_u4(StackFrame *f)
{
  PRUint32 value;
  
  value  = f->method->code[ f->pc ++ ] << 24;
  value |= f->method->code[ f->pc ++ ] << 16; 
  value |= f->method->code[ f->pc ++ ] << 8; 
  value |= f->method->code[ f->pc ++ ]; 
  
  return value;
}

StackFrame *
push_frame(HungryEnv *henv, int num_vars)
{
  int new_frame_depth;
  int new_frame_size;
  StackFrame *new_stack_frame;

  if (henv->top_frame == henv->stack_highwater)
    new_frame_depth = 1;
  else
    new_frame_depth = henv->top_frame->depth + 1;

  new_frame_size = sizeof(StackFrame) + sizeof(JavaStackItem) * num_vars;
  new_stack_frame = (StackFrame*)((char*)henv->top_frame - new_frame_size);

  if (new_stack_frame < henv->stack_lowwater)
    {
      /* do something special... throw a runtime exception or something. */
    }
  else
    {
      new_stack_frame->depth = new_frame_depth;
      new_stack_frame->parent = henv->top_frame;
      new_stack_frame->_env = henv;

      new_stack_frame->flags = 0;

      henv->top_frame = new_stack_frame;
    }

  PR_LOG(interpLm, PR_LOG_DEBUG,
         ("Pushed new frame: %p\n"
          "\t  now at depth %d\n",
          new_stack_frame, henv->top_frame->depth));

  return new_stack_frame;
}

void
pop_frame(HungryEnv *henv)
{
  PR_LOG(interpLm, PR_LOG_DEBUG, 
         ("\tPopping %s frame: %p\n",
          (henv->top_frame->flags & FRAME_NATIVE) ? "native" : "java",
          henv->top_frame));

  maybe_exit_monitor_for_method(henv,
                                henv->top_frame->method,
                                henv->top_frame->this_pointer);

  if (henv->vm->_verbose_flags & VERBOSE_METHOD)
    {
      int i;
      for (i = 0; i < henv->top_frame->depth; i ++) printf (" ");
      printf ("< %s.%s\n", getClassName(henv, henv->top_frame->method->clazz),
              henv->top_frame->method->name);
    }

  henv->top_frame = henv->top_frame->parent;

  PR_LOG(interpLm, PR_LOG_DEBUG,
         ("\t  now at depth %d\n",
          (henv->top_frame == henv->stack_highwater) ? 0
          : henv->top_frame->depth));
}

static inline void
execute_opcode(StackFrame *f, PRUint8 oc)
{
#define OPCODE_BODY(name, code) \
 case code:

#define OPCODE_START(name,code) \
  OPCODE_BODY(name, code)
#define OPCODE(name, code) \
  break; \
OPCODE_BODY(name, code)

  switch (oc) {
#include "interpfunc.c"
  default:
    {
      printf ("Instruction %d not implemented yet :)\n", 
              f->method->code[ f->pc-1 ]);
        
      abort_with_message("Instruction not implemented\n");
    }
  }
}

StackFrame *
create_frame_for_method (HungryEnv *henv, MethodStruct *method)
{
  StackFrame *new_frame = push_frame(henv,
                                     method->max_locals 
                                     + (method->access_flags & ACC_STATIC ? 0 : 1));

  new_frame->method = method;

  new_frame->pc = 0;

  return new_frame;
}

void
interp_loop(StackFrame *frame)
{
  PRUint8 opcode;
  HungryEnv *henv = frame->_env;
  int save_depth = frame->depth;
  PR_LOG(interpLm, PR_LOG_DEBUG,
         ("in interp_loop(0x%x)\n", frame));

  do {
    StackFrame *topframe;
    jthrowable exception;

    topframe = henv->top_frame;
    opcode = topframe->method->code[ topframe->pc ++ ];

    execute_opcode(topframe, opcode);
  }
  while (henv->top_frame != henv->stack_highwater
         && henv->top_frame->depth >= save_depth);
}

void
fill_local_vars_from_stack (StackFrame *df, /* destination frame */
                            int num_vars_needed, /* number of
                                                    parameter words
                                                    from the signature */
                            int static_p) /* is this a static call ? */
{
  int j;

  if (!static_p)
    num_vars_needed ++;

  for (j = num_vars_needed - 1; j >=0; j --)
    op_stack_pop_any (df->_env->op_stack, df->vars[j].value.l);

  /* the object reference is implicit, unless static. */
  if (!static_p)
    df->this_pointer = (japhar_object*)df->vars[0].value.l;
  else
    df->this_pointer = NULL;
}

void
fill_local_vars (StackFrame *df, /* destination frame */
                 MethodStruct *method,
                 InterpValue *args, /* must match the number in the
                                       signature. */
                 japhar_object* obj) /* NULL for static calls */
{
  int i;

  if (obj)
    df->vars[0].value.l = obj;

  for (i = method->num_param_words - 1; i >= 0; i --)
    df->vars[i + (obj ? 1 : 0)].value.j = args[i].j;
  
  df->this_pointer = obj;
}

void 
push_item_from_constant_pool (StackFrame *f, 
                              int index)
{
  const ConstantPoolEntry * const constant =
    &f->method->clazz->constants[ index ];
  int resolved = constant->generic.tag & CONSTANT_RESOLVED;
  int tag;

  if (resolved) 
    tag = constant->generic.tag ^ CONSTANT_RESOLVED;
  else
    tag = constant->generic.tag;

  PR_LOG(interpLm, PR_LOG_DEBUG, ("pushing item %d from constant pool (", index));
  
  switch (tag)
    {
    case CONSTANT_Integer:
      PR_LOG(interpLm, PR_LOG_DEBUG, ("Integer)\n"));
      op_stack_push_int(f->_env->op_stack, constant->integer_info.bytes);
      break;
    case CONSTANT_Float:
      {
        float fvalue;
        PR_LOG(interpLm, PR_LOG_DEBUG, ("Float)\n"));

        fvalue = *(float*)((void*)&constant->float_info.bytes);
        op_stack_push_float(f->_env->op_stack, fvalue);
        break;
      }
    case CONSTANT_Long:
      {
        PR_LOG(interpLm, PR_LOG_DEBUG, ("Long)\n"));
        op_stack_push_doubleword(f->_env->op_stack,
                                 constant->long_info.high_bytes,
                                 constant->long_info.low_bytes);
        break;
      }
    case CONSTANT_Double:
      {
        PR_LOG(interpLm, PR_LOG_DEBUG, ("Double)\n"));

        op_stack_push_doubleword(f->_env->op_stack,
                                 constant->double_info.high_bytes,
                                 constant->double_info.low_bytes);
        break;
      }
    case CONSTANT_String:
      {
        PR_LOG(interpLm, PR_LOG_DEBUG, ("String)\n"));
        op_stack_push_object (f->_env->op_stack,
                              ResolveString(f->_env, f->method->clazz,
                                            &f->method->clazz->constants[ index ]));
        break;
      }
    case CONSTANT_Utf8:
    case CONSTANT_Unicode:
    case CONSTANT_Class:
    case CONSTANT_Fieldref:
    case CONSTANT_Methodref:
    case CONSTANT_InterfaceMethodref:
    case CONSTANT_NameAndType:
    default:
      PR_LOG (interpLm, PR_LOG_ERROR,
              ("pushing invalid Constant Pool type: %d\n",
               constant->generic.tag));
      HVM_ExceptionThrow(f->_env, java_lang_RuntimeException,
                         "pushing invalid constant pool type");
      return;
    }
}
