netscape-revival
sun-java/runtime/classruntime.c
/*
* @(#)classruntime.c 1.59 96/04/17
*
* 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.
*/
/*-
* Runtime routines to support classes.
*/
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include "oobj.h"
#include "tree.h"
#include "interpreter.h"
#include "bool.h"
#include "timeval.h"
#include "javaThreads.h"
#include "monitor.h"
#include "exceptions.h"
#include "javaString.h"
#include "path.h"
#include "sys_api.h"
#include "java_lang_Throwable.h"
#include "java_lang_Thread.h"
#include "jri.h"
#if defined(XP_PC) && !defined(_WIN32)
#if !defined(stderr)
extern FILE *stderr;
#endif
#if !defined(stdout)
extern FILE *stdout;
#endif
#endif
#ifdef DEBUG
void
VerifyClasses()
{
static int32_t lastn;
int32_t i;
sysAssert(nbinclasses >= lastn);
lastn = nbinclasses;
sysAssert(nbinclasses <= sizebinclasses);
sysAssert(binclasses != 0 || nbinclasses == 0);
for (i = 0; i < nbinclasses; i++) {
ClassClass *clb = binclasses[i];
if (!clb) continue;
sysAssert(((int32_t) clb & 3) == 0);
sysAssert(clb->fields_count < 1000);
sysAssert(clb->methods_count < 1000);
sysAssert(cbInstanceSize(clb) && cbInstanceSize(clb) < 4000);
sysAssert(classname(clb)[0]);
}
}
#endif
/* These are all flavors of method invocation depending on the type
of method (defined by its access flags) being invoked. There
are 4 possibilities: java method, synchronized java method,
native (C) method, synchronized native method. Each methodblock
has a pointer to the appropriate one for its access, and each
one of these procedures optimizes its particular case.
They don't all use all they parameters, which is unfortunate. */
bool_t
invokeJavaMethod(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
int nlocals = mb->nlocals; /* maximum number of locals */
JavaFrame *old_frame = ee->current_frame; /* previous frame */
stack_item *optop = old_frame->optop; /* Points to just before args */
JavaStack *javastack = old_frame->javastack; /* Current stack pointer */
JavaFrame *frame = (JavaFrame *)(optop + nlocals); /* tentative */
#ifdef TRACING
if (trace) {
char where_am_i[100];
pc2string(mb->code, mb, where_am_i, where_am_i + sizeof where_am_i);
fprintf(stdout, "Entering %s\n", where_am_i);
fflush(stdout);
}
#endif /* TRACING */
if (frame->ostack + mb->maxstack > javastack->end_data) {
stack_item *copied_optop;
if (javastack->stack_so_far >= JavaStackSize) {
SignalError(ee, JAVAPKG "StackOverflowError", 0);
return FALSE;
}
javastack = javastack->next
? javastack->next : CreateNewJavaStack(ee, javastack);
if (javastack == 0) {
SignalError(ee, JAVAPKG "OutOfMemoryError", 0);
return FALSE;
}
frame = (JavaFrame *)(javastack->data + nlocals);
for (copied_optop = javastack->data; --args_size >= 0 ; )
copied_optop[args_size] = optop[args_size];
optop = copied_optop;
}
frame->vars = optop;
frame->javastack = javastack;
frame->prev = old_frame;
frame->optop = frame->ostack;
frame->current_method = mb;
frame->constant_pool = cbConstantPool(fieldclass(&mb->fb));
frame->returnpc = mb->code;
frame->monitor = 0;
frame->annotation = 0;
ee->current_frame = frame;
if (java_monitor)
frame->mon_starttime = now();
return TRUE;
}
bool_t
invokeSynchronizedJavaMethod(JHandle *o, struct methodblock *mb, int args_size,
ExecEnv *ee)
{
int nlocals = mb->nlocals; /* maximum number of locals */
JavaFrame *old_frame = ee->current_frame; /* previous frame */
stack_item *optop = old_frame->optop; /* Points to just before args */
JavaStack *javastack = old_frame->javastack; /* Current stack pointer */
JavaFrame *frame = (JavaFrame *)(optop + nlocals); /* tentative */
#ifdef TRACING
if (trace) {
char where_am_i[100];
pc2string(mb->code, mb, where_am_i, where_am_i + sizeof where_am_i);
fprintf(stdout, "Entering %s\n", where_am_i);
fflush(stdout);
}
#endif /* TRACING */
if (frame->ostack + mb->maxstack > javastack->end_data) {
stack_item *copied_optop;
if (javastack->stack_so_far >= JavaStackSize) {
SignalError(ee, JAVAPKG "StackOverflowError", 0);
return FALSE;
}
javastack = javastack->next
? javastack->next : CreateNewJavaStack(ee, javastack);
if (javastack == 0) {
SignalError(ee, JAVAPKG "OutOfMemoryError", 0);
return FALSE;
}
frame = (JavaFrame *)(javastack->data + nlocals);
for (copied_optop = javastack->data; --args_size >= 0 ; )
copied_optop[args_size] = optop[args_size];
optop = copied_optop;
}
frame->javastack = javastack;
frame->prev = old_frame;
frame->vars = optop;
frame->optop = frame->ostack;
frame->current_method = mb;
frame->constant_pool = cbConstantPool(fieldclass(&mb->fb));
frame->returnpc = mb->code;
frame->monitor = o;
frame->annotation = 0;
ee->current_frame = frame;
monitorEnter(obj_monitor(o));
if (java_monitor)
frame->mon_starttime = now();
return TRUE;
}
bool_t
invokeNativeMethod(JHandle *o, struct methodblock *mb, int args_size,
ExecEnv *ee)
{
JavaFrame *old_frame = ee->current_frame;
stack_item *optop = old_frame->optop;
if (java_monitor) {
long start_time, end_time;
start_time = now();
optop = (*(stack_item * (*) (stack_item*, ExecEnv*))
mb->code) (optop, ee);
end_time = now();
java_mon(old_frame->current_method, mb, end_time - start_time);
/* java_mon(fieldclass(&mb->fb), mb, ee, 0, end_time - start_time); */
} else {
optop = (*(stack_item * (*) (stack_item*, ExecEnv*))
mb->code) (optop, ee);
}
old_frame->optop = optop;
TRACE_METHOD(ee, mb, args_size, TRACE_METHOD_NATIVE_RETURN);
return !(exceptionOccurred(ee));
}
bool_t
invokeSynchronizedNativeMethod(JHandle *o, struct methodblock *mb, int args_size,
ExecEnv *ee)
{
JavaFrame *old_frame = ee->current_frame;
stack_item *optop = old_frame->optop;
monitorEnter(obj_monitor(o));
if (java_monitor) {
long start_time, end_time;
start_time = now();
optop = (*(stack_item * (*) (stack_item*, ExecEnv*))
mb->code) (optop, ee);
end_time = now();
java_mon(old_frame->current_method, mb, end_time - start_time);
/* java_mon(fieldclass(&mb->fb), mb, ee, 0, end_time - start_time); */
} else {
optop = (*(stack_item * (*) (stack_item*, ExecEnv*))
mb->code) (optop, ee);
}
old_frame->optop = optop;
monitorExit(obj_monitor(o));
TRACE_METHOD(ee, mb, args_size, TRACE_METHOD_NATIVE_RETURN);
return !(exceptionOccurred(ee));
}
bool_t
invokeLazyNativeMethod(JHandle *o, struct methodblock *mb, int args_size,
ExecEnv *ee)
{
if ((mb->code != 0) || dynoLink(mb)) {
mb->invoker = (mb->fb.access & ACC_SYNCHRONIZED)
? invokeSynchronizedNativeMethod : invokeNativeMethod;
return (mb->invoker)(o, mb, args_size, ee);
} else {
char buf[1024];
jio_snprintf(buf, sizeof(buf), "native method %s.%s not found: %s",
classname((&mb->fb)->clazz), fieldname(&mb->fb),
PR_GetErrorString());
SignalError(ee, JAVAPKG "UnsatisfiedLinkError", buf);
return FALSE;
}
}
bool_t
invokeAbstractMethod(JHandle *o, struct methodblock *mb, int args_size,
ExecEnv *ee)
{
SignalError(ee, JAVAPKG "AbstractMethodError",
fieldname(&mb->fb));
return FALSE;
}
bool_t
dynoLink(struct methodblock *mb)
{
char sym[300];
void* lib;
ClassClass *cb = fieldclass(&mb->fb);
if (cbLoader(cb)) {
return 0;
}
mangleMethodName(mb, sym, sizeof(sym), "_stub");
mb->code = (unsigned char *)sysDynamicLink(sym, &lib);
mb->lib = lib;
return (mb->code != 0);
}
/* I put this into a separate function so that other parts of the
interpreter can use it without knowing how an object is actually
created. - csw
*/
HObject *
newobject(ClassClass *cb, unsigned char *pc, struct execenv *ee)
{
HObject *ret;
if ((ret = ObjAlloc(cb, 0)) == 0) {
#if 0
char buf[128];
#endif
if (ee != NULL)
ee->current_frame->lastpc = pc;
#if 0
/*
** removed -- because we recurse trying to call newobject to
** allocate the exception:
*/
SignalError(ee, JAVAPKG "OutOfMemoryError", classname2string(classname(cb), buf, sizeof(buf)));
#endif
exceptionThrow((ee ? ee : EE()), exceptionInternalObject(IEXC_OutOfMemory));
return 0;
}
return ret;
}
char*
RunStaticInitializers(ClassClass *cb)
{
struct methodblock *mb;
int size;
char *s;
struct methodblock *StaticInitializer = 0;
char* ret = NULL;
mb = cbMethods(cb);
for (size = 0; size < (int) cb->methods_count; size++, mb++) {
sysAssert(mb->fb.u.offset < cbMethodTableSize(cb));
s = fieldname(&mb->fb);
if (s[0] == '<' && strcmp(s, "<clinit>") == 0
&& strcmp(fieldsig(&mb->fb), "()V") == 0)
StaticInitializer = mb;
}
if (StaticInitializer && !InhibitExecute) {
struct execenv *ee = EE();
#ifndef TRIMMED
if (verbose)
fprintf(stderr, "[Running static initializer for %s]\n",
classname(cb));
#endif
do_execute_java_method(ee, cb, 0, 0, StaticInitializer, TRUE);
if (ee && exceptionOccurred(ee)) {
/*
** XXX This sucks how Sun has done this. There's about 10
** levels of routines that just return exception name strings
** rather than call SignalError. Now way down here where we
** actually call java and could get back a real exception
** object, we have to yank out its name and return it, only
** to have it constructed again later. They used to just drop
** this error on the floor, so this hack is better than
** nothing.
*/
JHandle* exc = ee->exception.exc;
ClassClass* cl = obj_classblock(exc);
ret = classname(cl);
}
}
return ret;
}
void
InitializeInvoker(ClassClass *cb)
{
struct methodblock *mb;
int size;
mb = cbMethods(cb);
for (size = 0; size < (int) cb->methods_count; size++, mb++) {
unsigned int access = mb->fb.access;
sysAssert(mb->fb.u.offset < cbMethodTableSize(cb));
if (access & ACC_MACHINE_COMPILED) {
mb->invoker = invokeCompiledMethod;
} else if ((access & (ACC_ABSTRACT | ACC_NATIVE)) == 0) {
/* Normal java method */
mb->invoker = (access & ACC_SYNCHRONIZED)
? invokeSynchronizedJavaMethod
: invokeJavaMethod;
} else if (access & ACC_ABSTRACT) {
cbAccess(cb) |= ACC_ABSTRACT;
mb->invoker = invokeAbstractMethod;
} else {
mb->invoker = invokeLazyNativeMethod;
}
}
}
JRI_PUBLIC_API(void)
SignalError(struct execenv * ee, char *ename, char *DetailMessage)
{
ClassClass *cb;
if (ee == 0)
ee = EE();
#ifdef DEBUG
if (exceptionOccurred(ee)) {
/* Masking a pending error */
ee = ee;
}
#endif
/*
* Find the exception class (or the default). If we can't find it
* throw a preallocated object
*/
if ((cb = FindClassFromClass(ee, ename, TRUE, NULL)) == 0 &&
(cb = FindClassFromClass(ee, JAVAPKG "UnknownError", TRUE, NULL)) == 0) {
#ifndef TRIMMED
char where[100];
if (ee->current_frame) {
pc2string(ee->current_frame->lastpc,
ee->current_frame->current_method,
where, where + sizeof where);
fprintf(stderr, "Class missing for error: %s at %s\n", ename, where);
} else {
fprintf(stderr, "Class missing for error: %s\n", ename);
}
#endif
exceptionThrow(ee, exceptionInternalObject(IEXC_NoClassDefinitionFound));
return;
}
SignalError1(ee, cb, DetailMessage);
}
void
SignalError1(struct execenv * ee, ClassClass* cb, char *DetailMessage)
{
Hjava_lang_Throwable *ret;
Classjava_lang_Throwable *new_obj;
JavaFrame *frame;
sysAssert(ee);
/*
* Instantiate the object. If we can't then throw a preallocated
* object.
*/
if ((ret = (Hjava_lang_Throwable *)newobject(cb, 0, ee)) == NULL) {
exceptionThrow(ee, exceptionInternalObject(IEXC_OutOfMemory));
return;
}
new_obj = unhand(ret);
/* Backtrace through the interpreter frames. */
frame = ee ? ee->current_frame : 0;
#if 0 /* XXX What is this? */
if (frame == 0) {
#ifndef TRIMMED
fprintf(stderr, "Exception: %s %s (can't backtrace because of a missing "
"context)\n", cb->name, (DetailMessage ? DetailMessage : ""));
#endif
exceptionThrow(ee, (HObject *)ret);
return;
}
#endif
#ifdef TRACING
if (trace) {
printf("%6X %8X\tERROR %s\n", threadSelf(),
(frame ? frame->lastpc : (unsigned char*)-1), cb->name);
fflush(stdout);
}
#endif /* TRACING */
if (DetailMessage)
new_obj->detailMessage = makeJavaString(DetailMessage,
strlen(DetailMessage));
#ifdef DEBUG
if (threadSelf() == NULL)
/* can only happen on startup */
fprintf(stderr, "Exception: %s %s (can't backtrace because of a missing "
"context)\n", cb->name, (DetailMessage ? DetailMessage : ""));
else
#endif
fillInStackTrace(ret, ee);
/* This must be the last thing, since makeJavaString() may clear the
* signal Error slot.
*/
exceptionThrow(ee, (HObject *)ret);
}
char *
addhex(long n, char *buf, char *limit)
{
int i;
for (i = 32; (i -= 4) >= 0 && buf < limit;)
*buf++ = "0123456789ABCDEF"[(n >> i) & 0xF];
return buf;
}
char *
adddec(long n, char *buf, char *limit)
{
if (n < 0) {
n = -n;
if (buf < limit)
*buf++ = '-';
}
if (n >= 10)
buf = adddec(n / 10, buf, limit);
if (buf < limit)
*buf++ = (n % 10) + '0';
return buf;
}
int32_t
pc2lineno(struct methodblock *mb, unsigned int pc_offset)
{
uint32_t length = mb->line_number_table_length;
if (length > 0) {
struct lineno *ln = &mb->line_number_table[length];
int32_t i;
for (i = mb->line_number_table_length; --i >= 0; )
if (pc_offset >= (--ln)->pc)
return (int32_t) ln->line_number;
}
return -1;
}
static struct methodblock *pc2method(unsigned char *pc) {
struct methodblock *mb;
ClassClass *cb;
int32_t i, j;
for (i = nbinclasses; --i >= 0; ) {
cb = binclasses[i];
if (!cb) continue;
for (mb = cbMethods(cb), j = cb->methods_count; --j >= 0; mb++) {
if (!(mb->fb.access & ACC_NATIVE)) {
/*
** In WIN16 pointer arithmetic deals with only the low 16-bits of a pointer.
** So, case everything to long...
*/
long code_start;
if (mb->fb.access & ACC_MACHINE_COMPILED) {
if (PCinCompiledCode(pc, mb))
return mb;
} else {
code_start = (long)mb->code;
if ((long)pc >= code_start && (long)pc < code_start + mb->code_length)
return mb;
}
}
}
}
return 0;
}
char *
pc2string(unsigned char *pc, struct methodblock *mb, char *buf, char *limit)
{
if (buf >= limit) {
return buf;
}
--limit; /* Save room for terminating '\0' */
if (mb == 0) {
mb = pc2method(pc);
}
if (mb != 0) {
char cbuf[128];
ClassClass *cb = fieldclass(&mb->fb);
buf = addstr(classname2string(classname(cb),
cbuf, sizeof(cbuf)), buf, limit, 0);
buf = addstr(".", buf, limit, 0);
buf = addstr(fieldname(&mb->fb), buf, limit, '(');
if (cb != 0) {
if ((mb->fb.access & ACC_MACHINE_COMPILED) == 0) {
char *fn;
int32_t lineno;
if (cb->source_name)
fn = strrchr(classsrcname(cb), '/');
else
fn = 0;
buf = addstr("(", buf, limit, 0);
buf = addstr(fn ? fn + 1 : classsrcname(cb), buf, limit, 0);
lineno = pc2lineno(mb, pc - mb->code);
if (lineno >= 0) {
buf = addstr(":", buf, limit, 0);
buf = adddec(lineno, buf, limit);
}
buf = addstr(")", buf, limit, 0);
} else {
buf = addstr("(Compiled Code)", buf, limit, 0);
}
}
*buf = 0;
} else {
buf[0] = '\0';
}
return buf;
}
/*
Make an Java unicode string out of a series of bytes that are
already encoded as unicode.
*/
HArrayOfChar *
MakeUniString(unicode *str, long len)
{
ArrayOfChar *new_str;
JHandle *ret;
unicode *tmp;
sysAssert (str != 0);
for (tmp = str; *tmp; tmp++)
;
if (len > tmp-str)
len = (int) (tmp-str);
ret = ArrayAlloc(T_CHAR, len);
if (ret == 0)
return 0;
new_str = (ArrayOfChar *) unhand(ret);
memmove((char *) new_str->body, (char *) str, (size_t)(len * sizeof(unicode)));
return (HArrayOfChar *)ret;
}
/*
Make an Java unicode string from a non-unicode C-style string.
*/
HArrayOfChar *
MakeString(char *str, long len)
{
ArrayOfChar *new_str;
HArrayOfChar *ret;
sysAssert(str != 0);
ret = (HArrayOfChar *) ArrayAlloc(T_CHAR, len);
if (ret == 0)
return 0;
new_str = unhand(ret);
str2unicode(str, new_str->body, len);
return ret;
}
/*
Make an Java array of bytes
*/
HArrayOfByte *
MakeByteString(char *str, long len)
{
ArrayOfByte *new_str;
JHandle *ret;
ret = ArrayAlloc(T_BYTE, len);
if (ret == 0)
return 0;
new_str = (ArrayOfByte *) unhand(ret);
if (str) {
memmove((char *) new_str->body, (char *)str, (size_t) len);
}
return (HArrayOfByte *)ret;
}
JRI_PUBLIC_API(ExecEnv *)
EE()
{
ExecEnv *ee;
TID t = threadSelf();
ee = (t) ? (ExecEnv *)THREAD(threadSelf())->eetop : DefaultExecEnv;
sysAssert(ee != 0);
return ee;
}