netscape-revival
sun-java/runtime/interpreter.c
/*
* @(#)interpreter.c 1.174 96/03/30
*
* Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
/*
* Interpret an Java object file
*/
#ifdef XP_MAC
#include <ConditionalMacros.h>
#ifndef DEBUG
#pragma global_optimizer on
#pragma optimization_level 1
#endif
#endif
#include <sys/types.h>
#include <string.h>
#include <stdarg.h>
#include "bool.h"
#include "jriext.h"
#ifndef XP_MAC
#include <memory.h>
#endif
#include "opcodes.h"
#include "oobj.h"
#include "interpreter.h"
#include "signature.h"
#include "code.h"
#include "tree.h"
#include "javaThreads.h"
#include "monitor.h"
#include "exceptions.h"
#include "javaString.h"
#include "path.h"
#include "byteorder.h"
#include "jmath.h"
#include "sys_api.h"
#include "java_lang_Thread.h"
#include "java_lang_Throwable.h"
#if defined(XP_PC) && !defined(_WIN32)
#if !defined(stderr)
extern FILE *stderr;
#endif
#if !defined(stdout)
extern FILE *stdout;
#endif
#endif
#ifdef BREAKPTS
#include "sun_tools_debug_BreakpointQueue.h"
#endif
#ifdef TRACING
int trace;
int tracem;
# define TRACE(a) (trace ? printf("%6lX %8lX", (int32_t)threadSelf(), (int32_t)pc), \
printf a, fflush(stdout), 0 : 0)
# define TRACEIF(o, a) (opcode == o ? TRACE(a) : 0)
#else
# define TRACE(a)
# define TRACEIF(a, b)
#endif
#ifdef STATISTICS
int instruction_count[256];
#endif
#define UCAT(base,offset) (((unsigned char *)base)[offset])
#define ULCAST(x) ((unsigned long)(x))
#define USCAST(x) ((unsigned short)(x))
#define SCAT(base,offset) (((signed char *)base)[offset])
#define SLCAST(x) ((long)(x))
#define SSCAST(x) ((short)(x))
ExecEnv *DefaultExecEnv;
/* Some JIT compilers don't want to use the versions of
* opc_invokevirtual_quick and opc_getfield_quick that lose information
*/
bool_t UseLosslessQuickOpcodes=FALSE;
/*
* Java stack management
*/
/*
* Lock around code freeing deleted Java stack segments
*/
static sys_mon_t *_ostack_lock;
#define OSTACK_LOCK_INIT() monitorRegister(_ostack_lock, "Java stack lock")
#define OSTACK_LOCK() sysMonitorEnter(_ostack_lock)
#define OSTACK_UNLOCK() sysMonitorExit(_ostack_lock)
void FreeJavaStackMemory(void);
#ifdef WIN32
static void HACK_NOP() {
/* This is a hack to work around an optimizer bug in MSVC++ */
}
#else
#define HACK_NOP()
#endif
JavaStack *CreateNewJavaStack(ExecEnv *ee, JavaStack *previous_stack)
{
JavaStack *stack;
FreeJavaStackMemory(); /* Free any stack segments for dead threads */
stack = sysMalloc(sizeof (*stack));
if (stack == 0) {
return 0;
}
stack->execenv = ee;
stack->prev = previous_stack;
stack->next = 0;
if (previous_stack) {
sysAssert(previous_stack->next == 0);
previous_stack->next = stack;
stack->stack_so_far = previous_stack->stack_so_far
+ JAVASTACK_CHUNK_SIZE * sizeof(stack->data[0]);
} else {
stack->stack_so_far = JAVASTACK_CHUNK_SIZE * sizeof(stack->data[0]);
}
stack->end_data = &stack->data[JAVASTACK_CHUNK_SIZE];
return stack;
}
extern const JRIEnvInterface jri_NativeInterface;
extern const JRIRuntimeInterface jri_RuntimeInterface;
extern const JRIReflectionInterface jri_ReflectionInterface;
extern const JRIDebuggerInterface jri_DebuggerInterface;
extern const JRICompilerInterface jri_CompilerInterface;
extern const JRIExprInterface jri_ExprInterface;
/*
* Note that CreatenewJavaStack may return 0 if we run out of malloc
* space. Users of InitializeExecEnv should test for this case by checking
* ee->initial_stack, and throw an OutOfMemoryError if that's possible or
* otherwise call out_of_memory (for instance in the middle of startup).
*/
void InitializeExecEnv(ExecEnv *ee, JHandle *thread) {
if (DefaultExecEnv == 0 && thread == 0) {
DefaultExecEnv = ee;
}
ee->current_frame = 0;
ee->thread = thread;
ee->initial_stack = CreateNewJavaStack(ee, 0); /* 0 if out of memory */
if (thread) { /* we don't have thread at initialization */
unhand((Hjava_lang_Thread *)thread)->eetop = (long)ee;
}
exceptionClear(ee);
/* Stuff for the JRI: */
ee->nativeInterface = &jri_NativeInterface;
ee->runtimeInterface = &jri_RuntimeInterface;
ee->reflectionInterface = &jri_ReflectionInterface;
ee->debuggerInterface = &jri_DebuggerInterface;
ee->compilerInterface = &jri_CompilerInterface;
ee->exprInterface = &jri_ExprInterface;
ee->globalRefs.element = NULL;
ee->globalRefs.size = 0;
ee->globalRefs.top = 0;
ee->mochaContext = NULL;
#ifdef JAVA_PREEMPT_USING_COUNT
ee->opcodeCount = 0;
#endif
#ifdef DEBUG
ee->debugFlags = 0;
#endif
}
#ifdef DEBUG
static int java_stack_chunks_freed = 0;
#endif
/*
* The list on which we put freed thread stacks pending reclamation.
*/
typedef struct free_javastack_t {
struct free_javastack_t *next;
} free_javastack_t;
static free_javastack_t *javastackFreeList = (free_javastack_t *) 0;
void DeleteExecEnv(ExecEnv *ee, JHandle *thread) {
JavaStack *this_stack;
if (thread) {
unhand((Hjava_lang_Thread *)thread)->eetop = (long)0;
}
OSTACK_LOCK(); /* Could do per chunk, but higher locking cost */
for (this_stack = ee->initial_stack; this_stack != 0; ) {
JavaStack *next_stack = this_stack->next;
((free_javastack_t *) this_stack)->next = javastackFreeList;
javastackFreeList = (free_javastack_t *) this_stack;
#ifdef DEBUG
java_stack_chunks_freed++;
#endif
this_stack = next_stack;
}
/* JRI */
{
RefTable* refs = &ee->globalRefs;
if (refs->element) {
sysFree(refs->element);
refs->element = NULL;
}
refs->size = 0;
refs->top = 0;
}
OSTACK_UNLOCK();
ee->initial_stack = 0;
/* Free the EE structure which was allocated in ThreadRT0(...) */
sysFree(ee);
}
/*
* Free Java stack segments for dead threads. We do this whenever the
* idle thread runs, and whenever we grow an Java stack.
*/
void FreeJavaStackMemory() {
free_javastack_t* mem;
free_javastack_t* next;
/* There's a race in this test, but losing only delays freeing */
if (javastackFreeList != NULL) {
OSTACK_LOCK();
mem = javastackFreeList;
javastackFreeList = 0;
OSTACK_UNLOCK();
while (mem) {
next = mem->next;
sysFree(mem);
mem = next;
}
}
}
bool_t ExecuteJava(unsigned char *, ExecEnv *ee);
/* Call an Java method from C. The java method may have any number of arguments
* and may return a single 4 byte value which may be an integer, a pointer to
* an object handle, whatever.
*/
JRI_PUBLIC_API(long)
execute_java_dynamic_method(ExecEnv *ee, HObject *obj,
char *method_name, char *signature, ...)
{
va_list args;
long result;
va_start(args, signature);
result = do_execute_java_method_vararg(ee, obj, method_name, signature,
0, FALSE, args, 0, FALSE);
va_end(args);
return result;
}
/* Call a static Java method from C. The java method may have any number of
* arguments and may return a single 4-byte value.`
*/
long
execute_java_static_method(ExecEnv *ee, ClassClass *cb,
char *method_name, char *signature, ...)
{
va_list args;
long result;
va_start(args, signature);
result = do_execute_java_method_vararg(ee, cb, method_name, signature,
0, TRUE, args, 0, FALSE);
va_end(args);
return result;
}
/* Create a new Java object from C. For example:
*
* obj = execute_java_constructor(0, "java/lang/Float", 0, "(F)", f);
*
* EE should be either
* .) the current execution environment
* .) 0, in which case the current execution environment is calculated
* .) PRIVILEGED_EE, meaning explicitly allow this call to access
* constructors for which the current EE doesn't have privileges.
* [i.e. you can access private constructors, etc.]
*
* Either classname or cb gives the class of the object to be constructed.
* Pass the name of the class in classname, or a pointer to the class object
* in cb. Whichever one you give, the other should be NULL.
*
* signature is the signature of the arguments to the constructor. It's
* first and last character should be "(" and ")" respectively. Following
* are the arguments to the function.
*/
HObject *execute_java_constructor_vararg(struct execenv *ee,
char *classname,
ClassClass *cb,
char *signature, va_list args)
{
#ifdef TRACING
int pc = 0; /* for TRACE */
#endif
HObject *obj;
char real_signature[256];
hash_t initID;
int len;
ClassClass *current_class;
struct methodblock *mb;
bool_t security = TRUE;
if (ee == PRIVILEGED_EE) {
ee = EE();
security = FALSE;
} else if (ee == 0) {
ee = EE();
}
if (ee && ee->current_frame && ee->current_frame->current_method)
current_class = fieldclass(&ee->current_frame->current_method->fb);
else
current_class = 0;
/* Find the class. Make sure we can allocate an object of that class */
if (cb == NULL) {
cb = FindClass(0, classname, TRUE);
if (cb == NULL) {
if (!ee || !exceptionOccurred(ee)) {
SignalError(0, JAVAPKG "NoClassDefFoundError", classname);
}
return 0;
}
} else {
classname = classname(cb);
}
if (cbAccess(cb) & (ACC_INTERFACE | ACC_ABSTRACT)) {
SignalError(0, JAVAPKG "InstantiationException", classname(cb));
return 0;
}
if (security && !VerifyClassAccess(current_class, cb, FALSE)) {
SignalError(0, JAVAPKG "IllegalAccessException", classname(cb));
return 0;
}
/* Find the constructor */
if (jio_snprintf(real_signature, sizeof(real_signature), "%sV", signature) == -1) {
SignalError(0, JAVAPKG "InternalError", "signature overflow");
return 0;
}
/* Only call 'universal' methods (all argument and result types local). */
initID = NameAndTypeToHash("<init>", real_signature, 0);
for (len = cb->methods_count, mb = cb->methods; --len >= 0; mb++)
if (mb->fb.ID == initID)
break;
if (len < 0) {
SignalError(0, JAVAPKG "NoSuchMethodError", 0);
return 0;
}
if (security && !VerifyFieldAccess(current_class, fieldclass(&mb->fb),
mb->fb.access, FALSE)) {
SignalError(0, JAVAPKG "IllegalAccessException", 0);
return 0;
}
/* Allocate the object and call the constructor */
if ((obj = newobject(cb, 0, ee)) == 0) {
SignalError(0, JAVAPKG "OutOfMemoryError", 0);
return 0;
}
TRACE(("execute_java_constructor new %s => %s\n",
classname(cb), Object2CString(obj)));
do_execute_java_method_vararg(ee, obj, 0, 0, mb, FALSE, args, 0, FALSE);
return obj;
}
JRI_PUBLIC_API(HObject *)
execute_java_constructor(struct execenv *ee,
char *classname,
ClassClass *cb,
char *signature, ...)
{
HObject *obj;
va_list args;
va_start(args, signature);
obj = execute_java_constructor_vararg(ee, classname, cb, signature, args);
va_end(args);
return obj;
}
JRI_PUBLIC_API(long)
do_execute_java_method(ExecEnv *ee, void *obj,
char *method_name, char *signature,
struct methodblock *mb,
bool_t isStaticCall, ...) {
va_list args;
long result;
va_start(args, isStaticCall);
result = do_execute_java_method_vararg(ee, obj,
method_name, signature,
mb, isStaticCall, args, 0, FALSE);
va_end(args);
return result;
}
long
do_execute_java_method_vararg(ExecEnv *ee, void *obj,
char *method_name, char *method_signature,
struct methodblock *mb,
bool_t isStaticCall, va_list args,
long *otherBits, bool_t shortFloats) {
#ifdef TRACING
int pc = 0; /* for TRACE */
#endif
JavaFrame *current_frame, *previous_frame;
JavaStack *current_stack;
int32_t retval = 0;
char *p;
Java8 tdub;
if (ee == 0)
ee = EE();
if (otherBits) {
*otherBits = 0;
}
if (mb) {
method_name = fieldname(&mb->fb);
method_signature = fieldsig(&mb->fb);
}
TRACE(("\texecute_java_method_vararg method = %s, signature = %s\n\n", method_name, method_signature));
previous_frame = ee->current_frame;
if (previous_frame == 0) {
/* bottommost frame on this Exec Env. */
current_stack = ee->initial_stack;
current_frame = (JavaFrame *)(current_stack->data); /* no vars */
} else {
int args_size = mb ? mb->args_size
/* We don't need the +1 if a static call, but it's
* okay to make args_size too big. */
: (Signature2ArgsSize(method_signature) + 1);
current_stack = previous_frame->javastack; /* assume same stack */
if (previous_frame->current_method) {
int size = previous_frame->current_method->maxstack;
current_frame = (JavaFrame *)(&previous_frame->ostack[size]);
} else {
/* The only frames that don't have a mb are pseudo frames like
* this one and they don't really touch their stack. */
current_frame = (JavaFrame *)(previous_frame->optop + 3);
}
if (current_frame->ostack + args_size > current_stack->end_data) {
/* Ooops. The current stack isn't big enough. */
if (current_stack->next != 0) {
current_stack = current_stack->next;
} else {
current_stack = CreateNewJavaStack(ee, current_stack);
if (current_stack == 0) {
SignalError(0, JAVAPKG "OutOfMemoryError", 0);
return 0;
}
}
current_frame = (JavaFrame *)(current_stack->data); /* no vars */
}
}
ee->current_frame = current_frame;
current_frame->prev = previous_frame;
current_frame->javastack = current_stack;
current_frame->optop = current_frame->ostack;
current_frame->vars = 0; /* better not reference any! */
current_frame->monitor = 0; /* not monitoring anything */
current_frame->annotation = 0;
current_frame->current_method = 0; /* not part of a method. */
/* Push the target object, if not a static call */
if (!isStaticCall)
(current_frame->optop++)->p = obj;
for (p = method_signature + 1; *p != SIGNATURE_ENDFUNC; p++) {
switch (*p) {
case SIGNATURE_BOOLEAN:
case SIGNATURE_SHORT:
case SIGNATURE_BYTE:
case SIGNATURE_CHAR:
case SIGNATURE_INT:
(current_frame->optop++)->i = va_arg(args, long);
break;
case SIGNATURE_FLOAT:
if (shortFloats)
(current_frame->optop++)->i = va_arg(args, long);
else
(current_frame->optop++)->f = (float) va_arg(args, double);
break;
case SIGNATURE_CLASS:
(current_frame->optop++)->h = va_arg(args, HObject *);
while (*p != SIGNATURE_ENDCLASS) p++;
break;
case SIGNATURE_ARRAY:
while ((*p == SIGNATURE_ARRAY)) p++;
if (*p == SIGNATURE_CLASS)
{ while (*p != SIGNATURE_ENDCLASS) p++; }
(current_frame->optop++)->p = va_arg(args, void *);
break;
case SIGNATURE_LONG:
SET_INT64(tdub, current_frame->optop, va_arg(args, int64_t));
current_frame->optop += 2;
break;
case SIGNATURE_DOUBLE:
SET_DOUBLE(tdub, current_frame->optop, va_arg(args, double));
current_frame->optop += 2;
break;
default:
fprintf(stderr, "Invalid method signature '%s'\n", method_name);
return 0;
}
}
{
ClassClass *cb = (isStaticCall || mb != 0) ? obj
: obj_array_classblock((JHandle *)obj);
unsigned char pc[6];
cp_item_type constant_pool[10];
unsigned char cpt[10];
bool_t isInitializer = (method_name[0] == '<' && /* fast test */
!isStaticCall &&
!strcmp(method_name, "<init>"));
constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].p = cpt;
cpt[0] = CONSTANT_POOL_ENTRY_RESOLVED;
if (isStaticCall || mb) {
pc[0] = isStaticCall ? (mb ? opc_invokestatic_quick
: opc_invokestatic)
: opc_invokenonvirtual_quick;
pc[1] = 0; pc[2] = 1; /* constant pool entry #1 */
pc[3] = opc_return;
} else {
int nargs = (current_frame->optop - current_frame->ostack);
pc[0] = isInitializer ? opc_invokenonvirtual
: opc_invokeinterface;
pc[1] = 0; pc[2] = 1; /* constant pool entry #1 */
pc[3] = nargs;
pc[4] = 0; /* padding needed for guess */
pc[5] = opc_return;
}
if (mb) {
constant_pool[1].p = mb;
cpt[1] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Methodref;
} else {
constant_pool[1].i = (ULCAST(2) << 16) + 3; /* Class, NameAndType */
cpt[1] = (isStaticCall || isInitializer)
? CONSTANT_Methodref : CONSTANT_InterfaceMethodref;
constant_pool[2].p = cb; /* class */
cpt[2] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Class;
constant_pool[3].i = (ULCAST(4) << 16) + 5; /* Name, Type */
cpt[3] = CONSTANT_NameAndType;
constant_pool[4].cp = method_name;
cpt[4] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Utf8;
constant_pool[5].cp = method_signature;
cpt[5] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Utf8;
}
current_frame->constant_pool = constant_pool;
/* Run the byte codes outside the lock and catch any exceptions. */
ee->exceptionKind = EXCKIND_NONE;
if (ExecuteJava(pc, ee) && (p[1] != SIGNATURE_VOID)) {
retval = current_frame->optop[-1].i;
if ((p[1] == SIGNATURE_DOUBLE || p[1] == SIGNATURE_LONG)
&& (otherBits != 0))
*otherBits = current_frame->optop[-2].i;
}
/* Our caller can look at ee->exceptionKind and ee->exception. */
ee->current_frame = previous_frame;
return retval;
}
}
char *
Object2CString(JHandle *oh)
{ /* ARB to C string */
ClassObject *o;
ClassClass *cb;
/* Using a static buffer is not MT-safe! */
static char buf[100];
int maxlen = sizeof(buf) - 10;
if (oh == 0)
return "NULL";
o = unhand(oh);
if (-1000 < (long) o && (long) o < 1000)
/* Silently truncate on unlikely overflow */
(void) jio_snprintf(buf, sizeof(buf), "BOGUS-PTR[%ld]", (int32_t)o);
else {
switch (obj_flags(oh)) {
case T_NORMAL_OBJECT:
{
char cbuf[128];
cb = obj_classblock(oh);
/* Quietly truncate on buffer overflow */
(void) jio_snprintf(buf, sizeof(buf), "%s@%lX%c%lX",
classname2string(classname(cb), cbuf, sizeof(cbuf)),
(int32_t)oh, DIR_SEPARATOR, (int32_t)o);
}
break;
case T_CHAR: {
HArrayOfChar *str = (HArrayOfChar *) oh;
int32_t len = (int32_t) obj_length(str);
unicode *s = unhand(str)->body;
char *p = buf;
len = (len > maxlen ? maxlen : len);
*p++ = '"';
while (--len >= 0)
*p++ = (char) *s++;
*p++ = '"';
*p++ = '\0';
break;
}
case T_CLASS:
#define BODYOF(h) unhand(h)->body
{
char cbuf[128];
cb = ((ClassClass **) BODYOF((HArrayOfObject*)oh))[obj_length(oh)];
/* Quietly truncate on buffer overflow */
(void) jio_snprintf(buf, sizeof(buf), "%s[%d]",
cb ? classname2string(classname(cb), cbuf, sizeof(cbuf)) : "*MissingClassName*",
obj_length(oh));
}
break;
default:
/* Quietly truncate on buffer overflow */
(void) jio_snprintf(buf, sizeof(buf), "%s[%ld]",
arrayinfo[obj_flags(oh)].name, obj_length(oh));
}
}
return buf;
}
#ifdef BREAKPTS
static Hsun_tools_debug_BreakpointQueue *the_bkptQ = NULL;
static ClassClass *bkptHandler_class = NULL;
static int single_stepping = 0; /* debugger sets this */
void set_single_stepping(bool_t b) {
if (b) {
single_stepping++;
} else if (single_stepping > 0) {
single_stepping--;
}
}
static void
notify_debugger(HThread *hp, unsigned char *pc, HObject *exc,
unsigned char *catch_pc)
{
/* transfer control to the debugger thread by signalling the monitor. */
monitorEnter(obj_monitor(the_bkptQ));
/* communicate the information about this breakpoint */
unhand(the_bkptQ)->thread = hp;
unhand(the_bkptQ)->pc = (long)pc;
unhand(the_bkptQ)->opcode = -1;
unhand(the_bkptQ)->exception = (Hjava_lang_Throwable *)exc;
unhand(the_bkptQ)->catch_pc = (long)catch_pc;
unhand(the_bkptQ)->updated = FALSE;
/* this will yield to the debugger thread since it is running at */
/* MaximumPriority */
monitorNotifyAll(obj_monitor(the_bkptQ));
monitorExit(obj_monitor(the_bkptQ));
/* Win32 fails to block until the debugger thread is through, so
* wait for it to explicitly signal it's done. */
while (unhand(the_bkptQ)->updated == FALSE) {
monitorEnter(obj_monitor(bkptHandler_class));
monitorWait(obj_monitor(bkptHandler_class), 100);
monitorExit(obj_monitor(bkptHandler_class));
}
}
static unsigned char
get_breakpoint_opcode(unsigned char *pc, ExecEnv *ee)
{
HThread *hp = threadSelf();
int real_opcode;
ClassClass *agent_class;
long fSystemThread;
/* If this is the first time we're called, perform initialization. */
if (the_bkptQ == 0) {
Hsun_tools_debug_BreakpointQueue **bkptQ;
if (bkptHandler_class == NULL) {
/* Find the class */
bkptHandler_class = FindClass(ee, INTRP_BRKPT_STRING, TRUE);
if (bkptHandler_class == NULL) {
return opc_breakpoint;
}
}
/* Find the static variable that contains the queue */
bkptQ =(Hsun_tools_debug_BreakpointQueue **)
getclassvariable(bkptHandler_class, "the_bkptQ");
if (bkptQ == 0 || *bkptQ == 0) {
#ifdef DEBUG
printf("?breakpoint queue is null?\n");
#endif
SignalError(ee, JAVAPKG "InternalError",
"debugger not initialized");
return opc_breakpoint;
} else {
the_bkptQ = *bkptQ;
}
}
/* ignore non-debuggable threads */
agent_class = FindClass(ee, "sun/tools/debug/Agent", TRUE);
if (agent_class == NULL) {
return opc_breakpoint;
}
fSystemThread = execute_java_static_method(EE(), agent_class, "systemThread",
"(Ljava/lang/Thread;)Z", hp);
if (fSystemThread != 0) {
return execute_java_static_method(
EE(), (ClassClass *)bkptHandler_class,
"getOpcode", "(I)I", pc);
}
notify_debugger(hp, pc, NULL, NULL);
real_opcode = unhand(the_bkptQ)->opcode;
if (real_opcode == -1) {
real_opcode = execute_java_static_method(
EE(), (ClassClass *)bkptHandler_class,
"getOpcode", "(I)I", pc);
}
unhand(the_bkptQ)->opcode = -1;
return real_opcode;
}
void
notify_debugger_of_exception(unsigned char *pc, ExecEnv *ee, HObject *object)
{
int real_opcode;
unsigned char *catch_pc;
struct javaframe *fr;
static unsigned char *ProcedureFindThrowTag();
if (debugagent == 0 || get_the_bkptQ() == NULL) {
return;
}
/* walk up the stack to see if this exception is caught anywhere. */
catch_pc = NULL;
fr = ee->current_frame;
while (fr != NULL) {
if (fr->current_method != NULL) { /* skip C frames */
catch_pc = ProcedureFindThrowTag(fr, object, fr->lastpc);
if (catch_pc != NULL) {
break;
}
}
fr = fr->prev;
}
real_opcode = unhand(the_bkptQ)->opcode;
notify_debugger(threadSelf(), pc, object, catch_pc);
unhand(the_bkptQ)->opcode = real_opcode;
}
static void
set_breakpoint_opcode(unsigned char *pc, unsigned char opcode)
{
HThread *hp = threadSelf();
/* transfer control to the debugger thread by signalling the monitor. */
monitorEnter(obj_monitor(the_bkptQ));
/* communicate the information about this breakpoint */
unhand(the_bkptQ)->thread = hp;
unhand(the_bkptQ)->pc = (int)pc;
unhand(the_bkptQ)->opcode = opcode;
/* Yield to the debugger thread, running at Maximum priority. */
monitorNotifyAll(obj_monitor(the_bkptQ));
monitorExit(obj_monitor(the_bkptQ));
}
#endif
bool_t is_subclass_of(ClassClass *, ClassClass *, ExecEnv *);
JRI_PUBLIC_API(bool_t)
is_instance_of(JHandle * h, ClassClass *dcb, ExecEnv *ee)
{
if (h == NULL) { /* null can be cast to anything */
return TRUE;
} else if (obj_flags(h) == T_NORMAL_OBJECT) {
/* h is an object. Get its class and to the work */
return is_subclass_of(obj_classblock(h), dcb, ee);
} else {
/* h is an array. dcb must either be classJavaLangObject, or
interfaceJavaLangCloneable, or an array class */
return dcb->name[0] == SIGNATURE_ARRAY
? array_is_instance_of_array_type(h, dcb, ee)
: (dcb == classJavaLangObject
|| dcb == interfaceJavaLangCloneable);
}
}
bool_t
is_subclass_of(ClassClass *cb, ClassClass *dcb, ExecEnv *ee)
{
if (dcb->access & ACC_INTERFACE) {
for (; ; cb = unhand(cbSuperclass(cb))) {
if (ImplementsInterface(cb, dcb, ee))
return TRUE;
if (cbSuperclass(cb) == 0)
return FALSE;
}
} else {
for (; ; cb = unhand(cbSuperclass(cb))) {
if (cb == dcb)
return TRUE;
if (cbSuperclass(cb) == 0)
return FALSE;
}
}
}
/* Return TRUE if cb implements the interface icb */
bool_t
ImplementsInterface(ClassClass *cb, ClassClass *icb, ExecEnv *ee)
{
/* Let's avoid resolving the class unless we really have to. So first,
* First, do a string comparison of icb against all the interfaces thant
* cb implements.
*/
char *icb_name = icb->name;
cp_item_type *constant_pool = cb->constantpool;
int i;
for (i = 0; i < (int)(cb->implements_count); i++) {
int interface_index = cb->implements[i];
char *interface_name =
GetClassConstantClassName(constant_pool, interface_index);
/* First, check to see if the names match, so we know whether we
* need to bother trying to load in the interface definition to check
* for class loaders, etc */
if (strcmp(icb_name, interface_name) == 0) {
if (cbLoader(icb) == cbLoader(cb)) {
return TRUE;
} else {
ClassClass *sub_cb;
if (!ResolveClassConstantFromClass(cb, interface_index, ee,
1 << CONSTANT_Class))
return FALSE;
sub_cb = (ClassClass *)(constant_pool[interface_index].p);
if (sub_cb == icb)
return TRUE;
/* printf(" bogus match skipped\n");*//* should we break?? return false? */
}
}
}
/* See if any of the interfaces that we implement possibly implement icb */
for (i = 0; i < (int)(cb->implements_count); i++) {
int interface_index = cb->implements[i];
ClassClass *sub_cb;
if (!ResolveClassConstantFromClass(cb, interface_index, ee,
1 << CONSTANT_Class))
return FALSE;
sub_cb = (ClassClass *)(constant_pool[interface_index].p);
if (ImplementsInterface(sub_cb, icb, ee)) {
return TRUE;
}
}
return FALSE;
}
bool_t array_is_instance_of_array_type(JHandle * h, ClassClass *cb, ExecEnv *ee)
{
int32_t class_depth = cb->constantpool[CONSTANT_POOL_ARRAY_DEPTH_INDEX].i;
int32_t class_type = cb->constantpool[CONSTANT_POOL_ARRAY_TYPE_INDEX].i;
int32_t item_depth, item_type;
ClassClass *item_class, *class_class;
ResolveClassConstantFromClass(cb, CONSTANT_POOL_ARRAY_CLASS_INDEX,
ee, 1 << CONSTANT_Class);
if (exceptionOccurred(ee))
return FALSE;
class_class = (class_type == T_CLASS)
? cb->constantpool[CONSTANT_POOL_ARRAY_CLASS_INDEX].p : 0;
if (obj_flags(h) == T_CLASS) {
/* We have an array of classes. Find out the types of the elements. */
ArrayOfObject *array = (ArrayOfObject *)unhand(h);
/* uint32_t length = obj_length(h); Removed because it was crashing MSVC's cl 1.52 */
ClassClass *array_cb = (ClassClass *) array->body[obj_length(h)];
if (array_cb->name[0] == SIGNATURE_ARRAY) {
/* an Array of arrays */
item_depth =
array_cb->constantpool[CONSTANT_POOL_ARRAY_DEPTH_INDEX].i + 1;
item_type =
array_cb->constantpool[CONSTANT_POOL_ARRAY_TYPE_INDEX].i;
ResolveClassConstantFromClass(array_cb,
CONSTANT_POOL_ARRAY_CLASS_INDEX,
ee, 1 << CONSTANT_Class);
if (exceptionOccurred(ee))
return FALSE;
item_class =
array_cb->constantpool[CONSTANT_POOL_ARRAY_CLASS_INDEX].p;
} else {
/* An array of clases */
item_depth = 1;
item_type = T_CLASS;
item_class = array_cb;
}
} else {
item_depth = 1;
item_type = obj_flags(h);
item_class = 0; /* immaterial */
}
if (item_depth > class_depth) {
return (class_class == classJavaLangObject) ||
(class_class == interfaceJavaLangCloneable);
} else if (item_depth == class_depth) {
return (class_type == item_type)
&& ( (class_type != T_CLASS)
|| (class_class == item_class)
|| is_subclass_of(item_class, class_class, ee));
} else {
return FALSE;
}
return FALSE;
}
/*
* Lock around rewriting method code for quick_ instruction optimization
* We abandon locking efforts if the opcode has already been re-written.
*/
static sys_mon_t *_code_lock;
#define CODE_LOCK_INIT() monitorRegister(_code_lock, "Code rewrite lock")
#define CODE_UNLOCK() sysMonitorExit(_code_lock)
#define CODE_LOCK() { unsigned char real_opcode; \
sysMonitorEnter(_code_lock); \
real_opcode = *pc; \
REMOVE_BREAKPOINT_CONFUSION(real_opcode); \
if (opcode != real_opcode) { /* Someone else translated it */ \
CODE_UNLOCK(); \
opcode = real_opcode; \
goto again; \
} \
}
#ifdef BREAKPTS
#define WITH_OPCODE_CHANGE { \
bool_t debugging = (*pc == opc_breakpoint);
#define END_WITH_OPCODE_CHANGE \
if (debugging && (*pc != opc_breakpoint)) { \
opcode = *pc; \
*pc = opc_breakpoint; \
set_breakpoint_opcode(pc, opcode); \
goto again; } }
#define REMOVE_BREAKPOINT_CONFUSION(possible_opcode) \
if (opc_breakpoint == possible_opcode) \
possible_opcode = get_breakpoint_opcode(pc, ee); \
else \
/* a semicolon comes here */
#else
#define WITH_OPCODE_CHANGE
#define END_WITH_OPCODE_CHANGE
#define REMOVE_BREAKPOINT_CONFUSION(possible_opcode)
#endif
/* An exception has been thrown. Find the pc at which this exception
* is handled. Alternatively, rethrow the error if no appropriate pc
* is found.
*/
/* WARNING: Similar code in jit/win32/jinterf.c must be modified if the
code that follows is repaired/changed. See GetJavaExceptionHandler(). */
static unsigned char *
ProcedureFindThrowTag(ExecEnv *ee, JavaFrame *frame,
JHandle *object, unsigned char *pc)
{
if (frame->current_method == 0) {
return 0;
} else {
/* Search current class for catch frame, based on pc value,
matching specified type. */
struct methodblock *mb = frame->current_method;
ClassClass *methodClass = fieldclass(&mb->fb);
ClassClass *objectClass = obj_array_classblock(object);
struct CatchFrame *cf = mb->exception_table;
struct CatchFrame *cfEnd = cf + mb->exception_table_length;
codepos_t pcTarget = pc - mb->code; /* offset from start of code */
cp_item_type *constant_pool = frame->constant_pool;
for (; cf < cfEnd; cf++) {
if (cf->start_pc <= pcTarget && pcTarget < cf->end_pc) {
/* Is the class name a superclass of object's class. */
if (cf->catchType == 0) {
/* Special hack put in for finally. Always succeed. */
goto found;
} else {
/* The usual case */
int catchType = cf->catchType;
char *catchName =
GetClassConstantClassName(constant_pool, catchType);
ClassClass *catchClass = NULL;
ClassClass *cb;
for (cb = objectClass;; cb = unhand(cbSuperclass(cb))) {
if (strcmp(classname(cb), catchName) == 0) {
if (cbLoader(cb) == cbLoader(methodClass))
goto found;
if (catchClass == NULL) {
/* I haven't yet turned found the actual
* class corresponding to catchType, but it's
* got the right name. Find the actual class */
if (!ResolveClassConstantFromClass(
methodClass, catchType, ee,
1 << CONSTANT_Class))
return FALSE;
catchClass =
(ClassClass *)(constant_pool[catchType].p);
}
if (cb == catchClass)
goto found;
}
if (cbSuperclass(cb) == 0) {
/* Not found. Try next catch frame. */
break;
}
}
}
}
}
/* not found in this execution environment */
return 0;
found:
return mb->code + cf->handler_pc;
}
}
/* Called by opc_multianewarray. Create a filled in multidimensional array
* dimensions: The number of dimensions to fill in
* array_cb: The type of the array
* sizes: An array of the dimension sizes
*
* Return 0 if we've run out of space. Otherwise, return the allocated array.
*/
static HObject *MultiArrayAlloc2(int, ClassClass **, stack_item *, int32_t);
HObject *
MultiArrayAlloc(int dimensions, ClassClass *array_cb, stack_item *sizes)
{
/* Get the base type and depth of the array class we're constructing */
cp_item_type *constant_pool = cbConstantPool(array_cb);
int32_t depth = constant_pool[CONSTANT_POOL_ARRAY_DEPTH_INDEX].i;
int32_t base_type = constant_pool[CONSTANT_POOL_ARRAY_TYPE_INDEX].i;
ClassClass *sub_array_cb[256];
char *class_name = classname(array_cb);
struct execenv *ee = EE();
int i;
sysAssert(class_name[0] == SIGNATURE_ARRAY);
sysAssert(dimensions <= depth);
/* Fill in sub_array_cb with the array types of each of the lower.
* dimensions. Put a 0 in the slot if we've reached a non-class base type
*/
for (i = 1; i <= dimensions; i++) {
if (i < depth) {
sub_array_cb[i - 1] =
FindClassFromClass(ee, class_name + i, FALSE, array_cb);
} else if (i == depth && base_type == T_CLASS) {
ResolveClassConstantFromClass(array_cb,
CONSTANT_POOL_ARRAY_CLASS_INDEX,
ee, 1 << CONSTANT_Class);
if (exceptionOccurred(ee))
return NULL;
sub_array_cb[i - 1] =
constant_pool[CONSTANT_POOL_ARRAY_CLASS_INDEX].p;
} else {
sub_array_cb[i - 1] = 0;
}
}
/* Call a recursive function that does all the work */
return MultiArrayAlloc2(dimensions, sub_array_cb, sizes, base_type);
}
/* This function does all the actual work for MultiArrayAlloc(), after that
* function has done all the checking and initial work.
*/
static HObject *
MultiArrayAlloc2(int dimensions, ClassClass **types, stack_item *sizes,
int32_t base_type)
{
int32_t size = sizes[0].i; /* size of this dimension */
HObject *ret;
int32_t i;
if (types[0] != 0) {
ret = ArrayAlloc(T_CLASS, size); /* generate the array */
if (ret == 0)
return 0;
/* fill in the type */
unhand(((HArrayOfObject *)ret))->body[size] = (HObject*)types[0];
if (dimensions > 1) {
/* fill in the subarrays */
for (i = 0; i < size; i++) {
HObject *sub = MultiArrayAlloc2(dimensions - 1, types + 1,
sizes + 1, base_type);
if (sub == 0) /* out of memory? */
return 0;
unhand(((HArrayOfObject *)ret))->body[i] = sub;
}
} else {
/* this is the last dimension. The GC zero'd the array */
}
} else {
sysAssert(dimensions == 1);
ret = ArrayAlloc(base_type, size);
if (ret == 0) /* out of memory? */
return 0;
}
return ret;
}
/*
This routine is used to get the current time in milliseconds for
the profiling routines.
*/
long now()
{
return(sysGetMilliTicks());
}
/*
* Should a call to the specified methodblock be turned into
* an invokespecial_quick instead of a invokenonvirtual_quick?
*
* The four conditions that have to be satisfied:
* The method isn't private
* The method isn't an <init> method
* The ACC_SUPER flag is set in the current class
* The method's class is a superclass (and not equal) to the current class.
* hack: ...but ACC_SUPER is never set :-/... and it conflicts with ACC_SYNCHRONIZED :-/ 1.0.2 bug? :-/
* hack: See section 13.4.5 of the Java Lang Spec for discussion of this flag,
* and how it solves a bug, and remains backwards compatible with "older"
* compiled code
*/
bool_t
isSpecialSuperCall(ClassClass *current_class, struct methodblock *mb) {
if (((cbAccess(current_class) & ACC_SUPER) != 0)
&& (!IsPrivate(mb->fb.access))
&& (strcmp(mb->fb.name, "<init>") != 0)
&& (current_class != classJavaLangObject)) {
ClassClass *methodClass = fieldclass(&mb->fb);
ClassClass *cb = unhand(cbSuperclass(current_class));
for (; ; cb = unhand(cbSuperclass(cb))) {
if (cb == methodClass)
return TRUE;
if (cbSuperclass(cb) == 0)
return FALSE;
}
}
return FALSE;
}
/* __ExecuteJavaStart__ Search for __ExecuteJavaEnd__ find the end of the function */
/* Execute bytecodes at "pc" in the specified execution environment.
Returns TRUE if completed OK, FALSE if finished because of some
uncaught exception. */
bool_t ExecuteJava(unsigned char *initial_pc, ExecEnv *ee)
{
/* This is ugly. Some machines (and compilers) produce better code from
* positive offsets than from negative offsets. So we cache ee->optop in
* a local variable, but have it point OPTOP_OFFSET words further down the
* stack. That way, stack references will be a nonnegative index off offset
*/
/* avh: using a static buffer to print longs is unsafe */
static char longstringbuf[30];
#define OPTOP_OFFSET 4
#define S_INFO(offset) (optop[offset + OPTOP_OFFSET])
#define S_INT(offset) (S_INFO(offset).i)
#define S_FLOAT(offset) (S_INFO(offset).f)
#define S_HANDLE(offset) (S_INFO(offset).h)
#define S_OBJECT(offset) (S_INFO(offset).o)
#define S_POINTER(offset) (S_INFO(offset).p)
#define S_OSTRING(offset) (Object2CString(S_HANDLE(offset)))
#define S_LONGSTRING(offset) \
(int642CString(S_LONG(offset), longstringbuf, sizeof(longstringbuf)))
#ifdef OSF1
#define S_DOUBLE(offset) (S_INFO(offset).d)
#define S_DOUBLE2(offset) (S_INFO(offset).d)
#define S_LONG(offset) (S_INFO(offset).l)
#define S_LONG2(offset) (S_INFO(offset).l)
#define SET_S_DOUBLE(offset, value) S_INFO(offset).d = (value)
#define SET_S_LONG(offset, value) S_INFO(offset).l = (value)
#else
#define S_DOUBLE(offset) GET_DOUBLE(tdub,&(S_INFO(offset)))
#define S_DOUBLE2(offset) GET_DOUBLE(tdub2,&(S_INFO(offset)))
#define S_LONG(offset) GET_INT64(tdub,&(S_INFO(offset)))
#define S_LONG2(offset) GET_INT64(tdub2,&(S_INFO(offset)))
#define SET_S_DOUBLE(offset, value) SET_DOUBLE(tdub,&(S_INFO(offset)), (value))
#define SET_S_LONG(offset, value) SET_INT64(tdub,&(S_INFO(offset)), (value))
#endif
#define REAL_OPTOP() (&S_INFO(0))
#ifdef DEBUG
#define DECACHE_OPTOP() frame->optop = REAL_OPTOP(); ee->debugFlags &= ~EE_DEBUG_OPTOP_CACHED;
#define CACHE_OPTOP() optop = frame->optop - OPTOP_OFFSET; ee->debugFlags |= EE_DEBUG_OPTOP_CACHED;
#else
#define DECACHE_OPTOP() (frame->optop = REAL_OPTOP())
#define CACHE_OPTOP() (optop = frame->optop - OPTOP_OFFSET)
#endif
#define SIZE_AND_STACK(size, stack) pc += size; optop += stack; continue
#define SIZE_AND_STACK_BUT(size, stack) pc += size; optop += stack;
#define JUMP_AND_STACK(offset, stack) \
if (1) { pc += offset; optop += stack; \
if (offset > 0) continue; \
goto check_for_exception; } else
/*
#define pc2signedshort(pc) ((((signed char *)(pc))[1] << 8) | pc[2])
#define pc2signedlong(pc) ((((signed char *)(pc))[1] << 24) | (pc[2] << 16) \
| (pc[3] << 8) | (pc[4]))
*/
#define pc2signedshort(pc) ((int16_t)((SSCAST(UCAT(pc,1)) << 8) | UCAT(pc,2)))
#define pc2signedlong(pc)\
((int32_t)(\
(SLCAST(UCAT(pc,1)) << 24)\
| (SLCAST(UCAT(pc,2)) << 16)\
| (SLCAST(UCAT(pc,3)) << 8)\
| SLCAST(UCAT(pc,4)) \
))
/* I can't use the do { } while(0) trick because the compiler complains about
* the test not bein reachable. So I'm forced to use an uglier trick.
*/
#undef ERROR /* Avoid conflict on PC */
#ifdef BREAKPTS
#define ERROR(name, data) \
if (1) { frame->lastpc = pc; \
DECACHE_OPTOP(); \
SignalError(ee, JAVAPKG name, data); \
notify_debugger_of_exception(pc, ee, ee->exception.exc); \
goto handle_exception; } else
#else
#define ERROR(name, data) \
if (1) { frame->lastpc = pc; \
DECACHE_OPTOP(); \
SignalError(ee, JAVAPKG name, data); \
goto handle_exception; } else
#endif
register unsigned char *pc = initial_pc;
register JavaFrame *frame = ee->current_frame;
JavaFrame *initial_frame = frame;
register stack_item *optop;
register stack_item *vars = frame->vars;
unsigned char opcode;
cp_item_type *constant_pool = frame->constant_pool;
int args_size;
/* Declarations for commonly used temporary variables. Note that
some implementations will need to clear these explicitly in order
to ensure better garbage collection. */
struct fieldblock *fb;
struct methodblock *mb;
#if defined(XP_MAC) || (defined(XP_PC) && !defined(_WIN32))
struct methodblock **mbt;
#endif
stack_item *location;
ClassClass *cb, *array_cb;
JHandle *o, *h;
OBJECT *d;
ClassClass *current_class;
ClassClass *superClass;
HObject *retO;
JHandle *retH;
HArrayOfChar *arr;
char *signature;
HArrayOfObject *retArray;
char isig;
unsigned int offset;
int delta;
Java8 tdub, tdub2;
/*
* Never enter ExecuteJava(...) if the previous ee->optop has not been
* DECACHED(). If this happens the GC will not properly scan the
* previous java frame for roots...
*/
sysAssert( !(ee->debugFlags & EE_DEBUG_OPTOP_CACHED) );
/*
* It is necessary for proper java stack scavenging during GC that the
* EE passed into ExecuteJava(...) is the SAME as the thread's EE...
*
* Otherwise, any java stack frames will NOT be scanned for GC roots
*/
sysAssert( (ExecEnv *) THREAD(threadSelf())->eetop == ee );
CACHE_OPTOP();
if (!threadCheckStack()) {
/*
* Got a stack overflow: have to be careful to not do anything
* that writes to the stack. "StackOverflowError" is a
* special kind of exception, because we can't call SignalError().
*/
frame->lastpc = pc;
ee->exceptionKind = EXCKIND_STKOVRFLW;
#ifdef DEBUG
ee->debugFlags &= ~EE_DEBUG_OPTOP_CACHED;
#endif
return FALSE;
}
#ifdef TRACING
{
extern PRLogModule MethodLog;
if (PR_LOG_TEST(Method, out)) {
/* If netscape trace log messages are turned on,
we'd better turn tracing on too: */
tracem = 1;
}
}
if (trace) {
char where_am_i[100];
pc2string(pc, frame->current_method, where_am_i, where_am_i + sizeof where_am_i);
fprintf(stdout, "Entering %s\n", where_am_i);
fflush(stdout);
}
#endif /* TRACING */
while (1) {
sysAssert(REAL_OPTOP() >= frame->ostack);
sysAssert(!frame->current_method ||
REAL_OPTOP() <= frame->ostack + frame->current_method->maxstack);
#ifdef JAVA_PREEMPT_USING_COUNT
if (ee->opcodeCount++ >= JAVA_YIELD_COUNT) {
if (!exceptionOccurred(ee)) {
ee->opcodeCount = 0;
ee->exceptionKind = EXCKIND_YIELD;
goto check_for_exception;
} else {
sysAssert(0);
}
}
#endif
#ifdef DBINFO
frame->lastpc = pc;
#ifdef BREAKPTS
if (single_stepping > 0 &&
unhand((HThread *)(ee->thread))->single_step &&
frame->current_method != NULL) {
DECACHE_OPTOP();
notify_debugger(threadSelf(), pc, NULL, NULL);
}
#endif
#endif
opcode = *pc;
again:
#ifdef STATISTICS
instruction_count[opcode]++;
#endif
switch (opcode) {
/* NOPs. Don't do anything. */
case opc_nop:
TRACE(("\tnop\n"));
SIZE_AND_STACK(1, 0);
case opc_aload:
case opc_iload:
case opc_fload:
S_INFO(0) = vars[pc[1]];
TRACEIF(opc_iload, ("\tiload r%d => %ld\n", pc[1], S_INT(0)));
TRACEIF(opc_aload, ("\taload r%d => %s\n", pc[1], S_OSTRING(0)));
TRACEIF(opc_fload, ("\tfload r%d => %g\n", pc[1], S_FLOAT(0)));
SIZE_AND_STACK(2, 1);
case opc_lload:
case opc_dload: {
int reg = pc[1];
S_INFO(0) = vars[reg];
S_INFO(1) = vars[reg + 1];
TRACEIF(opc_lload, ("\tlload r%d => %s\n", reg, S_LONGSTRING(0)));
TRACEIF(opc_dload, ("\tdload r%d => %g\n", reg, S_DOUBLE(0)));
SIZE_AND_STACK(2, 2);
}
#define OPC_DO_LOAD_n(num) \
case opc_iload_##num: \
case opc_aload_##num: \
case opc_fload_##num: \
S_INFO(0) = vars[num]; \
TRACEIF(opc_iload_##num, \
("\t%s => %ld\n", opnames[opcode], S_INT(0))); \
TRACEIF(opc_aload_##num, \
("\t%s => %s\n", opnames[opcode], S_OSTRING(0))); \
TRACEIF(opc_fload_##num, \
("\t%s => %g\n", opnames[opcode], S_FLOAT(0))); \
SIZE_AND_STACK(1, 1); \
case opc_lload_##num: \
case opc_dload_##num: \
S_INFO(0) = vars[num]; \
S_INFO(1) = vars[num + 1]; \
TRACEIF(opc_lload_##num, \
("\t%s => %s\n", opnames[opcode], S_LONGSTRING(0))); \
TRACEIF(opc_dload_##num, \
("\t%s => %g\n", opnames[opcode], S_DOUBLE(0))); \
SIZE_AND_STACK(1, 2);
OPC_DO_LOAD_n(0)
OPC_DO_LOAD_n(1)
OPC_DO_LOAD_n(2)
OPC_DO_LOAD_n(3)
/* store a local variable onto the stack. */
case opc_istore:
case opc_astore:
case opc_fstore:
vars[pc[1]] = S_INFO(-1);
TRACEIF(opc_istore,("\tistore %ld => r%d\n", S_INT(-1), pc[1]));
TRACEIF(opc_astore,("\tastore %s => r%d\n", S_OSTRING(-1),pc[1]));
TRACEIF(opc_fstore,("\tfstore %g => r%d\n", S_FLOAT(-1), pc[1]));
SIZE_AND_STACK(2, -1);
case opc_lstore:
case opc_dstore: {
int reg = pc[1];
vars[reg] = S_INFO(-2);
vars[reg + 1] = S_INFO(-1);
TRACEIF(opc_lstore, ("\tlstore %s => r%d\n", S_LONGSTRING(-2),reg));
TRACEIF(opc_dstore, ("\tdstore %g => r%d\n", S_DOUBLE(-2), reg));
SIZE_AND_STACK(2, -2);
}
#define OPC_DO_STORE_n(num) \
case opc_istore_##num: \
case opc_astore_##num: \
case opc_fstore_##num: \
vars[num] = S_INFO(-1); \
TRACEIF(opc_istore_##num, ("\t%s %ld => r%d\n", \
opnames[opcode], S_INT(-1), num)); \
TRACEIF(opc_astore_##num, ("\t%s %d => r%d\n", \
opnames[opcode], S_OSTRING(-1), num)); \
TRACEIF(opc_fstore_##num, ("\t%s %g => r%d\n", \
opnames[opcode], S_FLOAT(-1), num)); \
SIZE_AND_STACK(1, -1); \
case opc_lstore_##num: \
case opc_dstore_##num: \
vars[num] = S_INFO(-2); \
vars[num + 1] = S_INFO(-1); \
TRACEIF(opc_lstore_##num, ("\t%s %s => r%d\n", \
opnames[opcode], S_LONGSTRING(-2), num)); \
TRACEIF(opc_dstore_##num, ("\t%s %g => r%d\n", \
opnames[opcode], S_DOUBLE(-2), num)); \
SIZE_AND_STACK(1, -2);
OPC_DO_STORE_n(0)
OPC_DO_STORE_n(1)
OPC_DO_STORE_n(2)
OPC_DO_STORE_n(3)
/* Return a value from a function */
case opc_areturn:
case opc_ireturn:
case opc_freturn:
frame->prev->optop[0] = S_INFO(-1);
frame->prev->optop++;
TRACEIF(opc_ireturn, ("\tireturn %ld\n", S_INT(-1)));
TRACEIF(opc_areturn, ("\tareturn %s\n", S_OSTRING(-1)));
TRACEIF(opc_freturn, ("\tfreturn %g\n", S_FLOAT(-1)));
goto finish_return;
case opc_lreturn:
case opc_dreturn:
frame->prev->optop[0] = S_INFO(-2);
frame->prev->optop[1] = S_INFO(-1);
frame->prev->optop += 2;
TRACEIF(opc_lreturn, ("\tlreturn %s\n", S_LONGSTRING(-2)));
TRACEIF(opc_dreturn, ("\tdreturn %g\n", S_DOUBLE(-2)));
goto finish_return;
/* Return from a function with no value */
case opc_return:
TRACE(("\treturn\n"));
goto finish_return;
case opc_i2f: /* Convert top of stack int to float */
S_FLOAT(-1) = (float) S_INT(-1);
TRACE(("\ti2f => %g\n", S_FLOAT(-1)));
SIZE_AND_STACK(1, 0);
case opc_i2l: /* Convert top of stack int to long */
SET_S_LONG(-1, int2ll(S_INT(-1)));
TRACE(("\ti2l => %s\n", S_LONGSTRING(-1)));
SIZE_AND_STACK(1, 1);
case opc_i2d:
SET_S_DOUBLE(-1, S_INT(-1));
TRACE(("\ti2d => %g\n", S_DOUBLE(-1)));
SIZE_AND_STACK(1, 1);
case opc_f2i: /* Convert top of stack float to int */
S_INT(-1) = (int32_t) S_FLOAT(-1);
TRACE(("\tf2i => %ld\n", S_INT(-1)));
SIZE_AND_STACK(1, 0);
case opc_f2l: /* convert top of stack float to long */
SET_S_LONG(-1, float2ll(S_FLOAT(-1)));
TRACE(("\tf2l => %s\n", S_LONGSTRING(-1)));
SIZE_AND_STACK(1, 1);
case opc_f2d: /* convert top of stack float to double */
SET_S_DOUBLE(-1, S_FLOAT(-1));
TRACE(("\tf2d => %g\n", S_DOUBLE(-1)));
SIZE_AND_STACK(1, 1);
case opc_l2i: /* convert top of stack long to int */
S_INT(-2) = ll2int(S_LONG(-2));
TRACE(("\tl2i => %ld\n", S_INT(-2)));
SIZE_AND_STACK(1, -1);
case opc_l2f: /* convert top of stack long to float */
S_FLOAT(-2) = ll2float(S_LONG(-2));
HACK_NOP();
TRACE(("\tl2f => %g\n", S_FLOAT(-2)));
SIZE_AND_STACK(1, -1);
case opc_l2d: /* convert top of stack long to double */
SET_S_DOUBLE(-2, ll2double(S_LONG(-2)));
HACK_NOP();
TRACE(("\tl2d => %g\n", S_DOUBLE(-2)));
SIZE_AND_STACK(1, 0);
case opc_d2i:
S_INT(-2) = (int32_t) S_DOUBLE(-2);
TRACE(("\td2i => %ld\n", S_INT(-2)));
SIZE_AND_STACK(1, -1);
case opc_d2f:
S_FLOAT(-2) = (float) S_DOUBLE(-2);
HACK_NOP();
TRACE(("\td2f => %g\n", S_FLOAT(-2)));
SIZE_AND_STACK(1, -1);
case opc_d2l:
SET_S_LONG(-2, double2ll(S_DOUBLE(-2)));
TRACE(("\td2l => %s\n", S_LONGSTRING(-2)));
SIZE_AND_STACK(1, 0);
case opc_int2byte:
S_INT(-1) = (signed char) S_INT(-1);
TRACE(("\tint2byte => %ld\n", S_INT(-1)));
SIZE_AND_STACK(1, 0);
case opc_int2char:
S_INT(-1) &= 0xFFFF;
TRACE(("\tint2char => %ld\n", S_INT(-1)));
SIZE_AND_STACK(1, 0);
case opc_int2short:
S_INT(-1) = (signed short) S_INT(-1);
TRACE(("\tint2short => %ld\n", S_INT(-1)));
SIZE_AND_STACK(1, 0);
case opc_fcmpl:
case opc_fcmpg: {
float l = S_FLOAT(-2);
float r = S_FLOAT(-1);
int value = (l < r) ? -1 : (l > r) ? 1 : (l == r ) ? 0
/* Beyond here, must be NaN. */
: (opcode == opc_fcmpl) ? -1
: (opcode == opc_fcmpg) ? 1
: 0;
S_INT(-2) = value;
TRACE(("\t%s => %ld\n", opnames[opcode], S_INT(-2)));
SIZE_AND_STACK(1, -1);
}
case opc_lcmp: {
int64_t l = S_LONG(-4);
int64_t r = S_LONG(-2);
S_INT(-4) = (ll_lt(l, r)) ? -1 : (ll_gt(l, r)) ? 1 : 0;
TRACE(("\tlcmp => %ld\n", S_INT(-4)));
SIZE_AND_STACK(1, -3);
}
case opc_dcmpl:
case opc_dcmpg: {
double l = S_DOUBLE(-4);
double r = S_DOUBLE(-2);
int value = (l < r) ? -1 : (l > r) ? 1 : (l == r ) ? 0
/* Beyond here, must be NaN. */
: (opcode == opc_dcmpl) ? -1
: (opcode == opc_dcmpg) ? 1
: 0;
S_INT(-4) = value;
TRACE(("\t%s => %ld\n", opnames[opcode], S_INT(-4)));
SIZE_AND_STACK(1, -3);
}
/* Push a 1-byte signed integer value onto the stack. */
case opc_bipush:
S_INT(0) = (signed char)(pc[1]);
TRACE(("\tbipush %ld\n", S_INT(0)));
SIZE_AND_STACK(2, 1);
/* Push a 2-byte signed integer constant onto the stack. */
case opc_sipush:
S_INT(0) = pc2signedshort(pc);
TRACE(("\tsipush %ld\n", S_INT(0)));
SIZE_AND_STACK(3, 1);
case opc_ldc:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE
ResolveClassConstant(constant_pool, pc[1], ee,
(1 << CONSTANT_Integer) |
(1 << CONSTANT_Float) |
(1 << CONSTANT_String));
if (!exceptionOccurred(ee))
pc[0] = opc_ldc_quick;
END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_ldc_quick:
S_POINTER(0) = constant_pool[pc[1]].p;
TRACE(("\tldc1 #%d => 0x%lx\n", pc[1], S_INT(0)));
SIZE_AND_STACK(2, 1);
case opc_ldc_w:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE
ResolveClassConstant(constant_pool, GET_INDEX(pc + 1), ee,
(1 << CONSTANT_Integer) |
(1 << CONSTANT_Float) |
(1 << CONSTANT_String));
if (!exceptionOccurred(ee))
pc[0] = opc_ldc_w_quick;
END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_ldc_w_quick:
S_POINTER(0) = constant_pool[GET_INDEX(pc + 1)].p;
TRACE(("\tldc2 #%ld => 0x%lx\n", GET_INDEX(pc + 1), S_INT(0)));
SIZE_AND_STACK(3, 1);
case opc_ldc2_w: /* load a two word constant */
DECACHE_OPTOP();
WITH_OPCODE_CHANGE
ResolveClassConstant(constant_pool, GET_INDEX(pc + 1), ee,
(1 << CONSTANT_Double) | (1 << CONSTANT_Long));
if (!exceptionOccurred(ee))
pc[0] = opc_ldc2_w_quick;
END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_ldc2_w_quick:
SET_S_LONG(0, GET_INT64(tdub2, &constant_pool[GET_INDEX(pc + 1)]));
TRACE(("\tldc2 #%d => long=%s double=%g\n", GET_INDEX(pc + 1),
S_LONGSTRING(0), S_DOUBLE2(0)));
SIZE_AND_STACK(3, 2);
#define CONSTANT_OP(opcode, this_type, value) \
case opcode: \
S_INFO(0).this_type = value; \
TRACE(("\t%s\n", opnames[opcode])); \
SIZE_AND_STACK(1, 1);
/* Push miscellaneous constants onto the stack. */
CONSTANT_OP(opc_aconst_null, p, 0)
CONSTANT_OP(opc_iconst_m1, i, -1)
CONSTANT_OP(opc_iconst_0, i, 0)
CONSTANT_OP(opc_iconst_1, i, 1)
CONSTANT_OP(opc_iconst_2, i, 2)
CONSTANT_OP(opc_iconst_3, i, 3)
CONSTANT_OP(opc_iconst_4, i, 4)
CONSTANT_OP(opc_iconst_5, i, 5)
CONSTANT_OP(opc_fconst_0, f, (float)0.0)
CONSTANT_OP(opc_fconst_1, f, (float)1.0)
CONSTANT_OP(opc_fconst_2, f, (float)2.0)
#if GENERATING68K
default:
switch (opcode) {
#endif
case opc_dconst_0:
SET_S_DOUBLE(0, 0.0);
TRACE(("\t%s\n", opnames[opcode]));
SIZE_AND_STACK(1, 2);
case opc_dconst_1:
SET_S_DOUBLE(0, 1.0);
TRACE(("\t%s\n", opnames[opcode]));
SIZE_AND_STACK(1, 2);
case opc_lconst_0:
SET_S_LONG(0, ll_zero_const);
TRACE(("\t%s\n", opnames[opcode]));
SIZE_AND_STACK(1, 2);
case opc_lconst_1:
SET_S_LONG(0, ll_one_const);
TRACE(("\t%s\n", opnames[opcode]));
SIZE_AND_STACK(1, 2);
#define BINARY_INT_OP(name, op, test) \
case opc_i##name: \
if (test && S_INT(-1) == 0) \
ERROR("ArithmeticException", "/ by zero"); \
S_INT(-2) = S_INT(-2) op S_INT(-1); \
TRACE(("\t%s => %ld\n", opnames[opcode], S_INT(-2))); \
SIZE_AND_STACK(1, -1); \
case opc_l##name: \
if (test && ll_eqz(S_LONG(-2))) \
ERROR("ArithmeticException", "/ by zero"); \
SET_S_LONG(-4, ll_##name(S_LONG(-4), S_LONG2(-2))); \
TRACE(("\t%s => %s\n", opnames[opcode], S_LONGSTRING(-4))); \
SIZE_AND_STACK(1, -2);
/* Perform various binary integer operations */
BINARY_INT_OP(add, +, 0)
BINARY_INT_OP(sub, -, 0)
BINARY_INT_OP(mul, *, 0)
BINARY_INT_OP(and, &, 0)
BINARY_INT_OP(or, |, 0)
BINARY_INT_OP(xor, ^, 0)
BINARY_INT_OP(div, /, 1)
BINARY_INT_OP(rem, %, 1)
#define BINARY_SHIFT_OP(name, op) \
case opc_i##name: \
S_INT(-2) = ((int32_t)(S_INT(-2))) op (S_INT(-1) & 0x1F); \
TRACE(("\t%s => %ld\n", opnames[opcode], S_INT(-2))); \
SIZE_AND_STACK(1, -1); \
case opc_l##name: \
SET_S_LONG(-3, ll_##name(S_LONG(-3), (S_INT(-1) & 0x3F))); \
TRACE(("\t%s => %s\n", opnames[opcode], S_LONGSTRING(-3))); \
SIZE_AND_STACK(1, -1); \
BINARY_SHIFT_OP(shl, <<)
BINARY_SHIFT_OP(shr, >>)
case opc_iushr:
S_INT(-2) = ((uint32_t)(S_INT(-2))) >> (S_INT(-1) & 0x1F);
TRACE(("\t%s => %ld\n", opnames[opcode], S_INT(-2)));
SIZE_AND_STACK(1, -1);
case opc_lushr:
SET_S_LONG(-3, ll_ushr(S_LONG(-3), (S_INT(-1) & 0x3F)));
TRACE(("\t%s => %s\n", opnames[opcode], S_LONGSTRING(-3)));
SIZE_AND_STACK(1, -1);
/* Unary negation */
case opc_ineg:
S_INT(-1) = -S_INT(-1);
TRACE(("\tineg => %ld\n", S_INT(-1)));
SIZE_AND_STACK(1, 0);
case opc_lneg:
SET_S_LONG(-2, ll_neg(S_LONG(-2)));
TRACE(("\tlneg => %ld\n", S_LONG(-2)));
SIZE_AND_STACK(1, 0);
/* Add a small constant to a local variable */
case opc_iinc:
vars[pc[1]].i += ((signed char *) pc)[2];
TRACE(("\tiinc r%d+%d => %ld\n",
pc[1], ((signed char *) pc)[2], vars[pc[1]].i));
SIZE_AND_STACK(3, 0);
#define BINARY_FLOAT_OP(name, op) \
case opc_f##name: \
S_FLOAT(-2) = S_FLOAT(-2) op S_FLOAT(-1); \
HACK_NOP(); \
TRACE(("\t%s => %g\n", opnames[opcode], S_FLOAT(-2))); \
SIZE_AND_STACK(1, -1); \
case opc_d##name: \
SET_S_DOUBLE(-4, S_DOUBLE(-4) op S_DOUBLE2(-2)); \
TRACE(("\t%s => %g\n", opnames[opcode], S_DOUBLE(-4))); \
SIZE_AND_STACK(1, -2);
BINARY_FLOAT_OP(add, +)
BINARY_FLOAT_OP(sub, -)
BINARY_FLOAT_OP(mul, *)
BINARY_FLOAT_OP(div, /)
case opc_frem:
S_FLOAT(-2) = (float) DREM(S_FLOAT(-2), S_FLOAT(-1));
TRACE(("\t%s => %g\n", opnames[opcode], S_FLOAT(-2)));
SIZE_AND_STACK(1, -1);
case opc_drem:
SET_S_DOUBLE(-4, DREM(S_DOUBLE(-4), S_DOUBLE2(-2)));
TRACE(("\t%s => %g\n", opnames[opcode], S_DOUBLE(-4)));
SIZE_AND_STACK(1, -2);
case opc_fneg:
S_FLOAT(-1) = -S_FLOAT(-1);
TRACE(("\tfneg => %g\n", S_FLOAT(-1)));
SIZE_AND_STACK(1, 0);
case opc_dneg:
SET_S_DOUBLE(-2, -S_DOUBLE(-2));
HACK_NOP();
TRACE(("\tdneg => %g\n", S_DOUBLE(-2)));
SIZE_AND_STACK(1, 0);
/* Unconditional goto. Maybe save return address in register. */
case opc_jsr:
S_INFO(0).addr = pc + 3;
optop++;
/* FALL THROUGH */
case opc_goto: {
int skip = pc2signedshort(pc);
TRACE(("\t%s %8X [.+(%d)]\n", opnames[opcode], pc + skip, skip));
JUMP_AND_STACK(skip, 0);
}
case opc_jsr_w:
S_INFO(0).addr = pc + 5;
optop++;
/* FALL THROUGH */
case opc_goto_w: {
int32_t skip = pc2signedlong(pc);
TRACE(("\t%s %8X\n", opnames[opcode], pc + skip));
JUMP_AND_STACK(skip, 0);
}
/* Return from subroutine. June to address in register. */
case opc_ret:
TRACE(("\tret %d (%8X)\n", pc[1], vars[pc[1]].addr));
pc = vars[pc[1]].addr;
SIZE_AND_STACK_BUT(0, 0);
goto check_for_exception;
#define COMPARISON_OP(name, comparison) \
case opc_if_icmp##name: { \
int skip = (S_INT(-2) comparison S_INT(-1)) \
? pc2signedshort(pc) : 3; \
TRACE(("\t%s goto %8lX (%staken)\n", \
opnames[opcode], \
(uint32_t)(pc + pc2signedshort(pc)), \
(S_INT(-2) comparison S_INT(-1)) ? "" : "not ")); \
JUMP_AND_STACK(skip, -2); \
} \
case opc_if##name: { \
int skip = (S_INT(-1) comparison 0) ? pc2signedshort(pc) : 3; \
TRACE(("\t%s goto %8lX (%staken) [.+(%d)]\n", \
opnames[opcode], \
(uint32_t)(pc + pc2signedshort(pc)), \
(S_INT(-1) comparison 0) ? "" : "not ", skip)); \
JUMP_AND_STACK(skip, -1); \
} \
#define COMPARISON_OP2(name, nullname, comparison) \
COMPARISON_OP(name, comparison) \
case opc_if_acmp##name: { \
int skip = (S_POINTER(-2) comparison S_POINTER(-1)) \
? pc2signedshort(pc) : 3; \
TRACE(("\t%s goto %8lX (%staken)\n", \
opnames[opcode], \
(uint32_t)(pc + pc2signedshort(pc)), \
(S_POINTER(-2) comparison S_POINTER(-1)) ? "" : "not ")); \
JUMP_AND_STACK(skip, -2); \
} \
case opc_if##nullname: { \
int skip = (S_POINTER(-1) comparison NULL) ? pc2signedshort(pc) : 3; \
TRACE(("\t%s goto %8lX (%staken)\n", \
opnames[opcode], \
(uint32_t) (pc + pc2signedshort(pc)), \
(S_INT(-1) comparison 0) ? "" : "not ")); \
JUMP_AND_STACK(skip, -1); \
} \
COMPARISON_OP2(eq, null, ==) /* also generate acmp_eq and acmp_ne */
COMPARISON_OP2(ne, nonnull, !=)
COMPARISON_OP(lt, <)
COMPARISON_OP(gt, >)
COMPARISON_OP(le, <=)
COMPARISON_OP(ge, >=)
/* Discard the top item on the stack */
case opc_pop:
TRACE(("\tpop\n"));
SIZE_AND_STACK(1, -1);
/* Discard the top item on the stack */
case opc_pop2:
TRACE(("\tpop2\n"));
SIZE_AND_STACK(1, -2);
/* Duplicate the top item on the stack */
case opc_dup:
S_INFO(0) = S_INFO(-1);
TRACE(("\tdup\n"));
SIZE_AND_STACK(1, 1);
case opc_dup2: /* Duplicate the top two items on the stack */
S_INFO(0) = S_INFO(-2);
S_INFO(1) = S_INFO(-1);
TRACE(("\tdup2\n"));
SIZE_AND_STACK(1, 2);
case opc_dup_x1: /* insert S_INFO(-1) on stack past 1 */
S_INFO(0) = S_INFO(-1);
S_INFO(-1) = S_INFO(-2);
S_INFO(-2) = S_INFO(0);
TRACE(("\tdup_x1\n"));
SIZE_AND_STACK(1, 1);
case opc_dup_x2: /* insert S_INFO(-1) on stack past 2*/
S_INFO(0) = S_INFO(-1);
S_INFO(-1) = S_INFO(-2);
S_INFO(-2) = S_INFO(-3);
S_INFO(-3) = S_INFO(0);
TRACE(("\tdup_x2\n"));
SIZE_AND_STACK(1, 1);
case opc_dup2_x1: /* insert S_INFO(-1,-2) on stack past 1 */
S_INFO(1) = S_INFO(-1);
S_INFO(0) = S_INFO(-2);
S_INFO(-1) = S_INFO(-3);
S_INFO(-2) = S_INFO(1);
S_INFO(-3) = S_INFO(0);
TRACE(("\tdup2_x1\n"));
SIZE_AND_STACK(1, 2);
case opc_dup2_x2: /* insert S_INFO(-1,2) on stack past 2 */
S_INFO(1) = S_INFO(-1);
S_INFO(0) = S_INFO(-2);
S_INFO(-1) = S_INFO(-3);
S_INFO(-2) = S_INFO(-4);
S_INFO(-3) = S_INFO(1);
S_INFO(-4) = S_INFO(0);
TRACE(("\tdup2_x2\n"));
SIZE_AND_STACK(1, 2);
case opc_swap: { /* swap top two elements on the stack */
stack_item j;
j = S_INFO(-1);
S_INFO(-1) = S_INFO(-2);
S_INFO(-2) = j;
SIZE_AND_STACK(1, 0);
}
/* Find the length of an array */
case opc_arraylength: {
arr = (HArrayOfChar *)S_HANDLE(-1);
if (arr == 0)
ERROR("NullPointerException", "trying to get array length");
S_INT(-1) = obj_length(arr);
TRACE(("\tarraylength %s => %ld\n",
Object2CString((JHandle*)arr), S_INT(-1)));
SIZE_AND_STACK(1, 0);
}
/* Goto one of a sequence of targets. */
case opc_tableswitch: {
unsigned char *method_base = ee->current_frame->current_method->code;
int32_t *lpc = (int32_t *) REL_ALIGN(method_base, pc-method_base+1); /* UCALIGN(pc + 1); */
int32_t key = S_INT(-1);
int32_t low = ntohl(lpc[1]);
int32_t high = ntohl(lpc[2]);
int32_t skip;
key -= low;
skip = ((key > high - low) || (key < 0))
? ntohl(lpc[0])
: ntohl(lpc[key + 3]);
TRACE(("\ttableswitch %ld [%ld-%ld] .+(%ld)\n", key+low, low, high, skip));
JUMP_AND_STACK(skip, -1);
}
/* Goto one of a sequence of targets */
case opc_lookupswitch: {
unsigned char *method_base = ee->current_frame->current_method->code;
int32_t *lpc = (int32_t *) REL_ALIGN(method_base, pc-method_base+1); /* UCALIGN(pc + 1); */
int32_t key = S_INT(-1);
int32_t skip = ntohl(lpc[0]); /* default skip amount */
int32_t npairs = ntohl(lpc[1]);
TRACE(("\tlookupswitch %ld\n", key));
while (--npairs >= 0) {
lpc += 2;
if (key == (int32_t) ntohl(lpc[0])) {
skip = ntohl(lpc[1]);
break;
}
}
JUMP_AND_STACK(skip, -1);
}
/* Throw to a given value */
case opc_athrow: {
o = S_HANDLE(-1);
TRACE(("\tathrow => %s\n", Object2CString(o)));
if (o == 0) {
ERROR("NullPointerException", "trying to throw");
} else {
exceptionThrow(ee, o);
#ifdef BREAKPTS
notify_debugger_of_exception(pc, ee, ee->exception.exc);
#endif
goto handle_exception;
}
}
case opc_getfield:
case opc_putfield:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE {
cp_index_type field_index;
CODE_LOCK();
field_index = GET_INDEX(pc+1);
CODE_UNLOCK();
ResolveClassConstant(constant_pool, field_index, ee,
1 << CONSTANT_Fieldref);
fb = (struct fieldblock *)constant_pool[field_index].p;
if (!exceptionOccurred(ee)) {
signature = fieldsig(fb);
TRACE(("\t%s %s.%s\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb)));
if (fb->access & ACC_STATIC) {
char buf[256];
int len;
classname2string(classname(fieldclass(fb)),
buf, sizeof(buf));
len = strlen(buf);
/* If buffer overflow, quietly truncate */
(void) jio_snprintf(buf + len, sizeof(buf)-len,
": field %s did not used to be static",
fb->name);
ERROR("IncompatibleClassChangeError", buf);
} else if ((fb->access & ACC_FINAL)
&& (opcode == opc_putfield)
&& ((ee->current_frame->current_method == NULL)
||
(fieldclass(fb) !=
fieldclass(&ee->current_frame->current_method->fb)))) {
char buf[256];
int len;
classname2string(classname(fieldclass(fb)), buf, sizeof(buf));
len = strlen(buf);
/* If buffer overflow, quietly truncate */
(void) jio_snprintf(buf + len, sizeof(buf)-len,
": field %s is final", fb->name);
ERROR("IllegalAccessError", buf);
} else {
offset = fb->u.offset / sizeof (OBJECT);
if (offset <= 0xFF && !UseLosslessQuickOpcodes) {
delta = (signature[0] == SIGNATURE_LONG
|| signature[0] == SIGNATURE_DOUBLE)
? (opc_getfield2_quick - opc_getfield)
: (opc_getfield_quick - opc_getfield);
CODE_LOCK();
pc[1] = offset;
pc[0] = opcode + delta;
CODE_UNLOCK();
} else {
delta = (opc_getfield_quick_w - opc_getfield);
pc[0] = opcode + delta;
}
}
}
} END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_getstatic:
case opc_putstatic:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE {
cp_index_type field_index = GET_INDEX(pc+1);
ResolveClassConstant(constant_pool, field_index, ee,
1 << CONSTANT_Fieldref);
fb = (struct fieldblock *)(constant_pool[field_index].p);
if (!exceptionOccurred(ee)) {
TRACE(("\t%s %s.%s\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb)));
if ((fb->access & ACC_STATIC) == 0) {
char buf[256];
int len;
classname2string(classname(fieldclass(fb)),
buf, sizeof(buf));
len = strlen(buf);
/* If buffer overflow, quietly truncate */
(void) jio_snprintf(buf + len, sizeof(buf)-len,
": field %s used to be static",
fb->name);
ERROR("IncompatibleClassChangeError", buf);
} else if ((fb->access & ACC_FINAL)
&& (opcode == opc_putstatic)
&& ((ee->current_frame->current_method == NULL)
||
(fieldclass(fb) !=
fieldclass(&ee->current_frame->current_method->fb)))) {
char buf[256];
int len;
classname2string(classname(fieldclass(fb)), buf, sizeof(buf));
len = strlen(buf);
/* If buffer overflow, quietly truncate */
(void) jio_snprintf(buf + len, sizeof(buf)-len,
": field %s is final", fb->name);
ERROR("IllegalAccessError", buf);
} else {
signature = fieldsig(fb);
delta = (signature[0] == SIGNATURE_LONG
|| signature[0] == SIGNATURE_DOUBLE)
? (opc_getstatic2_quick - opc_getstatic)
: (opc_getstatic_quick - opc_getstatic);
pc[0] = opcode + delta;
}
}
} END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_invokevirtual:
case opc_invokenonvirtual:
case opc_invokestatic:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE {
cp_index_type method_index;
CODE_LOCK();
method_index = GET_INDEX(pc+1);
CODE_UNLOCK();
ResolveClassConstant(constant_pool, method_index, ee,
1 << CONSTANT_Methodref);
if (!exceptionOccurred(ee)) {
mb = constant_pool[method_index].p;
TRACE(("\t%s %s.%s%s\n", opnames[opcode],
classname(fieldclass(&mb->fb)),
fieldname(&mb->fb), fieldsig(&mb->fb)));
if (opcode == opc_invokestatic) {
if ((mb->fb.access & ACC_STATIC) == 0) {
char buf[256];
int len;
classname2string(classname(fieldclass(&mb->fb)),
buf, sizeof(buf));
len = strlen(buf);
/* If buffer overflow, quietly truncate */
(void) jio_snprintf(buf + len, sizeof(buf)-len,
": method %s%s used to be static",
mb->fb.name, mb->fb.signature);
ERROR("IncompatibleClassChangeError", buf);
} else {
pc[0] = opc_invokestatic_quick;
}
} else {
if (mb->fb.access & ACC_STATIC) {
char buf[256];
int len;
classname2string(classname(fieldclass(&mb->fb)),
buf, sizeof(buf));
len = strlen(buf);
/* If buffer overflow, quietly truncate */
(void) jio_snprintf(buf + len, sizeof(buf)-len,
": method %s%s did not used to be static",
mb->fb.name, mb->fb.signature);
ERROR("IncompatibleClassChangeError", buf);
}
}
if (opcode == opc_invokevirtual) {
offset = mb->fb.u.offset;
if (offset <= 0xFF && !UseLosslessQuickOpcodes) {
/* almost always */
CODE_LOCK();
pc[1] = (unsigned char) mb->fb.u.offset;
pc[2] = (unsigned char) mb->args_size;
pc[0] = fieldclass(&mb->fb) == classJavaLangObject
? opc_invokevirtualobject_quick
: opc_invokevirtual_quick;
CODE_UNLOCK();
} else {
pc[0] = opc_invokevirtual_quick_w;
}
} else if (opcode == opc_invokenonvirtual) {
current_class = frame->current_method ?
fieldclass(&frame->current_method->fb) : NULL;
if ((current_class != NULL) &&
isSpecialSuperCall(current_class, mb)) {
fb_offset_t offset = mb->fb.u.offset;
struct methodblock *new_mb;
superClass =
unhand(cbSuperclass(current_class));
new_mb =
cbMethodTable(superClass)->methods[offset];
if (mb != new_mb) {
CODE_LOCK();
pc[1] = offset >> 8;
pc[2] = offset & 0xFF;
pc[0] = opc_invokesuper_quick;
CODE_UNLOCK();
} else
pc[0] = opc_invokenonvirtual_quick;
} else
pc[0] = opc_invokenonvirtual_quick;
}
}
} END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_invokeinterface:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE {
cp_index_type InterfaceMethodref_index;
CODE_LOCK();
InterfaceMethodref_index = GET_INDEX(pc + 1);
CODE_UNLOCK();
ResolveClassConstant(constant_pool, InterfaceMethodref_index,
ee, 1 << CONSTANT_InterfaceMethodref);
if (!exceptionOccurred(ee)) {
uint32_t InterfaceMethodref =
constant_pool[InterfaceMethodref_index].i;
cp_index_type NameAndType_index = InterfaceMethodref & 0xFFFF;
hash_t ID = constant_pool[NameAndType_index].i;
TRACE(("\t%s %s\n", opnames[opcode],
IDToNameAndTypeString(ID)));
CODE_LOCK();
pc[1] = (unsigned char) (NameAndType_index >> 8);
pc[2] = (unsigned char) (NameAndType_index & 0xFFL);
pc[4] = 0; /* no guess, yet */
pc[0] = opc_invokeinterface_quick;
CODE_UNLOCK();
}
} END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_putfield_quick: {
o = S_HANDLE(-2);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to set field at offset %ld",
(long)S_POINTER(-1));
ERROR("NullPointerException", buf);
}
TRACE(("\t%s %s.(#%ld) <= %lX\n", opnames[opcode],
Object2CString(o), pc[1], S_POINTER(-1)));
obj_setslot(o, pc[1], (long)S_POINTER(-1));
SIZE_AND_STACK(3, -2);
}
case opc_getfield_quick: {
o = S_HANDLE(-1);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to get field at offset %ld",
(long)S_POINTER(-1));
ERROR("NullPointerException", buf);
}
S_POINTER(-1) = (void *)obj_getslot(o, pc[1]);
TRACE(("\t%s %s.(#%d) <= %lX\n", opnames[opcode],
Object2CString(o), pc[1], S_POINTER(-1)));
SIZE_AND_STACK(3, 0);
}
case opc_putfield2_quick: {
o = S_HANDLE(-3);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to set field at offset %ld (long/double)",
(long)S_POINTER(-2));
ERROR("NullPointerException", buf);
}
TRACE(("\t%s %s.(#%d) <= long=%s double=%g\n", opnames[opcode],
Object2CString(o), pc[1], S_LONGSTRING(-2), S_DOUBLE2(-2)));
obj_setslot(o, pc[1], (long)S_POINTER(-2));
#ifndef OSF1
obj_setslot(o, pc[1] + 1, (long)S_POINTER(-1));
#endif
SIZE_AND_STACK(3, -3);
}
case opc_getfield2_quick: {
o = S_HANDLE(-1);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to get field at offset %ld (long/double)",
pc[1]);
ERROR("NullPointerException", buf);
}
S_POINTER(-1) = (void *)obj_getslot(o, pc[1]);
#ifdef OSF1
S_POINTER(0) = 0;
#else
S_POINTER(0) = (void *)obj_getslot(o, pc[1] + 1);
#endif
TRACE(("\t%s %s.(#%d) <= long=%s double=%g\n", opnames[opcode],
Object2CString(o), pc[1], S_LONGSTRING(-1), S_DOUBLE2(-1)));
SIZE_AND_STACK(3, 1);
}
case opc_putstatic_quick: {
fb = (struct fieldblock *)(constant_pool[GET_INDEX(pc+1)].p);
d = (OBJECT *)normal_static_address(fb);
*d = S_OBJECT(-1);
#ifdef TRACING
if (trace) {
char isig = fieldsig(fb)[0];
if (isig == SIGNATURE_FLOAT) {
TRACE(("\t%s %s.%s <= %g\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_FLOAT(-1)));
} else if (isig == SIGNATURE_CLASS || isig == SIGNATURE_ARRAY) {
TRACE(("\t%s %s.%s <= %s\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_OSTRING(-1)));
} else {
TRACE(("\t%s %s.%s <= %ld\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_INT(-1)));
}
}
#endif /* TRACING */
SIZE_AND_STACK(3, -1);
}
case opc_getstatic_quick: {
fb = (struct fieldblock *)(constant_pool[GET_INDEX(pc+1)].p);
d = (OBJECT *) normal_static_address(fb);
S_OBJECT(0) = *d;
#ifdef TRACING
if (trace) {
char isig = fieldsig(fb)[0];
if (isig == SIGNATURE_FLOAT) {
TRACE(("\t%s %s.%s <= %g\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_FLOAT(0)));
} else if (isig == SIGNATURE_CLASS || isig == SIGNATURE_ARRAY) {
TRACE(("\t%s %s.%s <= %s\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_OSTRING(0)));
} else {
TRACE(("\t%s %s.%s <= %ld\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_INT(0)));
}
}
#endif /* TRACING */
SIZE_AND_STACK(3, 1);
}
case opc_putstatic2_quick: {
fb = (struct fieldblock *)(constant_pool[GET_INDEX(pc+1)].p);
location = (stack_item *)twoword_static_address(fb);
location[0] = S_INFO(-2);
#ifndef OSF1
location[1] = S_INFO(-1);
#endif
#ifdef TRACING
if (trace) {
signature = fieldsig(fb);
if (signature[0] == SIGNATURE_LONG) {
TRACE(("\t%s %s.%s <= %s\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_LONGSTRING(-2)));
} else {
TRACE(("\t%s %s.%s <= %g\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_DOUBLE(-2)));
}
}
#endif /* TRACING */
SIZE_AND_STACK(3, -2);
}
case opc_putfield_quick_w: {
fb = (struct fieldblock *)(constant_pool[GET_INDEX(pc+1)].p);
isig = fieldsig(fb)[0];
offset = fb->u.offset / sizeof (OBJECT);
if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE) {
o = S_HANDLE(-3);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to set field %s",
fb->name);
ERROR("NullPointerException", buf);
}
#ifdef TRACING
if (trace) {
if (isig == SIGNATURE_LONG) {
TRACE(("\t%s %s.%s <= %s\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_LONGSTRING(-2)));
} else {
TRACE(("\t%s %s.%s <= %g\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_DOUBLE(-2)));
}
}
#endif /* TRACING */
obj_setslot(o, offset, (long)S_POINTER(-2));
#ifndef OSF1
obj_setslot(o, offset + 1, (long)S_POINTER(-1));
#endif
SIZE_AND_STACK(3, -3);
} else {
o = S_HANDLE(-2);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to set field %s",
fb->name);
ERROR("NullPointerException", buf);
}
#ifdef TRACING
if (trace) {
if (isig == SIGNATURE_FLOAT) {
TRACE(("\t%s %s.%s <= %g\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_FLOAT(-1)));
} else if (isig == SIGNATURE_CLASS ||
isig == SIGNATURE_ARRAY) {
TRACE(("\t%s %s.%s <= %s\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_OSTRING(-1)));
} else {
TRACE(("\t%s %s.%s <= %ld\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_INT(-1)));
}
}
#endif /* TRACING */
obj_setslot(o, offset, (long)S_POINTER(-1));
SIZE_AND_STACK(3, -2);
}
}
case opc_getstatic2_quick: {
fb = (struct fieldblock *)(constant_pool[GET_INDEX(pc+1)].p);
location = (stack_item *)twoword_static_address(fb);
S_INFO(0) = location[0];
#ifdef OSF1
S_LONG(1) = 0;
#else
S_INFO(1) = location[1];
#endif
#ifdef TRACING
if (trace) {
signature = fieldsig(fb);
if (signature[0] == SIGNATURE_LONG) {
TRACE(("\t%s %s.%s <= %s\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_LONGSTRING(0)));
} else {
TRACE(("\t%s %s.%s <= %g\n", opnames[opcode],
classname(fieldclass(fb)), fieldname(fb),
S_DOUBLE(0)));
}
}
#endif /* TRACING */
SIZE_AND_STACK(3, 2);
}
case opc_getfield_quick_w: {
fb = (struct fieldblock *)(constant_pool[GET_INDEX(pc+1)].p);
isig = fieldsig(fb)[0];
offset = fb->u.offset / sizeof (OBJECT);
o = S_HANDLE(-1);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to get field %s",
fb->name);
ERROR("NullPointerException", buf);
}
if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE) {
S_POINTER(-1) = (void *)obj_getslot(o, offset);
#ifdef OSF1
S_POINTER(0) = 0;
#else
S_POINTER(0) = (void *)obj_getslot(o, offset + 1);
#endif
#ifdef TRACING
if (trace) {
if (isig == SIGNATURE_LONG) {
TRACE(("\t%s %s.%s <= %s\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_LONGSTRING(-1)));
} else {
TRACE(("\t%s %s.%s <= %g\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_DOUBLE(-1)));
}
}
#endif /* TRACING */
SIZE_AND_STACK(3, 1);
} else {
S_POINTER(-1) = (void *)obj_getslot(o, offset);
#ifdef TRACING
if (trace) {
if (isig == SIGNATURE_FLOAT) {
TRACE(("\t%s %s.%s <= %g\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_FLOAT(-1)));
} else if (isig == SIGNATURE_CLASS ||
isig == SIGNATURE_ARRAY) {
TRACE(("\t%s %s.%s <= %s\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_OSTRING(-1)));
} else {
TRACE(("\t%s %s.%s <= %ld\n", opnames[opcode],
Object2CString(o), fieldname(fb),
S_INT(-1)));
}
}
#endif /* TRACING */
SIZE_AND_STACK(3, 0);
}
}
case opc_invokenonvirtual_quick:
mb = constant_pool[GET_INDEX(pc + 1)].p;
args_size = mb->args_size;
optop -= args_size;
frame->returnpc = pc + 3;
o = S_HANDLE(0);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to call %s%s (nonvirtual method)",
mb->fb.name, mb->fb.signature);
ERROR("NullPointerException", buf);
}
goto callmethod;
case opc_invokesuper_quick: {
int index = GET_INDEX(pc + 1);
current_class = fieldclass(&frame->current_method->fb);
superClass = unhand(cbSuperclass(current_class));
mb = cbMethodTable(superClass)->methods[index];
args_size = mb->args_size;
optop -= args_size;
frame->returnpc = pc + 3;
o = S_HANDLE(0);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to call %s%s (super)",
mb->fb.name, mb->fb.signature);
ERROR("NullPointerException", buf);
}
goto callmethod;
}
case opc_invokevirtual_quick_w:
mb = constant_pool[GET_INDEX(pc + 1)].p;
args_size = mb->args_size;
optop -= args_size;
frame->returnpc = pc + 3;
o = S_HANDLE(0);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to call %s%s (virtual method)",
mb->fb.name, mb->fb.signature);
ERROR("NullPointerException", buf);
}
mb = mt_slot(obj_array_methodtable(o), mb->fb.u.offset);
goto callmethod;
case opc_invokestatic_quick:
mb = constant_pool[GET_INDEX(pc + 1)].p;
args_size = mb->args_size;
optop -= args_size;
frame->returnpc = pc + 3;
/* o should be the handle to the method's class */
o = (JHandle *)(cbHandle(fieldclass(&mb->fb)));
goto callmethod;
case opc_invokeinterface_quick: {
cp_index_type NameAndType_index = GET_INDEX(pc + 1);
uint32_t ID = constant_pool[NameAndType_index].u;
int32_t mslot = pc[4]; /* guess the slot of the method name */
int32_t nslots;
struct methodblock **mbt; /* XXX These declarations should be deleted */
ClassClass *cb; /* XXX These declarations should be deleted */
args_size = pc[3];
optop -= args_size;
frame->returnpc = pc + 5;
o = S_HANDLE(0);
if (o == 0) {
char buf[64];
jio_snprintf(buf, sizeof(buf), "trying to call %s%s (interface method)",
mb->fb.name, mb->fb.signature);
ERROR("NullPointerException", buf);
}
/* Currently, arrays don't implement any interfaces that have
* methods in them. But this is cheap, and we may change our
* minds some time in the future. */
if (obj_flags(o) == T_NORMAL_OBJECT) {
struct methodtable *mtable = obj_methodtable(o);
mbt = mtable->methods;
cb = mtable->classdescriptor;
} else {
cb = classJavaLangObject;
mbt = cbMethodTable(cb)->methods;
}
nslots = cbMethodTableSize(cb);
if (mslot >= nslots || (mb = mbt[mslot]) == 0 || mb->fb.ID != ID) {
/* The guess didn't pay off, search */
int32_t n = cbMethodTableSize(cb);
int32_t slot = 0;
for (; --n > 0;) {
mb = mbt[++slot]; /* XXX what about slot 0??? */
if (mb->fb.ID == ID) {
break;
}
}
if (n <= 0) {
char buf[256];
int32_t len;
classname2string(classname(cb), buf, sizeof(buf));
len = strlen(buf);
/* If buffer overflow, quietly truncate */
(void) jio_snprintf(buf + len, (size_t) (sizeof(buf)-len),
": instance method %s not found",
IDToNameAndTypeString(ID));
ERROR("IncompatibleClassChangeError", buf);
}
/* After searching, set the guess for the next time around */
pc[4] = (unsigned char) slot;
} /* end of if(mslot...) block */
/* Don't allow calls to static methods.
Require that the called method be public.
The following lists those restrictions.
*/
if (((mb->fb.access & ACC_STATIC) != 0) ||
( ((mb->fb.access & ACC_PUBLIC) != ACC_PUBLIC)
&& (frame->current_method != NULL))) {
/* The call is illegal... so throw an exception */
char buf[256];
int32_t len;
char *msg;
classname2string(classname(cb), buf, sizeof(buf));
len = strlen(buf);
if (mb->fb.access & ACC_STATIC) {
msg = ": method %s%s used to be dynamic, but is now static";
} else {
msg = ": method %s%s has access restrictions: can't call via interface";
}
/* If buffer overflow, quietly truncate */
(void) jio_snprintf(buf + len, (size_t) (sizeof(buf)-len),
msg,
mb->fb.name, mb->fb.signature);
if (mb->fb.access & ACC_STATIC) {
ERROR("IncompatibleClassChangeError", buf);
} else {
ERROR("IllegalAccessError", buf);
}
}
if (exceptionOccurred(ee)) {
frame->lastpc = pc;
goto handle_exception;
}
goto callmethod;
}
case opc_invokevirtualobject_quick:
frame->returnpc = pc + 3;
args_size = pc[2];
optop -= args_size;
o = S_HANDLE(0);
if (o == 0) {
char buf[64];
struct methodblock* mb = frame->current_method;
if (mb)
jio_snprintf(buf, sizeof(buf), "trying to call %s%s (virtual method)",
mb->fb.name, mb->fb.signature);
else
jio_snprintf(buf, sizeof(buf), "trying to call native (virtual method)");
ERROR("NullPointerException", buf);
}
mb = mt_slot(obj_array_methodtable(o), pc[1]);
goto callmethod;
case opc_invokevirtual_quick:
frame->returnpc = pc + 3;
args_size = pc[2];
optop -= args_size;
o = S_HANDLE(0);
if (o == 0) {
char buf[64];
struct methodblock* mb = frame->current_method;
if (mb)
jio_snprintf(buf, sizeof(buf), "trying to call %s%s (virtual method)",
mb->fb.name, mb->fb.signature);
else
jio_snprintf(buf, sizeof(buf), "trying to call native (virtual method)");
ERROR("NullPointerException", buf);
}
mb = mt_slot(obj_methodtable(o), pc[1]);
callmethod:
sysAssert(o != 0);
TRACE(("\t%s %s.%s%s (%d)\n", opnames[opcode],
classname(fieldclass(&mb->fb)), fieldname(&mb->fb),
fieldsig(&mb->fb), args_size));
TRACE_METHOD(ee, mb, args_size, TRACE_METHOD_ENTER);
#ifndef DBINFO /* don't do it twice if DBINFO is defined */
frame->lastpc = pc;
#endif
DECACHE_OPTOP();
if (!mb->invoker(o, mb, args_size, ee)) {
/* FALSE indicates an exception might have occurred */
goto check_for_exception;
} else {
frame = ee->current_frame;
vars = frame->vars;
pc = frame->returnpc;
constant_pool = frame->constant_pool;
CACHE_OPTOP();
SIZE_AND_STACK(0, 0);
}
case opc_instanceof:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE
ResolveClassConstant(constant_pool, GET_INDEX(pc + 1), ee,
1 << CONSTANT_Class);
if (!exceptionOccurred(ee))
pc[0] = opc_instanceof_quick;
END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_instanceof_quick: {
DECACHE_OPTOP();
cb = constant_pool[GET_INDEX(pc + 1)].p;
h = S_HANDLE(-1);
S_INT(-1) = ((h != NULL) && is_instance_of(h, cb, ee));
TRACE(("\tinstanceof %s => %ld\n", classname(cb), S_INT(-1)));
SIZE_AND_STACK_BUT(3, 0); /* may error on implementations */
goto check_for_exception;
}
case opc_checkcast:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE
ResolveClassConstant(constant_pool, GET_INDEX(pc + 1), ee,
1 << CONSTANT_Class);
if (!exceptionOccurred(ee))
pc[0] = opc_checkcast_quick;
END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_checkcast_quick: {
DECACHE_OPTOP();
h = S_HANDLE(-1);
cb = constant_pool[GET_INDEX(pc + 1)].p;
if (!is_instance_of(h, cb, ee)) {
/* Checkcast fails */
char buf[255];
ERROR("ClassCastException",
classname2string(classname(obj_array_classblock(h)),
buf, sizeof(buf)));
}
TRACE(("\tcheckcast %s\n", classname(cb)));
SIZE_AND_STACK(3, 0);
}
case opc_new:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE
if (ResolveClassConstant(constant_pool, GET_INDEX(pc + 1), ee,
1 << CONSTANT_Class)) {
current_class = frame->current_method ?
fieldclass(&frame->current_method->fb) : NULL;
cb = constant_pool[GET_INDEX(pc + 1)].p;
if (cbAccess(cb) & (ACC_INTERFACE | ACC_ABSTRACT))
ERROR("InstantiationError", cb->name);
if (!VerifyClassAccess(current_class, cb, FALSE))
ERROR("IllegalAccessError", cb->name);
}
if (!exceptionOccurred(ee))
pc[0] = opc_new_quick;
END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_new_quick: {
cb = constant_pool[GET_INDEX(pc + 1)].p;
S_HANDLE(0) = newobject(cb, pc, ee);
TRACE(("\tnew %s => %s\n", classname(cb), S_OSTRING(0)));
SIZE_AND_STACK_BUT(3, 1);
goto check_for_exception;
}
case opc_anewarray:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE
ResolveClassConstant(constant_pool, GET_INDEX(pc + 1), ee,
1 << CONSTANT_Class);
if (!exceptionOccurred(ee))
pc[0] = opc_anewarray_quick;
END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_anewarray_quick: {
int32_t size = S_INT(-1);
if (size < 0)
ERROR("NegativeArraySizeException", 0);
retArray = (HArrayOfObject *)ArrayAlloc(T_CLASS, size);
if (retArray == 0)
ERROR("OutOfMemoryError", 0);
else {
array_cb = constant_pool[GET_INDEX(pc + 1)].p;
unhand(retArray)->body[size] = (HObject*)array_cb;
TRACE(("\tanewarray_quick %s[%ld] => %s\n",
array_cb->name, size, Object2CString((JHandle*)retArray)));
S_HANDLE(-1) = (JHandle *)retArray;
SIZE_AND_STACK(3, 0);
}
}
case opc_multianewarray:
DECACHE_OPTOP();
WITH_OPCODE_CHANGE
ResolveClassConstant(constant_pool, GET_INDEX(pc + 1), ee,
1 << CONSTANT_Class);
if (!exceptionOccurred(ee))
pc[0] = opc_multianewarray_quick;
END_WITH_OPCODE_CHANGE
goto check_for_exception;
case opc_multianewarray_quick: {
int dimensions = pc[3];
int i;
DECACHE_OPTOP();
array_cb = constant_pool[GET_INDEX(pc + 1)].p;
ResolveClassConstantFromClass(array_cb,
CONSTANT_POOL_ARRAY_CLASS_INDEX,
ee, 1 << CONSTANT_Class);
optop -= dimensions;
for (i = 0; i < dimensions; i++)
if (S_INT(i) < 0)
ERROR("NegativeArraySizeException", 0);
retO = MultiArrayAlloc(dimensions, array_cb, &S_INFO(0));
if (retO == 0)
ERROR("OutOfMemoryError", 0);
S_HANDLE(0) = retO;
TRACE(("\tmultianewarray_quick %s, dimensions %d => %s\n",
array_cb->name, dimensions,
Object2CString((JHandle*)retO)));
SIZE_AND_STACK(4, 1);
}
case opc_newarray: {
int32_t size = S_INT(-1);
int32_t type = pc[1];
/*int32_t array_size = sizearray(pc[1], size);*/
if (size < 0)
ERROR("NegativeArraySizeException", 0);
retH = ArrayAlloc(type, size);
if (retH == 0)
ERROR("OutOfMemoryError", 0);
TRACE(("\tnewarray %ld %ld => %s\n",
size, sizearray(type, size), Object2CString(retH)));
S_HANDLE(-1) = retH;
SIZE_AND_STACK(2, 0);
}
#define ARRAY_INTRO(type, indexOffset, arrayOffset) \
int32_t index = S_INT(indexOffset); \
H##type *arrh = (H##type *)(S_HANDLE(arrayOffset)); \
type *arr; \
if (arrh == 0) \
ERROR("NullPointerException", "trying to access array element"); \
arr = (type *) unhand(arrh); \
if (index < 0 || index >= (int32_t) obj_length(arrh)) { \
/* A 32-bit number is at most 10 digits */ \
char buf[30]; \
/* On (unlikely) overflow, quietly truncate */ \
(void) jio_snprintf(buf, sizeof(buf), "%ld", index); \
ERROR("ArrayIndexOutOfBoundsException", buf); \
}
#define ARRAY_LOAD_INTRO(type) ARRAY_INTRO(type, -1, -2)
case opc_iaload: {
ARRAY_LOAD_INTRO(ArrayOfInt);
S_INT(-2) = arr->body[index];
TRACE(("\tiaload ?[%ld] => %ld\n", index, S_INT(-2)));
SIZE_AND_STACK(1, -1);
}
case opc_laload: {
ARRAY_LOAD_INTRO(ArrayOfLong);
SET_S_LONG(-2, arr->body[index]);
TRACE(("\tlaload ?[%ld] => %s\n", index, S_LONGSTRING(-2)));
SIZE_AND_STACK(1, 0);
}
case opc_faload: {
ARRAY_LOAD_INTRO(ArrayOfFloat);
S_FLOAT(-2) = arr->body[index];
TRACE(("\tfaload ?[%ld] => %g\n", index, S_FLOAT(-2)));
SIZE_AND_STACK(1, -1);
}
case opc_daload: {
ARRAY_LOAD_INTRO(ArrayOfDouble);
SET_S_DOUBLE(-2, arr->body[index]);
TRACE(("\tdaload ?[%ld] => %g\n", index, S_DOUBLE(-2)));
SIZE_AND_STACK(1, 0);
}
case opc_aaload: {
ARRAY_LOAD_INTRO(ArrayOfObject);
S_HANDLE(-2) = arr->body[index];
TRACE(("\taaload ?[%ld] => %s\n", index, S_OSTRING(-2)));
SIZE_AND_STACK(1, -1);
}
case opc_baload: {
ARRAY_LOAD_INTRO(ArrayOfByte);
S_INT(-2) = arr->body[index];
TRACE(("\tbaload ?[%ld] => %ld\n", index, S_INT(-2)));
SIZE_AND_STACK(1, -1);
}
case opc_caload: {
ARRAY_LOAD_INTRO(ArrayOfChar);
S_INT(-2) = arr->body[index];
TRACE(("\tcaload ?[%ld] => %ld\n", index, S_INT(-2)));
SIZE_AND_STACK(1, -1);
}
case opc_saload: {
ARRAY_LOAD_INTRO(ArrayOfShort);
S_INT(-2) = arr->body[index];
TRACE(("\tsaload ?[%ld] => %ld\n", index, S_INT(-2)));
SIZE_AND_STACK(1, -1);
}
case opc_iastore: {
ARRAY_INTRO(ArrayOfInt, -2, -3);
arr->body[index] = S_INT(-1);
TRACE(("\tiastore ?[%ld] <= %ld\n", index, S_INT(-1)));
SIZE_AND_STACK(1, -3);
arr = 0;
}
case opc_lastore: {
ARRAY_INTRO(ArrayOfLong, -3, -4);
arr->body[index] = S_LONG(-2);
TRACE(("\tlastore ?[%ld] <= %s\n", index, S_LONGSTRING(-2)));
SIZE_AND_STACK(1, -4);
arr = 0;
}
case opc_fastore: {
ARRAY_INTRO(ArrayOfFloat, -2, -3);
arr->body[index] = S_FLOAT(-1);
TRACE(("\tfastore ?[%ld] <= %g\n", index, S_FLOAT(-1)));
SIZE_AND_STACK(1, -3);
arr = 0;
}
case opc_dastore: {
ARRAY_INTRO(ArrayOfDouble, -3, -4);
arr->body[index] = S_DOUBLE(-2);
TRACE(("\tdastore ?[%ld] <= %g\n", index, S_DOUBLE(-2)));
SIZE_AND_STACK(1, -4);
arr = 0;
}
case opc_bastore:{
ARRAY_INTRO(ArrayOfByte, -2, -3);
arr->body[index] = (char) S_INT(-1);
TRACE(("\tbastore ?[%ld] <= %ld\n", index, S_INT(-1)));
SIZE_AND_STACK(1, -3);
arr = 0;
}
case opc_castore:{
ARRAY_INTRO(ArrayOfChar, -2, -3);
arr->body[index] = (unicode) S_INT(-1);
TRACE(("\tcastore ?[%ld] <= %ld\n", index, (unicode) S_INT(-1)));
SIZE_AND_STACK(1, -3);
arr = 0;
}
case opc_sastore: {
ARRAY_INTRO(ArrayOfShort, -2, -3);
arr->body[index] = (int16_t) S_INT(-1);
TRACE(("\tsastore ?[%ld] <= %d\n", index, (short) S_INT(-1)));
SIZE_AND_STACK(1, -3);
arr = 0;
}
case opc_aastore: {
JHandle *value = S_HANDLE(-1);
ARRAY_INTRO(ArrayOfObject, -2, -3);
array_cb = (ClassClass *)(arr->body[obj_length(arrh)]);
if (!is_instance_of(value, array_cb, ee)) {
/* The value isn't null and doesn't match the type. */
ERROR("ArrayStoreException", 0);
}
arr->body[index] = value;
TRACE(("\taastore ?[%ld] <= %d\n", index, Object2CString(value)));
SIZE_AND_STACK(1, -3);
value = 0;
}
case opc_monitorenter: {
o = S_HANDLE(-1);
TRACE(("%s(%s)\n", opnames[opcode], Object2CString(o)));
if (o == 0) {
ERROR("NullPointerException", "trying to enter monitor");
} else {
#if defined(XP_MAC) || (defined(XP_PC) && !defined(_WIN32))
/* Be sure to fix md-include/sysmacros_md.h if you add any new
long-lived-locals.
The REALLY should be a single macro list:
#define CLEAR_ALL_LOCALS ... */
fb = 0;
mb = 0;
mbt = 0;
location = 0;
cb = 0;
array_cb = 0;
/* o = 0; /* XXX: This variable needs to be cleared as well */
h = 0;
d = 0;
current_class = 0;
superClass = 0;
retO = 0;
retH = 0;
arr = 0;
signature = 0;
retArray = 0;
#endif
monitorEnter(obj_monitor(o));
}
SIZE_AND_STACK(1, -1);
}
case opc_monitorexit: {
o = S_HANDLE(-1);
TRACE(("%s(%s)\n", opnames[opcode], Object2CString(o)));
if (o == 0) {
ERROR("NullPointerException", "trying to exit monitor");
} else {
monitorExit(obj_monitor(o));
}
SIZE_AND_STACK(1, -1);
}
case opc_breakpoint:
TRACE(("\tbreakpoint\n"));
#ifdef BREAKPTS
DECACHE_OPTOP();
opcode = get_breakpoint_opcode(pc, ee);
goto again;
#else
SIZE_AND_STACK(1, 0); /* treat as opc_nop */
#endif
case opc_wide: {
int reg = GET_INDEX(pc + 2);
int offset;
switch(pc[1]) {
case opc_aload:
case opc_iload:
case opc_fload:
S_INFO(0) = vars[reg];
TRACEIF(opc_iload,
("\tiload r%d => %ld\n", reg, S_INT(0)));
TRACEIF(opc_aload,
("\taload r%d => %s\n", reg, S_OSTRING(0)));
TRACEIF(opc_fload,
("\tfload r%d => %g\n", reg, S_FLOAT(0)));
SIZE_AND_STACK(4, 1);
case opc_lload:
case opc_dload:
S_INFO(0) = vars[reg];
S_INFO(1) = vars[reg + 1];
TRACEIF(opc_lload,
("\tlload r%d => %s\n", reg, S_LONGSTRING(0)));
TRACEIF(opc_dload,
("\tdload r%d => %g\n", reg, S_DOUBLE(0)));
SIZE_AND_STACK(4, 2);
case opc_istore:
case opc_astore:
case opc_fstore:
vars[reg] = S_INFO(-1);
TRACEIF(opc_istore,
("\tistore %ld => r%d\n", S_INT(-1), reg));
TRACEIF(opc_astore,
("\tastore %s => r%d\n", S_OSTRING(-1), reg));
TRACEIF(opc_fstore,
("\tfstore %g => r%d\n", S_FLOAT(-1), reg));
SIZE_AND_STACK(4, -1);
case opc_lstore:
case opc_dstore:
vars[reg] = S_INFO(-2);
vars[reg + 1] = S_INFO(-1);
TRACEIF(opc_lstore,
("\tlstore %s => r%d\n", S_LONGSTRING(-2), reg));
TRACEIF(opc_dstore,
("\tdstore %g => r%d\n", S_DOUBLE(-2), reg));
SIZE_AND_STACK(4, -2);
case opc_iinc:
offset = SSCAST(SCAT(pc,4)) << 8 | UCAT(pc,5); /* (((signed char *)pc)[4] << 8) + pc[5]; */
vars[reg].i += offset;
TRACE(("\tiinc r%d+%d => %ld\n", reg, offset, vars[reg].i));
SIZE_AND_STACK(6, 0);
case opc_ret:
TRACE(("\tret %d (%8X)\n", reg, vars[reg].addr));
pc = vars[reg].addr;
SIZE_AND_STACK_BUT(0, 0);
goto check_for_exception;
default:
ERROR("InternalError", "undefined opcode");
}
}
case opc_invokeignored_quick: /* 1.1 optimization. Not implemented */
default:
ERROR("InternalError", "undefined opcode");
} /* end of switch */
check_for_exception:
sysCheckException(ee);
/* Fall through */
handle_exception: {
/* We've got an exception. */
JHandle *object;
unsigned char *new_pc;
DECACHE_OPTOP();
object = ee->exception.exc;
new_pc = ProcedureFindThrowTag(ee, frame, object, pc);
if (new_pc != 0) {
/* Found the exception handler. */
exceptionClear(ee);
vars = frame->vars;
constant_pool = frame->constant_pool;
frame->optop = frame->ostack; /* Reset to empty */
CACHE_OPTOP();
/* Push exception object onto the stack. */
S_HANDLE(0) = object;
optop++;
pc = new_pc;
#ifdef TRACING
if (trace) {
char where_am_i[100];
pc2string(pc, frame->current_method,
where_am_i, where_am_i + sizeof where_am_i);
fprintf(stdout, "Catch at %s\n", where_am_i);
fflush(stdout);
}
#endif /* TRACING */
continue; /* continue with the outer loop */
}
}
/* Fall through */
finish_return:
/* Exit from a monitor, if necessary. */
if (frame->monitor)
monitorExit(obj_monitor(frame->monitor));
if (java_monitor) {
/* Indicate how much time we've used. */
mb = frame->current_method;
if (mb != 0) {
int32_t time = now() - frame->mon_starttime;
java_mon(frame->prev->current_method, mb, time);
}
}
#ifdef TRACING
if (trace) {
char where_am_i[100];
pc2string(pc, frame->current_method,
where_am_i, where_am_i + sizeof where_am_i);
fprintf(stdout, "Leaving %s\n", where_am_i);
fflush(stdout);
}
#endif /* TRACING */
if (frame == initial_frame) {
#ifdef DEBUG
ee->debugFlags &= ~EE_DEBUG_OPTOP_CACHED;
#endif
return !exceptionOccurred(ee);
} else {
TRACE_METHOD(ee, frame->current_method, args_size, TRACE_METHOD_RETURN);
ee->current_frame = frame = frame->prev;
CACHE_OPTOP();
if (exceptionOccurred(ee)) {
/* Go back, and look for an exception handler. */
pc = frame->lastpc;
goto handle_exception;
}
pc = frame->returnpc;
vars = frame->vars;
constant_pool = frame->constant_pool;
/* CACHE_OPTOP(); */
continue;
}
#if GENERATING68K
}
#endif
} /* end of infinite loop */
}
/* __ExecuteJavaEnd__ */
/* Search backwards for __ExecuteJavaStart__ to find the end of the function */
/*
* Note: Call InitializeInterpreter() BEFORE InitializeClassThread() to
* ensure that the Java stack lock is initialized before the idle thread
* has a chance to run!
*/
void
InitializeInterpreter()
{
_code_lock=(sys_mon_t *) sysMalloc(sysMonitorSizeof());
memset((char*) _code_lock, 0, sysMonitorSizeof());
CODE_LOCK_INIT();
_ostack_lock=(sys_mon_t *) sysMalloc(sysMonitorSizeof());
memset((char*) _ostack_lock, 0, sysMonitorSizeof());
OSTACK_LOCK_INIT();
#ifdef WIN32
/* if you dont get a JIT we really should set it back */
UseLosslessQuickOpcodes = TRUE;
#endif
#ifdef STATISTICS
memset(instruction_count, 0, sizeof(instruction_count));
#endif
}
#ifdef DEBUG
void PrintObject(JHandle *oh) {
if (oh == 0)
printf("NULL\n");
else {
ClassObject *o = unhand(oh);
if (-1000 < (long) o && (long) o < 1000)
printf("BOGUS-PTR[%ld]\n", (int32_t)o);
else if (obj_flags(oh) != T_NORMAL_OBJECT)
printf("%s\n", Object2CString(oh));
else {
ExecEnv *current_ee = threadSelf()
? (ExecEnv *)(unhand(threadSelf())->eetop)
: 0;
ExecEnv lee;
ClassClass *system_class;
ExecEnv *ee = current_ee ? current_ee
: (InitializeExecEnv(&lee, 0), &lee);
if (ee->initial_stack == 0) {
fprintf(stderr, "Out of memory\n");
}
system_class = FindClassFromClass(&lee, "java/lang/System", TRUE, NULL);
if (system_class == 0) {
fprintf(stderr, "Unable to find System class\n");
} else {
JHandle *out = *(JHandle **)getclassvariable(system_class, "out");
#ifdef TRACING
int oldtrace = trace; /* we don't want to see this */
trace = 0;
#endif /* TRACING */
execute_java_dynamic_method(ee, out,
"println", "(Ljava/lang/Object;)V",
oh);
#ifdef TRACING
trace = oldtrace;
#endif /* TRACING */
}
if (current_ee == 0)
DeleteExecEnv(&lee, 0);
}
}
}
#endif
#ifdef STATISTICS
void
DumpStatistics ()
{
int i;
for (i = 0; i < 256; i++) {
if (instruction_count[i]) {
printf("%30.-30s %6d\n", opnames[i], instruction_count[i]);
}
}
}
#endif
#ifdef XP_MAC
#ifndef DEBUG
#pragma global_optimizer reset
#endif
#endif
#ifdef TRACING
#if 0 /* Java 1.0.2 code that has moved to md/trace_md.c This
Code should be merged in one direction or the other. */
void
trace_method(struct execenv* ee, struct methodblock* mb,
int args_size, int type)
{
HArrayOfChar* threadName = threadSelf() ? getThreadName() : NULL;
JavaFrame *frame;
int depth;
int i;
for (frame = ee->current_frame, depth = 0;
frame != NULL;
frame = frame->prev)
if (frame->current_method != 0) depth++;
if (type == TRACE_METHOD_RETURN)
depth--;
printf("# ");
if (threadName) {
int threadNameLen = obj_length(threadName);
unicode *threadNameStr = unhand(threadName)->body;
for (i = 0; i < threadNameLen; i++)
putchar((char)(threadNameStr[i]));
}
printf(" [%2d] ", depth);
for (i = 0; i < depth; i++)
printf("| ");
printf("%c %s.%s%s ",
((type == TRACE_METHOD_ENTER) ? '>' : '<'),
classname(fieldclass(&mb->fb)),
fieldname(&mb->fb),
fieldsig(&mb->fb));
if (type == TRACE_METHOD_ENTER)
printf("(%d) entered\n", args_size);
else if (exceptionOccurred(ee))
printf("throwing %s\n",
ee->exception.exc->methods->classdescriptor->name);
else
printf("returning\n");
fflush(stdout);
}
#endif /* 0 Java 1.0.2 code */
#endif