/* -*- Mode: C; c-file-style: "gnu" -*-
   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 "method.h"
#include "jvmdiint.h"
#include "arrays.h"
#include "array-class.h"
#include "objects.h"
#include "log.h"
#include "interp.h"
#include "resolve.h"
#include "exceptions.h"
#include "native-threads.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#include <math.h>
#ifdef HAVE_FLOAT_H
#  include <float.h>
#endif

#include "op_stack.h"

#define MYLOG "InterpLoop"

#ifdef PROFILING
#define OPCODE(name, c, n, b, a) \
static void op_##name(StackFrame* f)
#define OPCODE_START(name, c, n, b, a) OPCODE(name, c, n, b, a)
#include "interpfunc.c"
#undef OPCODE
#undef OPCODE_START
#endif

opcode opcode_table[] = {

#ifdef PROFILING
#define OPCODE(name, code, needed, before, after) \
  { #name, op_##name, needed, before, after },
#define NOTIMPL(name, code) \
  { #name, op_notimplemented, 0, 0, 0 }
#else
#define OPCODE(name, code, needed, before, after) \
  { #name, needed, before, after },
#define NOTIMPL(name, code) \
  { #name, 0, 0, 0 }
#endif

#define OPCODE_START(name, code, needed, before, after) \
        OPCODE(name, code, needed, before, after)
#include "interploop.h"
#undef OPCODE
#undef OPCODE_START

  /*
   * The _quick methods should probably not be implemented.  Doing so
   * might give us problems with some of JavaSoft's patents.  Chapter
   * 9 of the Java Virtual Machine Specification informs that the
   * _quick optimization technique is covered by U.S. Patent
   * 5,367,685.
   */
  NOTIMPL(ldc_quick, 203),
  NOTIMPL(ldc_w_quick, 204),
  NOTIMPL(ldc2_w_quick, 205),
  NOTIMPL(getfield_quick, 206),
  NOTIMPL(putfield_quick, 207),
  NOTIMPL(getfield2_quick, 208),
  NOTIMPL(putfield2_quick, 209),
  NOTIMPL(getstatic_quick, 210),
  NOTIMPL(putstatic_quick, 211),
  NOTIMPL(getstatic2_quick, 212),
  NOTIMPL(putstatic2_quick, 213),
  NOTIMPL(invokevirtual_quick, 214),
  NOTIMPL(invokenonvirtual_quick, 215),
  NOTIMPL(invokesuper_quick, 216),
  NOTIMPL(invokestatic_quick, 217),
  NOTIMPL(invokeinterface_quick, 218),
  NOTIMPL(invokevirtualobject_quick, 219),
  NOTIMPL(unknown, 220),
  NOTIMPL(new_quick, 221),
  NOTIMPL(anewarray_quick, 222),
  NOTIMPL(multianewarray_quick, 223),
  NOTIMPL(checkcast_quick, 224),
  NOTIMPL(instanceof_quick, 225),
  NOTIMPL(invokevirtual_quick_w, 226),
  NOTIMPL(getfield_quick_w, 227),
  NOTIMPL(putfield_quick_w, 228),
  NOTIMPL(unknown, 229),
  NOTIMPL(unknown, 230),
  NOTIMPL(unknown, 231),
  NOTIMPL(unknown, 232),
  NOTIMPL(unknown, 233),
  NOTIMPL(unknown, 234),
  NOTIMPL(unknown, 235),
  NOTIMPL(unknown, 236),
  NOTIMPL(unknown, 237),
  NOTIMPL(unknown, 238),
  NOTIMPL(unknown, 239),
  NOTIMPL(unknown, 240),
  NOTIMPL(unknown, 241),
  NOTIMPL(unknown, 242),
  NOTIMPL(unknown, 243),
  NOTIMPL(unknown, 244),
  NOTIMPL(unknown, 245),
  NOTIMPL(unknown, 246),
  NOTIMPL(unknown, 247),
  NOTIMPL(unknown, 248),
  NOTIMPL(unknown, 249),
  NOTIMPL(unknown, 250),
  NOTIMPL(unknown, 251),
  NOTIMPL(unknown, 252),
  NOTIMPL(unknown, 253),
  NOTIMPL(impdep1, 254), /* (reserved opcode) */
  NOTIMPL(impdep2, 255)  /* (reserved opcode) */
};

StackFrame *
push_frame(JThreadInfo *thr_info, int num_vars)
{
  int new_frame_depth;
  int new_frame_size;
  StackFrame *new_stack_frame;
  JNIEnv *env;

  if (TOPFRAME(thr_info) == thr_info->stack_highwater)
    {
      new_frame_depth = 1;
      env = THREAD_getEnv();
    }
  else
    {
      new_frame_depth = TOPFRAME(thr_info)->depth + 1;
      env = TOPFRAME(thr_info)->jni_env;
    }

  new_frame_size = sizeof(StackFrame) + sizeof(JavaStackItem) * num_vars;
  new_stack_frame = (StackFrame*)((char*)TOPFRAME(thr_info) - new_frame_size);

  if (new_stack_frame < thr_info->stack_lowwater)
    {
      /* do something special... throw a runtime exception or something. */
    }
  else
    {
      new_stack_frame->depth = new_frame_depth;
      new_stack_frame->frame_size = new_frame_size;
      new_stack_frame->thread_info = thr_info;

      new_stack_frame->flags = 0;

      ENV(new_stack_frame) = env;
	  
      TOPFRAME(thr_info) = new_stack_frame;
    }

  JAVARLOG1(MYLOG, 1, "Pushed new frame: %p\n", new_stack_frame);
  JAVARLOG1(MYLOG, 1, "\t  now at depth %d\n", STACKDEPTH(TOPFRAME(thr_info)));

  return new_stack_frame;
}

StackFrame *
get_frame_parent(StackFrame *frame)
{
  return (StackFrame*)((char*)frame + frame->frame_size);
}

void
maybe_enter_monitor_for_method(JNIEnv *env,
			       MethodStruct *method,
			       japhar_obj this_obj)
{
  if (method->access_flags & ACC_SYNCHRONIZED)
    {
      HMonitor mon;

      if (this_obj == NULL)
	this_obj = clazzfile_to_jclass(env, method->clazz);
      
      mon = MONITOR_getForObject(this_obj);
      MONITOR_enter(mon);

      JAVARLOG3(MYLOG, 4, "enter monitor for method %s.%s (this_obj = %p)\n",
		getClassName(env, method->clazz),
		method->name, this_obj);
    }
}

void
maybe_exit_monitor_for_method(JNIEnv *env,
			      MethodStruct *method,
			      japhar_obj this_obj)
{
  if (method->access_flags & ACC_SYNCHRONIZED)
    {
      HMonitor mon;

      if (this_obj == NULL)
	this_obj = clazzfile_to_jclass(env, method->clazz);
      
      mon = MONITOR_getForObject(this_obj);
      if ( ! MONITOR_exit(mon) )
	{
	  throw_Exception(env, "java/lang/IllegalMonitorStateException",
			  "not monitor owner!");
	  return;
	}

      JAVARLOG3(MYLOG, 4, "exit  monitor for method %s.%s (this_obj = %p)\n",
		getClassName(env, method->clazz),
		method->name, this_obj);
    }
}

void
pop_frame(JThreadInfo *thr_info)
{
  JAVARLOG2(MYLOG, 1, "\tPopping %s frame: %p\n",
	    (TOPFRAME(thr_info)->flags & FRAME_NATIVE) ? "native" : "java",
	    TOPFRAME(thr_info));

  maybe_exit_monitor_for_method(ENV(TOPFRAME(thr_info)),
				METHOD(TOPFRAME(thr_info)),
				THISPTR(TOPFRAME(thr_info)));

  if (((HungryJNIEnv*)ENV(TOPFRAME(thr_info)))->_vm->_verbose_flags 
      & VERBOSE_METHOD)
    {
      int i;
      for (i = 0; i < TOPFRAME(thr_info)->depth; i ++) printf (" ");
      printf ("< %s.%s\n", getClassName(env, METHOD(TOPFRAME(thr_info))->clazz),
	      METHOD(TOPFRAME(thr_info))->name);
    }

#ifdef DEBUG
  free(METHODNAME(TOPFRAME(thr_info)));
#endif

  TOPFRAME(thr_info) = get_frame_parent(TOPFRAME(thr_info));

  JAVARLOG1(MYLOG, 1, "\t  now at depth %d\n",
	    (TOPFRAME(thr_info) == thr_info->stack_highwater) ? 0
	    : STACKDEPTH(TOPFRAME(thr_info)) );
}

static void
execute_opcode(StackFrame *f, u1 oc)
{
#ifdef PROFILING
  opcode *op = &opcode_table[oc];

  (op->func)(f);
#else
#define OPCODE_BODY(name, code, needed, before, after) \
 case code:

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

  switch (oc) {
#include "interpfunc.c"
  default:
    {
      JNIEnv *env = ENV(f);
	
      printf ("Instruction %d not implemented yet :)\n", 
	      CODE(f)[ PC(f)-1 ]);
	
      (*env)->FatalError(env, "Instruction not implemented\n");
    }
  }
#endif
}

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

  METHOD(new_frame) = method;
#ifdef DEBUG
  METHODNAME(new_frame) = strdup(method->name);
  if (NULL == METHODNAME(new_frame) )
    {
      throw_Exception(thr_info->thread_env, "java/lang/OutOfMemoryError",
		      "unable to allocate method name");
      return NULL;
    }

  CLASSNAME(new_frame) = getClassName(ENV(new_frame), method->clazz);
#endif

  PC(new_frame) = 0;
  WIDE(new_frame) = JNI_FALSE;

  return new_frame;
}

void
interp_loop(StackFrame *frame)
{
  u1 opcode;
  HungryJNIEnv *henv = (HungryJNIEnv*)ENV(frame);
  JThreadInfo *thread_info = THREAD_INFO(frame);
  int save_depth = frame->depth;
  JAVARLOG1(MYLOG, 3, "in interp_loop(0x%x)\n", frame);

  do {
    StackFrame *topframe;
    jthrowable exception;

    topframe = TOPFRAME(thread_info);
    opcode = get_next_u1(topframe);

    execute_opcode(topframe, opcode);

    if ((exception = henv->_exception /* optimized from (*env)->ExceptionOccurred(env)*/))
      {
	throw_exception(ENV(frame), exception, topframe);
		  
	/* the only way the exception is still set is if we
	   need to throw it from java to native code.  Code
	   that calls interp_loop (or sub_mainloop for that 
	   matter) should handle this.  We just return and
	   let them deal with it. */
	if (henv->_exception /* optimized from (*env)->ExceptionOccurred(env) */)
	  {
	    JAVARLOG0("Exceptions", 1, "Exception thrown from java code to native code.");
	    return;
	  }
      }
  }
  while (TOPFRAME(thread_info) != thread_info->stack_highwater
	 && TOPFRAME(thread_info)->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_value (ENV(df), OPSTACK (df), SIG_JINT /* XXX */,
			  &VARS(df)[j].value);
    }

  /* the object reference is implicit, unless static. */
  if (!static_p)
    {
      THISPTR(df) = (jobject)VARS(df)[0].value.l;
    }
  else
    {
      THISPTR(df) = NULL;
    }
  return;
}

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

  if (obj)
    {
      VARS(df)[0].value.l = obj;
    }

  /* XXXX by changing away from using a Signature inside MethodStructs, we
     lose the ability to fill in the tags for variables. */
  for (i = method->num_param_words - 1; i >= 0; i --)
    VARS(df)[i + (obj ? 1 : 0)].value.j = args[i].j;
  
  THISPTR(df) = obj;
  
  return;
}

/* This is also used by java.lang.Thread */
#define STACK_SIZE 1000
jboolean
setup_stackframes(JThreadInfo *thread_info)
{
  StackFrame *frames = (StackFrame*)malloc(sizeof(StackFrame) * STACK_SIZE);

  if (NULL == frames)
    return JNI_FALSE;

  thread_info->stack_lowwater = frames;
  thread_info->stack_highwater = frames;
  thread_info->stack_highwater += STACK_SIZE;
  thread_info->top_frame = thread_info->stack_highwater;

  return JNI_TRUE;
}

static void
set_field(char *data,
	  FieldStruct *field,
	  jvalue value)
{
  switch(field->java_type)
    {
    case SIG_JBOOLEAN:
      *(jboolean*)(data + field->field_offset) = value.z;
      break;
    case SIG_JBYTE:
      *(jbyte*)(data + field->field_offset) = value.b;
      break;
    case SIG_JCHAR:
      *(jchar*)(data + field->field_offset) = value.c;
      break;
    case SIG_JSHORT:
      *(jshort*)(data + field->field_offset) = value.s;
      break;
    case SIG_JINT:
      *(jint*)(data + field->field_offset) = value.i;
      break;
    case SIG_JLONG:
      *(jlong*)(data + field->field_offset) = value.j;
      break;
    case SIG_JFLOAT:
      *(jfloat*)(data + field->field_offset) = value.f;
      break;
    case SIG_JDOUBLE:
      *(jdouble*)(data + field->field_offset) = value.d;
      break;
    case SIG_JOBJECT:
      *(jobject*)(data + field->field_offset) = value.l;
      break;
    default:
      assert(0);
      break;
    }
}

/**
 * Make sure the supplied constant pool entry is resolved.
 */
static void
ensure_resolved(ClazzFile *clazz, ConstantPoolEntry *entry)
{
  JNIEnv *env;
  int resolved = entry->generic.tag & CONSTANT_RESOLVED;
  if (resolved) {
    return;
  }
  env = THREAD_getEnv();
  switch(entry->generic.tag)
    {
    case CONSTANT_Integer:
      /* nothing to do to resolve an Integer */
      break;
    case CONSTANT_Float:
      entry->res_float_info.value = *(jfloat*)&entry->float_info.bytes;
      break;
    case CONSTANT_Long:
      /* XXX is this the correct way to resolve a jlong? */
      entry->res_long_info.value =
	(((jlong)(*(jint*)&entry->long_info.high_bytes)) << 32) |
	*(jint*)&entry->long_info.low_bytes;
      break;
    case CONSTANT_String:
      ResolveString(env, clazz, entry);
      break;
    case CONSTANT_Double:
    default:
      /* sorry, not yet implemented */
      fprintf(stderr, "constant tag '%d' is not yet supported\n",
	      entry->generic.tag);
      assert(0);
      break;
    }
  entry->generic.tag |= CONSTANT_RESOLVED;
}

/**
 * Returns value for constant fields.
 * XXX for the moment it only correctly handles int fields
 */
static void
get_constant_field(FieldStruct *field,
		   jvalue *value)
{
  ConstantPoolEntry *entry;

  /*
   * We must make sure the class has been initialized
   * (for example, we might be accessing a static final from
   * reflection and therefore the container class needs to be
   * initialized now).
   */
  initialize_class(THREAD_getEnv(), field->clazz);

  entry = get_constant(field->clazz, field->constant_value_index);
  ensure_resolved(field->clazz, entry);
  switch(entry->generic.tag & ~CONSTANT_RESOLVED)
    {
    case CONSTANT_Integer:
      value->i = entry->integer_info.bytes;
      break;
    case CONSTANT_Double:
      value->d = entry->res_double_info.value;
      break;
    case CONSTANT_Float:
      value->f = entry->res_float_info.value;
      break;
    case CONSTANT_Long:
      value->j = entry->res_long_info.value;
      break;
    case CONSTANT_String:
      value->l = entry->res_string_info.string;
      break;
    default:
      /* sorry, not yet implemented */
      fprintf(stderr, "constant tag %d is not yet supported\n",
	      entry->generic.tag & ~CONSTANT_RESOLVED);
      assert(0);
      break;
    }
}

static void
get_field(char *data,
	  FieldStruct *field,
	  jvalue *value)
{
  if (field->has_constant_value) {
    get_constant_field(field, value);
    return;
  }
  switch(field->java_type)
    {
    case SIG_JBOOLEAN:
      value->z = *(jboolean*)(data + field->field_offset);
      break;
    case SIG_JBYTE:
      value->b = *(jbyte*)(data + field->field_offset);
      break;
    case SIG_JCHAR:
      value->c = *(jchar*)(data + field->field_offset);
      break;
    case SIG_JSHORT:
      value->s = *(jshort*)(data + field->field_offset);
      break;
    case SIG_JINT:
      value->i = *(jint*)(data + field->field_offset);
      break;
    case SIG_JLONG:
      value->j = *(jlong*)(data + field->field_offset);
      break;
    case SIG_JFLOAT:
      value->f = *(jfloat*)(data + field->field_offset);
      break;
    case SIG_JDOUBLE:
      value->d = *(jdouble*)(data + field->field_offset);
      break;
    case SIG_JOBJECT:
      value->l = *(jobject*)(data + field->field_offset);
      break;
    default:
      assert(0);
      break;
    }
}


void
set_instance_field(japhar_obj obj,
		   FieldStruct *field,
		   jvalue value)
{
  char *instance_data;

  assert((field->access_flags & ACC_STATIC) == 0
	 && field->clazz == *obj);
  instance_data = ((char*)obj +
		   ((*obj)->nesting_level + 1) * sizeof(ClazzFile*));

  set_field(instance_data, field, value);
}

void
get_instance_field(japhar_obj obj,
		   FieldStruct *field,
		   jvalue *value)
{
  char *instance_data;
  /*
   * Make sure this number (+3) matches the one in
   * objects.c:object_size_without_fields()
   */
  instance_data = ((char*)obj +
		   ((*obj)->nesting_level + 1) * sizeof(ClazzFile*));

  get_field(instance_data, field, value);
}

void
set_static_field(ClazzFile *clazz,
		 FieldStruct *field,
		 jvalue value)
{
  char *static_data;

  /*
   * We must make sure the class has been initialized
   * (for example, we might be accessing a static final from
   * reflection and therefore the container class needs to be
   * initialized now).
   */
  initialize_class(THREAD_getEnv(), clazz);

  assert(field->access_flags & ACC_STATIC
	 && field->clazz == clazz);

  static_data = clazz->static_fields;

  set_field(static_data, field, value);
}

void
get_static_field(ClazzFile *clazz,
		 FieldStruct *field,
		 jvalue *value)
{
  char *static_data;

#if 0
  /*
   * We must make sure the class has been initialized
   * (for example, we might be accessing a static final from
   * reflection and therefore the container class needs to be
   * initialized now).
   */
  initialize_class(THREAD_getEnv(), clazz);
#endif

  static_data = clazz->static_fields;

  get_field(static_data, field, value);
}

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

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

  JAVARLOG0(MYLOG, 1, "in push_item_from_constant_pool()\n");
  JAVARLOG1(MYLOG, 2, "    pushing item %d from constant pool (", index);
  
  switch (tag)
    {
    case CONSTANT_Utf8:
      JAVARLOG0(MYLOG, 2, "Utf8 FINISH ME)\n");
      abort();
      break;
    case CONSTANT_Unicode:
      JAVARLOG0(MYLOG, 2, "Unicode FINISH ME)\n");
      abort();
      break;
    case CONSTANT_Integer:
      JAVARLOG0(MYLOG, 2, "Integer)\n");
      op_stack_push_int(ENV(f), OPSTACK(f), constant->integer_info.bytes);
      break;
    case CONSTANT_Float:
      {
	float fvalue;
	JAVARLOG0(MYLOG, 2, "Float)\n");

	fvalue = *(float*)((void*)&constant->float_info.bytes);
	op_stack_push_float(ENV(f), OPSTACK(f), fvalue);
	break;
      }
    case CONSTANT_Long:
      JAVARLOG0(MYLOG, 2, "Long)\n");
      op_stack_push_long_w(ENV(f), OPSTACK(f),
			   constant->long_info.high_bytes,
			   constant->long_info.low_bytes);
      break;
    case CONSTANT_Double:
      {
	JAVARLOG0(MYLOG, 2, "Double)\n");

	op_stack_push_double_w(ENV(f), OPSTACK(f),
			       constant->double_info.high_bytes,
			       constant->double_info.low_bytes);
	break;
      }
    case CONSTANT_String:
      JAVARLOG0(MYLOG, 2, "String)\n");
      op_stack_push_object (ENV(f), OPSTACK(f),
			    ResolveString(ENV(f), METHOD(f)->clazz,
					  get_constant(METHOD(f)->clazz,index)));
      break;
    case CONSTANT_Class:
    case CONSTANT_Fieldref:
    case CONSTANT_Methodref:
    case CONSTANT_InterfaceMethodref:
    case CONSTANT_NameAndType:
    default:
      printf ("pushing invalid Constant Pool type: %d\n", constant->generic.tag);
      throw_Exception(ENV(f), "java/lang/RuntimeException", NULL);
      return;
    }
}

void
do_method_call (StackFrame *this_frame,
		MethodStruct *method)
{
  ClazzFile *cf = method->clazz;

  initialize_class(ENV(this_frame), cf);

  JAVARLOG2("DO_METHOD_CALL", 1, "    In do_method_call, class_name=%s,\n"
	    "     method_name=%s,\n",
	    getClassName(ENV(this_frame), cf), method->name);

  if (((HungryJNIEnv*)ENV(this_frame))->_vm->_verbose_flags 
      & VERBOSE_METHOD)
    {
      int i;
      for (i = 0; i < this_frame->depth; i ++) printf (" ");
      printf ("> %s.%s\n", getClassName(env, method->clazz), method->name);
    }

  if (method->access_flags & ACC_NATIVE)
    {
      JAVARLOG0(MYLOG, 1, "   access is ACC_NATIVE\n");
      JAVARLOG2(MYLOG, 1, "\tNative method name is %s_%s\n",
		getClassName(ENV(this_frame), cf),
		method->name);

      do_native_method_call(ENV(this_frame), method);
    }
  else
    {
      StackFrame *f = create_frame_for_method (THREAD_INFO(this_frame), method);

      JAVARLOG2(MYLOG, 1, "    Virtual Method is %s.%s\n", 
		getClassName(ENV(this_frame), cf),
		method->name);

      fill_local_vars_from_stack (f, method->num_param_words,
				  method->access_flags & ACC_STATIC);

      OPSTACK_TOP(f) = OPSTACK(f)->stack_top;

      maybe_enter_monitor_for_method(ENV(f), method, THISPTR(f));
    }
}

void
maybe_push_return_value(StackFrame *f)
{
  StackFrame *parent = get_frame_parent(f);

  if (parent < THREAD_INFO(f)->stack_highwater)
    {
      /* restore the old stack top -- this must use OPSTACK_TOP(f)!! */
      OPSTACK(f)->stack_top = OPSTACK_TOP(f);

      /* and, if the parent frame is java (and there's actually a
         return value), push the return value. */
      if (! (parent->flags & FRAME_NATIVE)
	  && THREAD_INFO(parent)->return_value.tag != SIG_JVOID )
	op_stack_push_value(ENV(f),
			    OPSTACK(f),
			    THREAD_INFO(f)->return_value.tag,
			    &THREAD_INFO(f)->return_value.value);
    }
}

