// interpret.cc - Code for the interpreter
-/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation
+/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation
This file is part of libgcj.
#include <config.h>
#include <platform.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
-
#pragma implementation "java-interp.h"
#include <jvm.h>
#include <java/lang/StringBuffer.h>
#include <java/lang/Class.h>
#include <java/lang/reflect/Modifier.h>
-#include <java/lang/ClassCastException.h>
#include <java/lang/VirtualMachineError.h>
#include <java/lang/InternalError.h>
#include <java/lang/NullPointerException.h>
#include <java/lang/ArithmeticException.h>
#include <java/lang/IncompatibleClassChangeError.h>
+#include <java/lang/InstantiationException.h>
#include <java/lang/Thread.h>
#include <java-insns.h>
#include <java-signal.h>
using namespace gcj;
-static void throw_internal_error (char *msg)
+static void throw_internal_error (const char *msg)
__attribute__ ((__noreturn__));
static void throw_incompatible_class_change_error (jstring msg)
__attribute__ ((__noreturn__));
-#ifndef HANDLE_SEGV
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)
+static void throw_class_format_error (const char *msg)
__attribute__ ((__noreturn__));
#ifdef DIRECT_THREADED
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
{
sp[top-(n+x)-i] = sp[top-i];
}
-
}
// Used to convert from floating types to integral types.
| (((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) \
void* __this)
{
_Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this;
- _this->run (ret, args);
+ run (ret, args, _this);
}
void
jobject rcv = (jobject) args[0].ptr;
JvSynchronize mutex (rcv);
- _this->run (ret, args);
+ run (ret, args, _this);
}
void
{
_Jv_InterpMethod *_this = (_Jv_InterpMethod *) __this;
_Jv_InitClass (_this->defining_class);
- _this->run (ret, args);
+ run (ret, args, _this);
}
void
_Jv_InitClass (sync);
JvSynchronize mutex (sync);
- _this->run (ret, args);
+ run (ret, args, _this);
}
#ifdef DIRECT_THREADED
if (! first_pass)
{
insns = (insn_slot *) _Jv_AllocBytes (sizeof (insn_slot) * next);
+ number_insn_slots = next;
next = 0;
}
{
int index = get1u (pc);
++pc;
- SET_DATUM (pool_data[index].o);
+ // For an unresolved class we want to delay resolution
+ // until execution.
+ if (defining_class->constants.tags[index] == JV_CONSTANT_Class)
+ {
+ --next;
+ SET_INSN (insn_targets[int (op_jsr_w) + 1]);
+ SET_INT (index);
+ }
+ else
+ SET_DATUM (pool_data[index].o);
}
break;
{
int index = get2u (pc);
pc += 2;
- SET_DATUM (pool_data[index].o);
+ // For an unresolved class we want to delay resolution
+ // until execution.
+ if (defining_class->constants.tags[index] == JV_CONSTANT_Class)
+ {
+ --next;
+ SET_INSN (insn_targets[int (op_jsr_w) + 1]);
+ SET_INT (index);
+ }
+ else
+ SET_DATUM (pool_data[index].o);
}
break;
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;
+ // It isn't worth throwing an exception if this table is
+ // corrupted, but at the same time we don't want a crash.
+ if (byte_pc < 0 || byte_pc >= code_length)
+ byte_pc = 0;
+ line_table[i].pc = &insns[pc_mapping[byte_pc]];
+ }
+
prepared = insns;
}
#endif /* DIRECT_THREADED */
-// These exist so that the stack-tracing code can find the boundaries
-// of the interpreter.
-void *_Jv_StartOfInterpreter;
-void *_Jv_EndOfInterpreter;
-extern "C" void *_Unwind_FindEnclosingFunction (void *pc);
-
+/* Run the given method.
+ When args is NULL, don't run anything -- just compile it. */
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;
- // Record the address of the start of this member function in
- // _Jv_StartOfInterpreter. Such a write to a global variable
- // without acquiring a lock is correct iff reads and writes of words
- // in memory are atomic, but Java requires that anyway.
- foo:
- if (_Jv_StartOfInterpreter == NULL)
- _Jv_StartOfInterpreter = _Unwind_FindEnclosingFunction (&&foo);
-
// FRAME_DESC registers this particular invocation as the top-most
// interpreter frame. This lets the stack tracing code (for
// Throwable) print information about the method being interpreted
// 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, thread);
- _Jv_word stack[max_stack];
+ _Jv_word stack[meth->max_stack];
_Jv_word *sp = stack;
- _Jv_word locals[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);
-
- _Jv_word *pool_data = defining_class->constants.data;
-
- /* These three are temporaries for common code used by several
- instructions. */
- void (*fun)();
- _Jv_ResolvedMethod* rmeth;
- int tmpval;
+ _Jv_word locals[meth->max_locals];
#define INSN_LABEL(op) &&insn_##op
INSN_LABEL(ifnonnull),
INSN_LABEL(goto_w),
INSN_LABEL(jsr_w),
+#ifdef DIRECT_THREADED
+ INSN_LABEL (ldc_class)
+#else
0
+#endif
};
pc_t pc;
#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;
+
+ // If we're only compiling, stop here
+ if (args == NULL)
+ return;
+
+ pc = (insn_slot *) meth->prepared;
#else
#define GET2S() (pc += 2, get2s (pc- 2))
#define GET1U() get1u (pc++)
#define GET2U() (pc += 2, get2u (pc - 2))
-#define AVAL1U() ({ int index = get1u (pc++); pool_data[index].o; })
-#define AVAL2U() ({ int index = get2u (pc); pc += 2; pool_data[index].o; })
+ // Note that these could be more efficient when not handling 'ldc
+ // class'.
+#define AVAL1U() \
+ ({ int index = get1u (pc++); \
+ resolve_pool_entry (meth->defining_class, index).o; })
+#define AVAL2U() \
+ ({ int index = get2u (pc); pc += 2; \
+ resolve_pool_entry (meth->defining_class, index).o; })
+ // Note that we don't need to resolve the pool entry here as class
+ // constants are never wide.
#define AVAL2UP() ({ int index = get2u (pc); pc += 2; &pool_data[index]; })
#define SKIP_GOTO pc += 2
#define GOTO_VAL() pc - 1 + get2s (pc)
#define TAKE_GOTO pc = GOTO_VAL ()
+ /* 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, meth->args_raw_size);
+
+ _Jv_word *pool_data = meth->defining_class->constants.data;
+
+ /* These three are temporaries for common code used by several
+ instructions. */
+ void (*fun)();
+ _Jv_ResolvedMethod* rmeth;
+ int tmpval;
+
try
{
// We keep nop around. It is used if we're interpreting the
* the corresponding bit JV_CONSTANT_ResolvedFlag in the tag
* directly. For now, I don't think it is worth it. */
- rmeth = (_Jv_Linker::resolve_pool_entry (defining_class,
+ 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;
- if (rmeth->vtable_index == -1)
+ if (rmeth->method->accflags & Modifier::FINAL)
{
- // final methods do not appear in the vtable,
- // if it does not appear in the superclass.
+ // We can't rely on NULLCHECK working if the method is final.
+ SAVE_PC();
+ if (! sp[0].o)
+ throw_null_pointer_exception ();
+
+ // Final methods might not appear in the vtable.
fun = (void (*)()) rmeth->method->ncode;
}
else
{
+ NULLCHECK (sp[0].o);
jobject rcv = sp[0].o;
_Jv_VTable *table = *(_Jv_VTable**) rcv;
- fun = (void (*)()) table->get_method (rmeth->vtable_index);
+ fun = (void (*)()) table->get_method (rmeth->method->index);
}
#ifdef DIRECT_THREADED
{
rmeth = (_Jv_ResolvedMethod *) AVAL ();
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;
- if (rmeth->vtable_index == -1)
+ if (rmeth->method->accflags & Modifier::FINAL)
{
- // final methods do not appear in the vtable,
- // if it does not appear in the superclass.
+ // We can't rely on NULLCHECK working if the method is final.
+ SAVE_PC();
+ if (! sp[0].o)
+ throw_null_pointer_exception ();
+
+ // Final methods might not appear in the vtable.
fun = (void (*)()) rmeth->method->ncode;
}
else
{
jobject rcv = sp[0].o;
_Jv_VTable *table = *(_Jv_VTable**) rcv;
- fun = (void (*)()) table->get_method (rmeth->vtable_index);
+ fun = (void (*)()) table->get_method (rmeth->method->index);
}
}
goto perform_invoke;
perform_invoke:
{
+ SAVE_PC();
+
/* here goes the magic again... */
ffi_cif *cif = &rmeth->cif;
ffi_raw *raw = (ffi_raw*) sp;
PUSHA ((jobject) AVAL2U ());
NEXT_INSN;
+#ifdef DIRECT_THREADED
+ // For direct threaded we have a separate 'ldc class' operation.
+ insn_ldc_class:
+ {
+ // We could rewrite the instruction at this point.
+ int index = INTVAL ();
+ jobject k = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
+ index)).o;
+ PUSHA (k);
+ }
+ NEXT_INSN;
+#endif /* DIRECT_THREADED */
+
insn_ldc2_w:
{
void *where = AVAL2UP ();
insn_getstatic:
{
jint fieldref_index = GET2U ();
- _Jv_Linker::resolve_pool_entry (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)
insn_getfield:
{
jint fieldref_index = GET2U ();
- _Jv_Linker::resolve_pool_entry (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)
insn_putstatic:
{
jint fieldref_index = GET2U ();
- _Jv_Linker::resolve_pool_entry (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;
insn_putfield:
{
jint fieldref_index = GET2U ();
- _Jv_Linker::resolve_pool_entry (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;
{
int index = GET2U ();
- rmeth = (_Jv_Linker::resolve_pool_entry (defining_class,
+ 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_null_pointer_exception ();
+ }
fun = (void (*)()) rmeth->method->ncode;
// 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_null_pointer_exception ();
+ }
fun = (void (*)()) rmeth->method->ncode;
}
goto perform_invoke;
{
int index = GET2U ();
- rmeth = (_Jv_Linker::resolve_pool_entry (defining_class,
+ rmeth = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
index)).rmethod;
sp -= rmeth->stack_item_count;
{
int index = GET2U ();
- rmeth = (_Jv_Linker::resolve_pool_entry (defining_class,
+ rmeth = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
index)).rmethod;
sp -= rmeth->stack_item_count;
insn_new:
{
int index = GET2U ();
- jclass klass = (_Jv_Linker::resolve_pool_entry (defining_class,
+ jclass klass = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
index)).clazz;
+ /* VM spec, section 3.11.5 */
+ if ((klass->getModifiers() & Modifier::ABSTRACT)
+ || klass->isInterface())
+ throw new java::lang::InstantiationException;
jobject res = _Jv_AllocObject (klass);
PUSHA (res);
insn_anewarray:
{
int index = GET2U ();
- jclass klass = (_Jv_Linker::resolve_pool_entry (defining_class,
+ jclass klass = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
index)).clazz;
int size = POPI();
jobject result = _Jv_NewObjectArray (size, klass, 0);
insn_checkcast:
{
+ SAVE_PC();
jobject value = POPA();
jint index = GET2U ();
- jclass to = (_Jv_Linker::resolve_pool_entry (defining_class,
+ 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());
+ value = (jobject) _Jv_CheckCast (to, value);
PUSHA (value);
#ifdef DIRECT_THREADED
checkcast_resolved:
{
+ SAVE_PC();
jobject value = POPA ();
jclass to = (jclass) AVAL ();
- if (value != NULL && ! to->isInstance (value))
- throw new java::lang::ClassCastException (to->getName());
+ value = (jobject) _Jv_CheckCast (to, value);
PUSHA (value);
}
NEXT_INSN;
insn_instanceof:
{
+ SAVE_PC();
jobject value = POPA();
jint index = GET2U ();
- jclass to = (_Jv_Linker::resolve_pool_entry (defining_class,
+ jclass to = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
index)).clazz;
PUSHI (to->isInstance (value));
int dim = GET1U ();
jclass type
- = (_Jv_Linker::resolve_pool_entry (defining_class,
+ = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
kind_index)).clazz;
jint *sizes = (jint*) __builtin_alloca (sizeof (jint)*dim);
#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))
}
static void
-throw_internal_error (char *msg)
+throw_internal_error (const char *msg)
{
throw new java::lang::InternalError (JvNewStringLatin1 (msg));
}
throw new java::lang::IncompatibleClassChangeError (msg);
}
-#ifndef HANDLE_SEGV
-static java::lang::NullPointerException *null_pointer_exc;
static void
throw_null_pointer_exception ()
{
- if (null_pointer_exc == NULL)
- null_pointer_exc = new java::lang::NullPointerException;
+ throw new java::lang::NullPointerException;
+}
- throw null_pointer_exc;
+/* 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;
}
-#endif
/** Do static initialization for fields with a constant initializer */
void
return self->ncode;
}
+#ifdef DIRECT_THREADED
+/* Find the index of the given insn in the array of insn slots
+ for this method. Returns -1 if not found. */
+jlong
+_Jv_InterpMethod::insn_index (pc_t pc)
+{
+ jlong left = 0;
+ jlong right = number_insn_slots;
+ insn_slot* slots = reinterpret_cast<insn_slot*> (prepared);
+
+ while (right >= 0)
+ {
+ jlong mid = (left + right) / 2;
+ if (&slots[mid] == pc)
+ return mid;
+
+ if (pc < &slots[mid])
+ right = mid - 1;
+ else
+ left = mid + 1;
+ }
+
+ return -1;
+}
+#endif // DIRECT_THREADED
+
+void
+_Jv_InterpMethod::get_line_table (jlong& start, jlong& end,
+ jintArray& line_numbers,
+ jlongArray& code_indices)
+{
+#ifdef DIRECT_THREADED
+ /* For the DIRECT_THREADED case, if the method has not yet been
+ * compiled, the linetable will change to insn slots instead of
+ * bytecode PCs. It is probably easiest, in this case, to simply
+ * compile the method and guarantee that we are using insn
+ * slots.
+ */
+ _Jv_CompileMethod (this);
+
+ if (line_table_len > 0)
+ {
+ start = 0;
+ end = number_insn_slots;
+ line_numbers = JvNewIntArray (line_table_len);
+ code_indices = JvNewLongArray (line_table_len);
+
+ jint* lines = elements (line_numbers);
+ jlong* indices = elements (code_indices);
+ for (int i = 0; i < line_table_len; ++i)
+ {
+ lines[i] = line_table[i].line;
+ indices[i] = insn_index (line_table[i].pc);
+ }
+ }
+#else // !DIRECT_THREADED
+ if (line_table_len > 0)
+ {
+ start = 0;
+ end = code_length;
+ line_numbers = JvNewIntArray (line_table_len);
+ code_indices = JvNewLongArray (line_table_len);
+
+ jint* lines = elements (line_numbers);
+ jlong* indices = elements (code_indices);
+ for (int i = 0; i < line_table_len; ++i)
+ {
+ lines[i] = line_table[i].line;
+ indices[i] = (jlong) line_table[i].bytecode_pc;
+ }
+ }
+#endif // !DIRECT_THREADED
+}
+
void *
_Jv_JNIMethod::ncode ()
{
}
static void
-throw_class_format_error (char *msg)
+throw_class_format_error (const char *msg)
{
throw_class_format_error (JvNewStringLatin1 (msg));
}
void
_Jv_InterpreterEngine::do_allocate_static_fields (jclass klass,
- int static_size)
+ int pointer_size,
+ int other_size)
{
_Jv_InterpClass *iclass = (_Jv_InterpClass *) klass->aux_info;
- char *static_data = (char *) _Jv_AllocBytes (static_size);
- memset (static_data, 0, static_size);
+ // Splitting the allocations here lets us scan reference fields and
+ // avoid scanning non-reference fields. How reference fields are
+ // scanned is a bit tricky: we allocate using _Jv_AllocRawObj, which
+ // means that this memory will be scanned conservatively (same
+ // difference, since we know all the contents here are pointers).
+ // Then we put pointers into this memory into the 'fields'
+ // structure. Most of these are interior pointers, which is ok (but
+ // even so the pointer to the first reference field will be used and
+ // that is not an interior pointer). The 'fields' array is also
+ // allocated with _Jv_AllocRawObj (see defineclass.cc), so it will
+ // be scanned. A pointer to this array is held by Class and thus
+ // seen by the collector.
+ char *reference_fields = (char *) _Jv_AllocRawObj (pointer_size);
+ char *non_reference_fields = (char *) _Jv_AllocBytes (other_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)
+ if ((field->flags & java::lang::reflect::Modifier::STATIC) == 0)
+ continue;
+
+ char *base = field->isRef() ? reference_fields : non_reference_fields;
+ field->u.addr = base + field->u.boffset;
+
+ if (iclass->field_initializers[i] != 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);
- }
+ _Jv_Linker::resolve_field (field, klass->loader);
+ _Jv_InitField (0, klass, i);
}
}
_Jv_ResolvedMethod *
_Jv_InterpreterEngine::do_resolve_method (_Jv_Method *method, jclass klass,
- jboolean staticp, jint vtable_index)
+ jboolean staticp)
{
int arg_count = _Jv_count_arguments (method->signature, staticp);
&result->arg_types[0],
NULL);
- result->vtable_index = vtable_index;
result->method = method;
result->klass = klass;
}
}
+#ifdef DIRECT_THREADED
+void
+_Jv_CompileMethod (_Jv_InterpMethod* method)
+{
+ if (method->prepared == NULL)
+ _Jv_InterpMethod::run (NULL, NULL, method);
+}
+#endif // DIRECT_THREADED
+
#endif // INTERPRETER