OSDN Git Service

Fix problem that caused compiled java code to trigger an internal gdb error.
[pf3gnuchains/gcc-fork.git] / libjava / interpret.cc
index 40c7cba..a2bcbb8 100644 (file)
@@ -11,11 +11,7 @@ details.  */
 /* Author: Kresten Krab Thorup <krab@gnu.org>  */
 
 #include <config.h>
-
-// Define this to get the direct-threaded interpreter.  If undefined,
-// we revert to a basic bytecode interpreter.  The former is faster
-// but uses more memory.
-#define DIRECT_THREADED
+#include <platform.h>
 
 #pragma implementation "java-interp.h"
 
@@ -38,9 +34,15 @@ details.  */
 #include <java/lang/Thread.h>
 #include <java-insns.h>
 #include <java-signal.h>
+#include <java/lang/ClassFormatError.h>
+#include <execution.h>
+#include <java/lang/reflect/Modifier.h>
 
 #ifdef INTERPRETER
 
+// Execution engine for interpreted code.
+_Jv_InterpreterEngine _Jv_soleInterpreterEngine;
+
 #include <stdlib.h>
 
 using namespace gcj;
@@ -54,6 +56,11 @@ static void throw_null_pointer_exception ()
   __attribute__ ((__noreturn__));
 #endif
 
+static void throw_class_format_error (jstring msg)
+       __attribute__ ((__noreturn__));
+static void throw_class_format_error (char *msg)
+       __attribute__ ((__noreturn__));
+
 #ifdef DIRECT_THREADED
 // Lock to ensure that methods are not compiled concurrently.
 // We could use a finer-grained lock here, however it is not safe to use
@@ -71,26 +78,6 @@ void _Jv_InitInterpreter() {}
 
 extern "C" double __ieee754_fmod (double,double);
 
-// This represents a single slot in the "compiled" form of the
-// bytecode.
-union insn_slot
-{
-  // Address of code.
-  void *insn;
-  // An integer value used by an instruction.
-  jint int_val;
-  // A pointer value used by an instruction.
-  void *datum;
-};
-
-// The type of the PC depends on whether we're doing direct threading
-// or a more ordinary bytecode interpreter.
-#ifdef DIRECT_THREADED
-typedef insn_slot *pc_t;
-#else
-typedef unsigned char *pc_t;
-#endif
-
 static inline void dupx (_Jv_word *sp, int n, int x)
 {
   // first "slide" n+x elements n to the right
@@ -105,7 +92,6 @@ static inline void dupx (_Jv_word *sp, int n, int x)
     {
       sp[top-(n+x)-i] = sp[top-i];
     }
-  
 }
 
 // Used to convert from floating types to integral types.
@@ -236,15 +222,16 @@ static jint get4(unsigned char* loc) {
        | (((jint)(loc[3])) << 0);
 }
 
+#define SAVE_PC() frame_desc.pc = pc
 
 #ifdef HANDLE_SEGV
-#define NULLCHECK(X) 
-#define NULLARRAYCHECK(X)
+#define NULLCHECK(X) SAVE_PC()
+#define NULLARRAYCHECK(X) SAVE_PC()
 #else
 #define NULLCHECK(X) \
-  do { if ((X)==NULL) throw_null_pointer_exception (); } while (0)
+  do { SAVE_PC(); if ((X)==NULL) throw_null_pointer_exception (); } while (0)
 #define NULLARRAYCHECK(X) \
-  do { if ((X)==NULL) { throw_null_pointer_exception (); } } while (0)
+  do { SAVE_PC(); if ((X)==NULL) { throw_null_pointer_exception (); } } while (0)
 #endif
 
 #define ARRAYBOUNDSCHECK(array, index)                                       \
@@ -262,7 +249,7 @@ _Jv_InterpMethod::run_normal (ffi_cif *,
                              void* __this)
 {
   _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this;
-  _this->run (ret, args);
+  run (ret, args, _this);
 }
 
 void
@@ -276,7 +263,7 @@ _Jv_InterpMethod::run_synch_object (ffi_cif *,
   jobject rcv = (jobject) args[0].ptr;
   JvSynchronize mutex (rcv);
 
-  _this->run (ret, args);
+  run (ret, args, _this);
 }
 
 void
@@ -287,7 +274,7 @@ _Jv_InterpMethod::run_class (ffi_cif *,
 {
   _Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this;
   _Jv_InitClass (_this->defining_class);
-  _this->run (ret, args);
+  run (ret, args, _this);
 }
 
 void
@@ -302,7 +289,7 @@ _Jv_InterpMethod::run_synch_class (ffi_cif *,
   _Jv_InitClass (sync);
   JvSynchronize mutex (sync);
 
-  _this->run (ret, args);
+  run (ret, args, _this);
 }
 
 #ifdef DIRECT_THREADED
@@ -765,24 +752,26 @@ _Jv_InterpMethod::compile (const void * const *insn_targets)
       exc[i].start_pc.p = &insns[pc_mapping[exc[i].start_pc.i]];
       exc[i].end_pc.p = &insns[pc_mapping[exc[i].end_pc.i]];
       exc[i].handler_pc.p = &insns[pc_mapping[exc[i].handler_pc.i]];
-      jclass handler = (_Jv_ResolvePoolEntry (defining_class,
-                                             exc[i].handler_type.i)).clazz;
+      jclass handler
+       = (_Jv_Linker::resolve_pool_entry (defining_class,
+                                            exc[i].handler_type.i)).clazz;
       exc[i].handler_type.p = handler;
     }
 
+  // Translate entries in the LineNumberTable from bytecode PC's to direct
+  // threaded interpreter instruction values.
+  for (int i = 0; i < line_table_len; i++)
+    {
+      int byte_pc = line_table[i].bytecode_pc;
+      line_table[i].pc = &insns[pc_mapping[byte_pc]];
+    }  
+
   prepared = insns;
 }
 #endif /* DIRECT_THREADED */
 
-// This function exists so that the stack-tracing code can find the
-// boundaries of the interpreter.
-void
-_Jv_StartOfInterpreter (void)
-{
-}
-
 void
-_Jv_InterpMethod::run (void *retp, ffi_raw *args)
+_Jv_InterpMethod::run (void *retp, ffi_raw *args, _Jv_InterpMethod *meth)
 {
   using namespace java::lang::reflect;
 
@@ -793,20 +782,20 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
   // destructor so it cleans up automatically when the interpreter
   // returns.
   java::lang::Thread *thread = java::lang::Thread::currentThread();
-  _Jv_MethodChain frame_desc (this,
-                             (_Jv_MethodChain **) &thread->interp_frame);
+  _Jv_InterpFrame frame_desc (meth,
+                             (_Jv_InterpFrame **) &thread->interp_frame);
 
-  _Jv_word stack[max_stack];
+  _Jv_word stack[meth->max_stack];
   _Jv_word *sp = stack;
 
-  _Jv_word locals[max_locals];
+  _Jv_word locals[meth->max_locals];
 
   /* Go straight at it!  the ffi raw format matches the internal
      stack representation exactly.  At least, that's the idea.
   */
-  memcpy ((void*) locals, (void*) args, args_raw_size);
+  memcpy ((void*) locals, (void*) args, meth->args_raw_size);
 
-  _Jv_word *pool_data = defining_class->constants.data;
+  _Jv_word *pool_data = meth->defining_class->constants.data;
 
   /* These three are temporaries for common code used by several
      instructions.  */
@@ -1048,14 +1037,14 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
 #define AMPAMP(label) &&label
 
   // Compile if we must. NOTE: Double-check locking.
-  if (prepared == NULL)
+  if (meth->prepared == NULL)
     {
       _Jv_MutexLock (&compile_mutex);
-      if (prepared == NULL)
-       compile (insn_target);
+      if (meth->prepared == NULL)
+       meth->compile (insn_target);
       _Jv_MutexUnlock (&compile_mutex);
     }
-  pc = (insn_slot *) prepared;
+  pc = (insn_slot *) meth->prepared;
 
 #else
 
@@ -1106,20 +1095,25 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
       {
        int index = GET2U ();
 
-       /* _Jv_ResolvePoolEntry returns immediately if the value already
-        * is resolved.  If we want to clutter up the code here to gain
-        * a little performance, then we can check the corresponding bit
-        * JV_CONSTANT_ResolvedFlag in the tag directly.  For now, I
-        * don't think it is worth it.  */
+       /* _Jv_Linker::resolve_pool_entry returns immediately if the
+        * value already is resolved.  If we want to clutter up the
+        * code here to gain a little performance, then we can check
+        * the corresponding bit JV_CONSTANT_ResolvedFlag in the tag
+        * directly.  For now, I don't think it is worth it.  */
 
-       rmeth = (_Jv_ResolvePoolEntry (defining_class, index)).rmethod;
+       SAVE_PC();
+       rmeth = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                                  index)).rmethod;
 
        sp -= rmeth->stack_item_count;
        // We don't use NULLCHECK here because we can't rely on that
        // working if the method is final.  So instead we do an
        // explicit test.
        if (! sp[0].o)
-         throw new java::lang::NullPointerException;
+         {
+           //printf("invokevirtual pc = %p/%i\n", pc, meth->get_pc_val(pc));
+           throw new java::lang::NullPointerException;
+         }
 
        if (rmeth->vtable_index == -1)
          {
@@ -1152,7 +1146,10 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
        // working if the method is final.  So instead we do an
        // explicit test.
        if (! sp[0].o)
-         throw new java::lang::NullPointerException;
+         {
+           SAVE_PC();
+           throw new java::lang::NullPointerException;
+         }
 
        if (rmeth->vtable_index == -1)
          {
@@ -1172,6 +1169,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
 
     perform_invoke:
       {
+        SAVE_PC();
+       
        /* here goes the magic again... */
        ffi_cif *cif = &rmeth->cif;
        ffi_raw *raw = (ffi_raw*) sp;
@@ -1207,15 +1206,15 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
            switch (rtype)
              {
              case FFI_TYPE_SINT8:
-               PUSHI (rvalue.byte_value);
+               PUSHI ((jbyte)(rvalue.int_value & 0xff));
                break;
 
              case FFI_TYPE_SINT16:
-               PUSHI (rvalue.short_value);
+               PUSHI ((jshort)(rvalue.int_value & 0xffff));
                break;
 
              case FFI_TYPE_UINT16:
-               PUSHI (rvalue.char_value);
+               PUSHI (rvalue.int_value & 0xffff);
                break;
 
              case FFI_TYPE_FLOAT:
@@ -2066,11 +2065,11 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
       NEXT_INSN;
 
     insn_dcmpl:
-      tmpval = 1;
+      tmpval = -1;
       goto dcmp;
 
     insn_dcmpg:
-      tmpval = -1;
+      tmpval = 1;
 
     dcmp:
       {
@@ -2402,7 +2401,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
     insn_getstatic:
       {
        jint fieldref_index = GET2U ();
-       _Jv_ResolvePoolEntry (defining_class, fieldref_index);
+        SAVE_PC(); // Constant pool resolution could throw.
+       _Jv_Linker::resolve_pool_entry (meth->defining_class, fieldref_index);
        _Jv_Field *field = pool_data[fieldref_index].field;
 
        if ((field->flags & Modifier::STATIC) == 0)
@@ -2489,7 +2489,7 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
     insn_getfield:
       {
        jint fieldref_index = GET2U ();
-       _Jv_ResolvePoolEntry (defining_class, fieldref_index);
+       _Jv_Linker::resolve_pool_entry (meth->defining_class, fieldref_index);
        _Jv_Field *field = pool_data[fieldref_index].field;
 
        if ((field->flags & Modifier::STATIC) != 0)
@@ -2605,7 +2605,7 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
     insn_putstatic:
       {
        jint fieldref_index = GET2U ();
-       _Jv_ResolvePoolEntry (defining_class, fieldref_index);
+       _Jv_Linker::resolve_pool_entry (meth->defining_class, fieldref_index);
        _Jv_Field *field = pool_data[fieldref_index].field;
 
        jclass type = field->type;
@@ -2692,7 +2692,7 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
     insn_putfield:
       {
        jint fieldref_index = GET2U ();
-       _Jv_ResolvePoolEntry (defining_class, fieldref_index);
+       _Jv_Linker::resolve_pool_entry (meth->defining_class, fieldref_index);
        _Jv_Field *field = pool_data[fieldref_index].field;
 
        jclass type = field->type;
@@ -2818,14 +2818,18 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
       {
        int index = GET2U ();
 
-       rmeth = (_Jv_ResolvePoolEntry (defining_class, index)).rmethod;
+       rmeth = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                                  index)).rmethod;
 
        sp -= rmeth->stack_item_count;
 
        // We don't use NULLCHECK here because we can't rely on that
        // working for <init>.  So instead we do an explicit test.
        if (! sp[0].o)
-         throw new java::lang::NullPointerException;
+         {
+           SAVE_PC();
+           throw new java::lang::NullPointerException;
+         }
 
        fun = (void (*)()) rmeth->method->ncode;
 
@@ -2846,7 +2850,10 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
        // We don't use NULLCHECK here because we can't rely on that
        // working for <init>.  So instead we do an explicit test.
        if (! sp[0].o)
-         throw new java::lang::NullPointerException;
+         {
+           SAVE_PC();
+           throw new java::lang::NullPointerException;
+         }
        fun = (void (*)()) rmeth->method->ncode;
       }
       goto perform_invoke;
@@ -2856,7 +2863,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
       {
        int index = GET2U ();
 
-       rmeth = (_Jv_ResolvePoolEntry (defining_class, index)).rmethod;
+       rmeth = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                                  index)).rmethod;
 
        sp -= rmeth->stack_item_count;
 
@@ -2885,7 +2893,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
       {
        int index = GET2U ();
 
-       rmeth = (_Jv_ResolvePoolEntry (defining_class, index)).rmethod;
+       rmeth = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                                  index)).rmethod;
 
        sp -= rmeth->stack_item_count;
 
@@ -2928,7 +2937,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
     insn_new:
       {
        int index = GET2U ();
-       jclass klass = (_Jv_ResolvePoolEntry (defining_class, index)).clazz;
+       jclass klass = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                                         index)).clazz;
        jobject res = _Jv_AllocObject (klass);
        PUSHA (res);
 
@@ -2961,7 +2971,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
     insn_anewarray:
       {
        int index = GET2U ();
-       jclass klass = (_Jv_ResolvePoolEntry (defining_class, index)).clazz;
+       jclass klass = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                                         index)).clazz;
        int size  = POPI();
        jobject result = _Jv_NewObjectArray (size, klass, 0);
        PUSHA (result);
@@ -3001,9 +3012,11 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
 
     insn_checkcast:
       {
+        SAVE_PC();
        jobject value = POPA();
        jint index = GET2U ();
-       jclass to = (_Jv_ResolvePoolEntry (defining_class, index)).clazz;
+       jclass to = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                                      index)).clazz;
 
        if (value != NULL && ! to->isInstance (value))
          throw new java::lang::ClassCastException (to->getName());
@@ -3020,6 +3033,7 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
 #ifdef DIRECT_THREADED
     checkcast_resolved:
       {
+        SAVE_PC();
        jobject value = POPA ();
        jclass to = (jclass) AVAL ();
        if (value != NULL && ! to->isInstance (value))
@@ -3031,9 +3045,11 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
 
     insn_instanceof:
       {
+        SAVE_PC();
        jobject value = POPA();
        jint index = GET2U ();
-       jclass to = (_Jv_ResolvePoolEntry (defining_class, index)).clazz;
+       jclass to = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                                      index)).clazz;
        PUSHI (to->isInstance (value));
 
 #ifdef DIRECT_THREADED
@@ -3095,7 +3111,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
        int dim        = GET1U ();
 
        jclass type    
-         = (_Jv_ResolvePoolEntry (defining_class, kind_index)).clazz;
+         = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+                                              kind_index)).clazz;
        jint *sizes    = (jint*) __builtin_alloca (sizeof (jint)*dim);
 
        for (int i = dim - 1; i >= 0; i--)
@@ -3183,10 +3200,10 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
 #else
       int logical_pc = pc - 1 - bytecode ();
 #endif
-      _Jv_InterpException *exc = exceptions ();
+      _Jv_InterpException *exc = meth->exceptions ();
       jclass exc_class = ex->getClass ();
 
-      for (int i = 0; i < exc_count; i++)
+      for (int i = 0; i < meth->exc_count; i++)
        {
          if (PCVAL (exc[i].start_pc) <= logical_pc
              && logical_pc < PCVAL (exc[i].end_pc))
@@ -3196,8 +3213,8 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
 #else
              jclass handler = NULL;
              if (exc[i].handler_type.i != 0)
-               handler = (_Jv_ResolvePoolEntry (defining_class,
-                                                exc[i].handler_type.i)).clazz;
+               handler = (_Jv_Linker::resolve_pool_entry (defining_class,
+                                                            exc[i].handler_type.i)).clazz;
 #endif /* DIRECT_THREADED */
 
              if (handler == NULL || handler->isAssignableFrom (exc_class))
@@ -3219,13 +3236,6 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args)
     }
 }
 
-// This function exists so that the stack-tracing code can find the
-// boundaries of the interpreter.
-void
-_Jv_EndOfInterpreter (void)
-{
-}
-
 static void
 throw_internal_error (char *msg)
 {
@@ -3250,4 +3260,562 @@ throw_null_pointer_exception ()
 }
 #endif
 
+/* Look up source code line number for given bytecode (or direct threaded
+   interpreter) PC. */
+int
+_Jv_InterpMethod::get_source_line(pc_t mpc)
+{
+  int line = line_table_len > 0 ? line_table[0].line : -1;
+  for (int i = 1; i < line_table_len; i++)
+    if (line_table[i].pc > mpc)
+      break;
+    else
+      line = line_table[i].line;
+
+  return line;
+}
+
+/** Do static initialization for fields with a constant initializer */
+void
+_Jv_InitField (jobject obj, jclass klass, int index)
+{
+  using namespace java::lang::reflect;
+
+  if (obj != 0 && klass == 0)
+    klass = obj->getClass ();
+
+  if (!_Jv_IsInterpretedClass (klass))
+    return;
+
+  _Jv_InterpClass *iclass = (_Jv_InterpClass*)klass->aux_info;
+
+  _Jv_Field * field = (&klass->fields[0]) + index;
+
+  if (index > klass->field_count)
+    throw_internal_error ("field out of range");
+
+  int init = iclass->field_initializers[index];
+  if (init == 0)
+    return;
+
+  _Jv_Constants *pool = &klass->constants;
+  int tag = pool->tags[init];
+
+  if (! field->isResolved ())
+    throw_internal_error ("initializing unresolved field");
+
+  if (obj==0 && ((field->flags & Modifier::STATIC) == 0))
+    throw_internal_error ("initializing non-static field with no object");
+
+  void *addr = 0;
+
+  if ((field->flags & Modifier::STATIC) != 0)
+    addr = (void*) field->u.addr;
+  else
+    addr = (void*) (((char*)obj) + field->u.boffset);
+
+  switch (tag)
+    {
+    case JV_CONSTANT_String:
+      {
+       jstring str;
+       str = _Jv_NewStringUtf8Const (pool->data[init].utf8);
+       pool->data[init].string = str;
+       pool->tags[init] = JV_CONSTANT_ResolvedString;
+      }
+      /* fall through */
+
+    case JV_CONSTANT_ResolvedString:
+      if (! (field->type == &java::lang::String::class$
+            || field->type == &java::lang::Class::class$))
+       throw_class_format_error ("string initialiser to non-string field");
+
+      *(jstring*)addr = pool->data[init].string;
+      break;
+
+    case JV_CONSTANT_Integer:
+      {
+       int value = pool->data[init].i;
+
+       if (field->type == JvPrimClass (boolean))
+         *(jboolean*)addr = (jboolean)value;
+       
+       else if (field->type == JvPrimClass (byte))
+         *(jbyte*)addr = (jbyte)value;
+       
+       else if (field->type == JvPrimClass (char))
+         *(jchar*)addr = (jchar)value;
+
+       else if (field->type == JvPrimClass (short))
+         *(jshort*)addr = (jshort)value;
+       
+       else if (field->type == JvPrimClass (int))
+         *(jint*)addr = (jint)value;
+
+       else
+         throw_class_format_error ("erroneous field initializer");
+      }  
+      break;
+
+    case JV_CONSTANT_Long:
+      if (field->type != JvPrimClass (long))
+       throw_class_format_error ("erroneous field initializer");
+
+      *(jlong*)addr = _Jv_loadLong (&pool->data[init]);
+      break;
+
+    case JV_CONSTANT_Float:
+      if (field->type != JvPrimClass (float))
+       throw_class_format_error ("erroneous field initializer");
+
+      *(jfloat*)addr = pool->data[init].f;
+      break;
+
+    case JV_CONSTANT_Double:
+      if (field->type != JvPrimClass (double))
+       throw_class_format_error ("erroneous field initializer");
+
+      *(jdouble*)addr = _Jv_loadDouble (&pool->data[init]);
+      break;
+
+    default:
+      throw_class_format_error ("erroneous field initializer");
+    }
+}
+
+inline static unsigned char*
+skip_one_type (unsigned char* ptr)
+{
+  int ch = *ptr++;
+
+  while (ch == '[')
+    { 
+      ch = *ptr++;
+    }
+  
+  if (ch == 'L')
+    {
+      do { ch = *ptr++; } while (ch != ';');
+    }
+
+  return ptr;
+}
+
+static ffi_type*
+get_ffi_type_from_signature (unsigned char* ptr)
+{
+  switch (*ptr) 
+    {
+    case 'L':
+    case '[':
+      return &ffi_type_pointer;
+      break;
+
+    case 'Z':
+      // On some platforms a bool is a byte, on others an int.
+      if (sizeof (jboolean) == sizeof (jbyte))
+       return &ffi_type_sint8;
+      else
+       {
+         JvAssert (sizeof (jbyte) == sizeof (jint));
+         return &ffi_type_sint32;
+       }
+      break;
+
+    case 'B':
+      return &ffi_type_sint8;
+      break;
+      
+    case 'C':
+      return &ffi_type_uint16;
+      break;
+         
+    case 'S': 
+      return &ffi_type_sint16;
+      break;
+         
+    case 'I':
+      return &ffi_type_sint32;
+      break;
+         
+    case 'J':
+      return &ffi_type_sint64;
+      break;
+         
+    case 'F':
+      return &ffi_type_float;
+      break;
+         
+    case 'D':
+      return &ffi_type_double;
+      break;
+
+    case 'V':
+      return &ffi_type_void;
+      break;
+    }
+
+  throw_internal_error ("unknown type in signature");
+}
+
+/* this function yields the number of actual arguments, that is, if the
+ * function is non-static, then one is added to the number of elements
+ * found in the signature */
+
+int 
+_Jv_count_arguments (_Jv_Utf8Const *signature,
+                    jboolean staticp)
+{
+  unsigned char *ptr = (unsigned char*) signature->chars();
+  int arg_count = staticp ? 0 : 1;
+
+  /* first, count number of arguments */
+
+  // skip '('
+  ptr++;
+
+  // count args
+  while (*ptr != ')')
+    {
+      ptr = skip_one_type (ptr);
+      arg_count += 1;
+    }
+
+  return arg_count;
+}
+
+/* This beast will build a cif, given the signature.  Memory for
+ * the cif itself and for the argument types must be allocated by the
+ * caller.
+ */
+
+static int 
+init_cif (_Jv_Utf8Const* signature,
+         int arg_count,
+         jboolean staticp,
+         ffi_cif *cif,
+         ffi_type **arg_types,
+         ffi_type **rtype_p)
+{
+  unsigned char *ptr = (unsigned char*) signature->chars();
+
+  int arg_index = 0;           // arg number
+  int item_count = 0;          // stack-item count
+
+  // setup receiver
+  if (!staticp)
+    {
+      arg_types[arg_index++] = &ffi_type_pointer;
+      item_count += 1;
+    }
+
+  // skip '('
+  ptr++;
+
+  // assign arg types
+  while (*ptr != ')')
+    {
+      arg_types[arg_index++] = get_ffi_type_from_signature (ptr);
+
+      if (*ptr == 'J' || *ptr == 'D')
+       item_count += 2;
+      else
+       item_count += 1;
+
+      ptr = skip_one_type (ptr);
+    }
+
+  // skip ')'
+  ptr++;
+  ffi_type *rtype = get_ffi_type_from_signature (ptr);
+
+  ptr = skip_one_type (ptr);
+  if (ptr != (unsigned char*)signature->chars() + signature->len())
+    throw_internal_error ("did not find end of signature");
+
+  if (ffi_prep_cif (cif, FFI_DEFAULT_ABI,
+                   arg_count, rtype, arg_types) != FFI_OK)
+    throw_internal_error ("ffi_prep_cif failed");
+
+  if (rtype_p != NULL)
+    *rtype_p = rtype;
+
+  return item_count;
+}
+
+#if FFI_NATIVE_RAW_API
+#   define FFI_PREP_RAW_CLOSURE ffi_prep_raw_closure
+#   define FFI_RAW_SIZE ffi_raw_size
+#else
+#   define FFI_PREP_RAW_CLOSURE ffi_prep_java_raw_closure
+#   define FFI_RAW_SIZE ffi_java_raw_size
+#endif
+
+/* we put this one here, and not in interpret.cc because it
+ * calls the utility routines _Jv_count_arguments 
+ * which are static to this module.  The following struct defines the
+ * layout we use for the stubs, it's only used in the ncode method. */
+
+typedef struct {
+  ffi_raw_closure  closure;
+  ffi_cif   cif;
+  ffi_type *arg_types[0];
+} ncode_closure;
+
+typedef void (*ffi_closure_fun) (ffi_cif*,void*,ffi_raw*,void*);
+
+void *
+_Jv_InterpMethod::ncode ()
+{
+  using namespace java::lang::reflect;
+
+  if (self->ncode != 0)
+    return self->ncode;
+
+  jboolean staticp = (self->accflags & Modifier::STATIC) != 0;
+  int arg_count = _Jv_count_arguments (self->signature, staticp);
+
+  ncode_closure *closure =
+    (ncode_closure*)_Jv_AllocBytes (sizeof (ncode_closure)
+                                       + arg_count * sizeof (ffi_type*));
+
+  init_cif (self->signature,
+           arg_count,
+           staticp,
+           &closure->cif,
+           &closure->arg_types[0],
+           NULL);
+
+  ffi_closure_fun fun;
+
+  args_raw_size = FFI_RAW_SIZE (&closure->cif);
+
+  JvAssert ((self->accflags & Modifier::NATIVE) == 0);
+
+  if ((self->accflags & Modifier::SYNCHRONIZED) != 0)
+    {
+      if (staticp)
+       fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class;
+      else
+       fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object; 
+    }
+  else
+    {
+      if (staticp)
+       fun = (ffi_closure_fun)&_Jv_InterpMethod::run_class;
+      else
+       fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal;
+    }
+
+  FFI_PREP_RAW_CLOSURE (&closure->closure,
+                       &closure->cif, 
+                       fun,
+                       (void*)this);
+
+  self->ncode = (void*)closure;
+  return self->ncode;
+}
+
+void *
+_Jv_JNIMethod::ncode ()
+{
+  using namespace java::lang::reflect;
+
+  if (self->ncode != 0)
+    return self->ncode;
+
+  jboolean staticp = (self->accflags & Modifier::STATIC) != 0;
+  int arg_count = _Jv_count_arguments (self->signature, staticp);
+
+  ncode_closure *closure =
+    (ncode_closure*)_Jv_AllocBytes (sizeof (ncode_closure)
+                                   + arg_count * sizeof (ffi_type*));
+
+  ffi_type *rtype;
+  init_cif (self->signature,
+           arg_count,
+           staticp,
+           &closure->cif,
+           &closure->arg_types[0],
+           &rtype);
+
+  ffi_closure_fun fun;
+
+  args_raw_size = FFI_RAW_SIZE (&closure->cif);
+
+  // Initialize the argument types and CIF that represent the actual
+  // underlying JNI function.
+  int extra_args = 1;
+  if ((self->accflags & Modifier::STATIC))
+    ++extra_args;
+  jni_arg_types = (ffi_type **) _Jv_AllocBytes ((extra_args + arg_count)
+                                               * sizeof (ffi_type *));
+  int offset = 0;
+  jni_arg_types[offset++] = &ffi_type_pointer;
+  if ((self->accflags & Modifier::STATIC))
+    jni_arg_types[offset++] = &ffi_type_pointer;
+  memcpy (&jni_arg_types[offset], &closure->arg_types[0],
+         arg_count * sizeof (ffi_type *));
+
+  if (ffi_prep_cif (&jni_cif, _Jv_platform_ffi_abi,
+                   extra_args + arg_count, rtype,
+                   jni_arg_types) != FFI_OK)
+    throw_internal_error ("ffi_prep_cif failed for JNI function");
+
+  JvAssert ((self->accflags & Modifier::NATIVE) != 0);
+
+  // FIXME: for now we assume that all native methods for
+  // interpreted code use JNI.
+  fun = (ffi_closure_fun) &_Jv_JNIMethod::call;
+
+  FFI_PREP_RAW_CLOSURE (&closure->closure,
+                       &closure->cif, 
+                       fun,
+                       (void*) this);
+
+  self->ncode = (void *) closure;
+  return self->ncode;
+}
+
+static void
+throw_class_format_error (jstring msg)
+{
+  throw (msg
+        ? new java::lang::ClassFormatError (msg)
+        : new java::lang::ClassFormatError);
+}
+
+static void
+throw_class_format_error (char *msg)
+{
+  throw_class_format_error (JvNewStringLatin1 (msg));
+}
+
+\f
+
+void
+_Jv_InterpreterEngine::do_verify (jclass klass)
+{
+  _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info;
+  for (int i = 0; i < klass->method_count; i++)
+    {
+      using namespace java::lang::reflect;
+      _Jv_MethodBase *imeth = iclass->interpreted_methods[i];
+      _Jv_ushort accflags = klass->methods[i].accflags;
+      if ((accflags & (Modifier::NATIVE | Modifier::ABSTRACT)) == 0)
+       {
+         _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (imeth);
+         _Jv_VerifyMethod (im);
+       }
+    }
+}
+
+void
+_Jv_InterpreterEngine::do_create_ncode (jclass klass)
+{
+  _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info;
+  for (int i = 0; i < klass->method_count; i++)
+    {
+      // Just skip abstract methods.  This is particularly important
+      // because we don't resize the interpreted_methods array when
+      // miranda methods are added to it.
+      if ((klass->methods[i].accflags
+          & java::lang::reflect::Modifier::ABSTRACT)
+         != 0)
+       continue;
+
+      _Jv_MethodBase *imeth = iclass->interpreted_methods[i];
+
+      if ((klass->methods[i].accflags & java::lang::reflect::Modifier::NATIVE)
+         != 0)
+       {
+         // You might think we could use a virtual `ncode' method in
+         // the _Jv_MethodBase and unify the native and non-native
+         // cases.  Well, we can't, because we don't allocate these
+         // objects using `new', and thus they don't get a vtable.
+         _Jv_JNIMethod *jnim = reinterpret_cast<_Jv_JNIMethod *> (imeth);
+         klass->methods[i].ncode = jnim->ncode ();
+       }
+      else if (imeth != 0)             // it could be abstract
+       {
+         _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (imeth);
+         klass->methods[i].ncode = im->ncode ();
+       }
+    }
+}
+
+void
+_Jv_InterpreterEngine::do_allocate_static_fields (jclass klass,
+                                                 int static_size)
+{
+  _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info;
+
+  char *static_data = (char *) _Jv_AllocBytes (static_size);
+  memset (static_data, 0, static_size);
+
+  for (int i = 0; i < klass->field_count; i++)
+    {
+      _Jv_Field *field = &klass->fields[i];
+
+      if ((field->flags & java::lang::reflect::Modifier::STATIC) != 0)
+       {
+         field->u.addr  = static_data + field->u.boffset;
+             
+         if (iclass->field_initializers[i] != 0)
+           {
+             _Jv_Linker::resolve_field (field, klass->loader);
+             _Jv_InitField (0, klass, i);
+           }
+       }
+    }
+
+  // Now we don't need the field_initializers anymore, so let the
+  // collector get rid of it.
+  iclass->field_initializers = 0;
+}
+
+_Jv_ResolvedMethod *
+_Jv_InterpreterEngine::do_resolve_method (_Jv_Method *method, jclass klass,
+                                         jboolean staticp, jint vtable_index)
+{
+  int arg_count = _Jv_count_arguments (method->signature, staticp);
+
+  _Jv_ResolvedMethod* result = (_Jv_ResolvedMethod*)
+    _Jv_AllocBytes (sizeof (_Jv_ResolvedMethod)
+                   + arg_count*sizeof (ffi_type*));
+
+  result->stack_item_count
+    = init_cif (method->signature,
+               arg_count,
+               staticp,
+               &result->cif,
+               &result->arg_types[0],
+               NULL);
+
+  result->vtable_index        = vtable_index;
+  result->method              = method;
+  result->klass               = klass;
+
+  return result;
+}
+
+void
+_Jv_InterpreterEngine::do_post_miranda_hook (jclass klass)
+{
+  _Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info;
+  for (int i = 0; i < klass->method_count; i++)
+    {
+      // Just skip abstract methods.  This is particularly important
+      // because we don't resize the interpreted_methods array when
+      // miranda methods are added to it.
+      if ((klass->methods[i].accflags
+          & java::lang::reflect::Modifier::ABSTRACT)
+         != 0)
+       continue;
+      // Miranda method additions mean that the `methods' array moves.
+      // We cache a pointer into this array, so we have to update.
+      iclass->interpreted_methods[i]->self = &klass->methods[i];
+    }
+}
+
 #endif // INTERPRETER