/*
* @(#)exception.c 1.27 96/03/28
*
* 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.
*/
/*-
* Implementation of class Throwable
*
* former classruntime.c, Wed Jun 26 18:43:20 1991
*/
#include <stdio.h>
#include <signal.h>
#include "tree.h"
#include "javaString.h"
#include "monitor.h"
#include "javaThreads.h"
#include "exceptions.h"
#include "java_lang_Throwable.h"
#include "java_lang_Thread.h"
#include "java_io_PrintStream.h"
#if defined(min)
#undef min
#endif
#define min(x, y) (((int32_t)(x) < (int32_t)(y)) ? (x) : (y))
extern HArrayOfByte *MakeByteString(char *str, long len);
static int
getStackDepth(JavaFrame* start_frame)
{
JavaFrame frame_buf;
JavaFrame* frame;
int size;
for (size = 0, frame = start_frame; frame != NULL; ) {
if (frame->current_method != NULL) {
size++;
frame = frame->current_method->fb.access & ACC_MACHINE_COMPILED ?
CompiledFramePrev(frame, &frame_buf) : frame->prev;
} else {
frame = frame->prev;
}
}
return size;
}
void
printStackTrace(Hjava_lang_Throwable *o, StackPrinter printer,
void* stream, size_t len)
{
/* ignore len */
struct HArrayOfInt *backtrace = (HArrayOfInt *)(unhand(o)->backtrace);
if (backtrace != NULL) {
int32_t *real_data = unhand(backtrace)->body;
unsigned char **data;
unsigned char **enddata =
(unsigned char **)(real_data + obj_length(backtrace));
ExecEnv* ee = EE();
int currentStackDepth = getStackDepth(ee->current_frame);
int savedDepth = 0, currentLine = 0;
for (data = (unsigned char**)real_data; data < enddata; data++) {
if (*data != 0)
savedDepth++;
}
for (data = (unsigned char**)real_data; data < enddata; data++) {
if (*data != 0) {
#define LEN 128
char where[LEN];
int i = 0;
if ((savedDepth - currentLine++) == currentStackDepth) {
strncpy(where + i, "* at ", 5); i += 5;
} else {
strncpy(where + i, " at ", 5); i += 5;
}
pc2string(*data, 0, where + i, where + sizeof(where));
i = strlen(where);
i = min(i, LEN-2);
strncpy(where + i, "\n\0", 2); i += 1; /* don't include the null */
(void)printer(ee, stream, 0, where, i);
}
}
}
}
static int
printToPrintStream(void* env,
void* dest, size_t destLen,
const char* srcBuf, size_t srcLen)
{
/* ignore context, destLen */
ExecEnv* ee = (ExecEnv*)env;
Hjava_io_PrintStream* s = (Hjava_io_PrintStream*)dest;
HArrayOfByte* bytes = MakeByteString((char*)srcBuf, srcLen);
execute_java_dynamic_method(ee, (HObject*)s,
"write", "([B)V", bytes);
return 0;
}
void
java_lang_Throwable_printStackTrace0(Hjava_lang_Throwable *o,
Hjava_io_PrintStream *s)
{
printStackTrace(o, printToPrintStream, s, 0);
}
/* The pc's are saved as an int[] (to make it easy for the GC). The
* following constant indicates how may array elements are needed per pc
* It will be 1 on 32-bit machines and 2 on 64-bit machines.
*/
#define MULTIPLIER (sizeof(char *)/sizeof(long))
void
fillInStackTrace(Hjava_lang_Throwable *o, ExecEnv *ee) {
int size;
Classjava_lang_Throwable *obj = unhand(o);
JavaFrame *start_frame = ee->current_frame;
struct HArrayOfInt *backtrace = (HArrayOfInt *)obj->backtrace;
JavaFrame *frame;
int32_t *real_data;
unsigned char **data, **enddata;
JavaFrame start_frame_buf, frame_buf; /* two buffers to avoid aliasing conflicts */
/* Go backwards, removing all frames that are just part of the <init> of
* the throwable object "o"
*/
while (start_frame != NULL) {
if (start_frame->current_method != NULL) {
if (strcmp(fieldname(&start_frame->current_method->fb), "<init>") ||
start_frame->vars[0].h != (JHandle *)o)
break;
start_frame = start_frame->current_method->fb.access & ACC_MACHINE_COMPILED ?
CompiledFramePrev(start_frame, &start_frame_buf) : start_frame->prev;
} else {
start_frame = start_frame->prev;
}
}
/* how many useful frames are there on the stack? */
size = getStackDepth(start_frame);
/* if backtrace is null or not long enough, increase it */
if ((backtrace == NULL) || obj_length(backtrace) < size * MULTIPLIER) {
backtrace = (HArrayOfInt *)ArrayAlloc(T_INT, size * MULTIPLIER);
if (backtrace == NULL)
return;
obj->backtrace = (HObject *)backtrace;
}
/* Fill in the backtrace array with the pc's */
real_data = unhand(backtrace)->body;
data = (unsigned char **)(real_data);
enddata = (unsigned char **)(real_data + obj_length(backtrace));
for (frame = start_frame;
frame != NULL && data < enddata; ) { /* second test "just in case" */
if (frame->current_method != NULL) {
struct methodblock *mb = frame->current_method;
if (mb->fb.access & ACC_MACHINE_COMPILED) {
*data++ = CompiledCodePC(frame, mb);
frame = CompiledFramePrev(frame, &frame_buf);
} else {
*data++ = frame->lastpc;
frame = frame->prev;
}
} else {
frame = frame->prev;
}
}
/* Zero out any remaining space. */
while (data < enddata)
*data++ = 0;
}
/*
* Fill in the current stack trace in this exception. This is
* usually called automatically when the exception is created but it
* may also be called explicitly by the user. This routine returns
* `this' so you can write 'throw e.fillInStackTrace();'
*/
Hjava_lang_Throwable *
java_lang_Throwable_fillInStackTrace(Hjava_lang_Throwable *obj) {
ExecEnv *ee = (ExecEnv *)(THREAD(threadSelf())->eetop);
fillInStackTrace(obj, ee);
return obj;
}