progress towards thread support

This includes support for using the least significant bits of the class
pointer to indicate object state, which we'll use to indicate the
presence of a monitor pointer, among other things.
This commit is contained in:
Joel Dice 2007-07-01 15:34:22 -06:00
parent 051e3bc7a8
commit 38cea04322
9 changed files with 282 additions and 103 deletions

View File

@ -5,7 +5,9 @@ public class TestExceptions {
}
private static void moreDangerous() {
evenMoreDangerous();
// synchronized (TestExceptions.class) {
evenMoreDangerous();
// }
}
private static void dangerous() {

View File

@ -1,4 +1,4 @@
public class Test {
public class TestGC {
private static void small() {
for (int i = 0; i < 1024; ++i) {

View File

@ -127,7 +127,7 @@ fast-cflags = $(fast) $(cflags)
classpath-sources = $(shell find $(classpath)/java -name '*.java')
classpath-classes = $(call java-classes,$(classpath-sources),$(classpath))
input = $(bld)/classes/TestExceptions.class
input = $(bld)/classes/Test.class
input-depends = \
$(classpath-classes) \
$(jni-library)

View File

@ -26,6 +26,9 @@ typedef void* object;
const unsigned BytesPerWord = sizeof(uintptr_t);
const unsigned BitsPerWord = BytesPerWord * 8;
const uintptr_t PointerMask
= ((~static_cast<uintptr_t>(0)) / BytesPerWord) * BytesPerWord;
const unsigned LikelyPageSizeInBytes = 4 * 1024;
inline unsigned
@ -109,6 +112,13 @@ cast(object p, unsigned offset)
return *reinterpret_cast<T*>(static_cast<uint8_t*>(p) + offset);
}
template <class T>
inline T*
mask(T* p)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(p) & PointerMask);
}
}
#endif//COMMON_H

View File

@ -26,6 +26,32 @@ System* system(Context*);
void NO_RETURN abort(Context*);
void assert(Context*, bool);
inline object
get(object o, unsigned offsetInWords)
{
return mask(cast<object>(o, offsetInWords * BytesPerWord));
}
inline object*
getp(object o, unsigned offsetInWords)
{
return &cast<object>(o, offsetInWords * BytesPerWord);
}
inline void
set(object* o, object value)
{
*o = reinterpret_cast<object>
(reinterpret_cast<uintptr_t>(value)
| reinterpret_cast<uintptr_t>(*o) & (~PointerMask));
}
inline void
set(object o, unsigned offsetInWords, object value)
{
set(getp(o, offsetInWords), value);
}
class Segment {
public:
class Map {
@ -662,7 +688,7 @@ fresh(Context* c, object o)
inline bool
wasCollected(Context* c, object o)
{
return o and (not fresh(c, o)) and fresh(c, cast<object>(o, 0));
return o and (not fresh(c, o)) and fresh(c, get(o, 0));
}
inline object
@ -779,55 +805,55 @@ copy(Context* c, object o)
}
object
update3(Context* c, object* p, bool* needsVisit)
update3(Context* c, object o, bool* needsVisit)
{
if (wasCollected(c, *p)) {
if (wasCollected(c, o)) {
*needsVisit = false;
return follow(c, *p);
return follow(c, o);
} else {
*needsVisit = true;
return copy(c, *p);
return copy(c, o);
}
}
object
update2(Context* c, object* p, bool* needsVisit)
update2(Context* c, object o, bool* needsVisit)
{
switch (c->mode) {
case MinorCollection:
case OverflowCollection:
if (c->gen2.contains(*p)) {
if (c->gen2.contains(o)) {
*needsVisit = false;
return *p;
return o;
}
break;
case Gen2Collection:
if (c->gen2.contains(*p)) {
return update3(c, p, needsVisit);
if (c->gen2.contains(o)) {
return update3(c, o, needsVisit);
} else {
assert(c, c->nextGen1.contains(*p) or c->nextGen2.contains(*p));
assert(c, c->nextGen1.contains(o) or c->nextGen2.contains(o));
*needsVisit = false;
return *p;
return o;
}
break;
default: break;
}
return update3(c, p, needsVisit);
return update3(c, o, needsVisit);
}
object
update(Context* c, object* p, bool* needsVisit)
{
if (*p == 0) {
if (mask(*p) == 0) {
*needsVisit = false;
return *p;
return 0;
}
object r = update2(c, p, needsVisit);
object r = update2(c, mask(*p), needsVisit);
// update heap map.
if (r) {
@ -956,22 +982,22 @@ bitsetNext(Context* c, uintptr_t* p)
}
void
collect(Context* c, void** p)
collect(Context* c, object* p)
{
object original = *p;
object original = mask(*p);
object parent = 0;
if (Debug) {
fprintf(stderr, "update %p (%s) at %p (%s)\n",
*p, segment(c, *p), p, segment(c, p));
mask(*p), segment(c, *p), p, segment(c, p));
}
bool needsVisit;
*p = update(c, p, &needsVisit);
set(p, update(c, mask(p), &needsVisit));
if (Debug) {
fprintf(stderr, " result: %p (%s) (visit? %d)\n",
*p, segment(c, *p), needsVisit);
mask(*p), segment(c, *p), needsVisit);
}
if (not needsVisit) return;
@ -995,17 +1021,16 @@ collect(Context* c, void** p)
virtual bool visit(unsigned offset) {
if (Debug) {
fprintf(stderr, " update %p (%s) at %p - offset %d from %p (%s)\n",
cast<object>(copy, offset * BytesPerWord),
segment(c, cast<object>(copy, offset * BytesPerWord)),
&cast<object>(copy, offset * BytesPerWord),
get(copy, offset),
segment(c, get(copy, offset)),
getp(copy, offset),
offset,
copy,
segment(c, copy));
}
bool needsVisit;
object childCopy = update
(c, &cast<object>(copy, offset * BytesPerWord), &needsVisit);
object childCopy = update(c, getp(copy, offset), &needsVisit);
if (Debug) {
fprintf(stderr, " result: %p (%s) (visit? %d)\n",
@ -1027,7 +1052,7 @@ collect(Context* c, void** p)
second = offset;
}
} else {
cast<object>(copy, offset * BytesPerWord) = childCopy;
set(copy, offset, childCopy);
}
if (visits > 1 and total > 2 and (second or needsVisit)) {
@ -1070,8 +1095,8 @@ collect(Context* c, void** p)
parent = original;
}
original = cast<object>(copy, walker.first * BytesPerWord);
cast<object>(copy, walker.first * BytesPerWord) = follow(c, original);
original = get(copy, walker.first);
set(copy, walker.first, follow(c, original));
goto visit;
} else {
// ascend
@ -1131,16 +1156,16 @@ collect(Context* c, void** p)
if (Debug) {
fprintf(stderr, " next is %p (%s) at %p - offset %d from %p (%s)\n",
cast<object>(copy, walker.next * BytesPerWord),
segment(c, cast<object>(copy, walker.next * BytesPerWord)),
&cast<object>(copy, walker.next * BytesPerWord),
get(copy, walker.next),
segment(c, get(copy, walker.next)),
getp(copy, walker.next),
walker.next,
copy,
segment(c, copy));
}
original = cast<object>(copy, walker.next * BytesPerWord);
cast<object>(copy, walker.next * BytesPerWord) = follow(c, original);
original = get(copy, walker.next);
set(copy, walker.next, follow(c, original));
goto visit;
} else {
return;
@ -1236,7 +1261,7 @@ collect(Context* c, Segment* s, unsigned limit)
Walker(Context* c, object p): c(c), p(p) { }
virtual bool visit(unsigned offset) {
collect(c, &cast<object>(p, offset * BytesPerWord));
collect(c, getp(p, offset));
return true;
}

View File

@ -1,8 +1,12 @@
#include "sys/mman.h"
#include "sys/types.h"
#include "sys/stat.h"
#include <sys/time.h>
#include <time.h>
#include "fcntl.h"
#include "dlfcn.h"
#include "errno.h"
#include "pthread.h"
#include "common.h"
#include "system.h"
#include "heap.h"
@ -86,6 +90,22 @@ dynamicCall(void* function, uint64_t* arguments, uint8_t* argumentTypes,
namespace {
void*
run(void* t)
{
static_cast<vm::System::Thread*>(t)->run();
return 0;
}
int64_t
now()
{
timeval tv = { 0, 0 };
gettimeofday(&tv, 0);
return (static_cast<int64_t>(tv.tv_sec) * 1000) +
(static_cast<int64_t>(tv.tv_usec) / 1000);
}
const char*
append(vm::System* s, const char* a, const char* b, const char* c,
const char* d)
@ -108,17 +128,95 @@ class System: public vm::System {
public:
class Monitor: public vm::System::Monitor {
public:
Monitor(vm::System* s): s(s) { }
Monitor(vm::System* s): s(s), context(0), depth(0) {
pthread_mutex_init(&mutex, 0);
pthread_cond_init(&condition, 0);
}
virtual bool tryAcquire(void*) { return true; }
virtual void acquire(void*) { }
virtual void release(void*) { }
virtual void wait(void*) { }
virtual void notify(void*) { }
virtual void notifyAll(void*) { }
virtual void dispose() { s->free(this); }
virtual bool tryAcquire(void* context) {
if (this->context == context) {
++ depth;
return true;
} else {
switch (pthread_mutex_trylock(&mutex)) {
case EBUSY:
return false;
case 0:
this->context = context;
++ depth;
return true;
default:
vm::abort(s);
}
}
}
virtual void acquire(void* context) {
if (this->context != context) {
pthread_mutex_lock(&mutex);
this->context = context;
}
++ depth;
}
virtual void release(void* context) {
if (this->context == context) {
if (-- depth == 0) {
this->context = 0;
pthread_mutex_unlock(&mutex);
}
} else {
vm::abort(s);
}
}
virtual void wait(void* context, int64_t time) {
if (this->context == context) {
if (time) {
int64_t then = now() + time;
timespec ts = { then / 1000, (then % 1000) * 1000 * 1000 };
int rv = pthread_cond_timedwait(&condition, &mutex, &ts);
assert(s, rv == 0);
} else {
int rv = pthread_cond_wait(&condition, &mutex);
assert(s, rv == 0);
}
} else {
vm::abort(s);
}
}
virtual void notify(void* context) {
if (this->context == context) {
int rv = pthread_cond_signal(&condition);
assert(s, rv == 0);
} else {
vm::abort(s);
}
}
virtual void notifyAll(void* context) {
if (this->context == context) {
int rv = pthread_cond_broadcast(&condition);
assert(s, rv == 0);
} else {
vm::abort(s);
}
}
virtual void dispose() {
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&condition);
s->free(this);
}
vm::System* s;
pthread_mutex_t mutex;
pthread_cond_t condition;
void* context;
unsigned depth;
};
class Library: public vm::System::Library {
@ -149,14 +247,20 @@ class System: public vm::System {
vm::System::Library* next_;
};
System(unsigned limit): limit(limit), count(0) { }
System(unsigned limit): limit(limit), count(0) {
pthread_mutex_init(&mutex, 0);
}
~System() {
pthread_mutex_destroy(&mutex);
}
virtual bool success(Status s) {
return s == 0;
}
virtual void* tryAllocate(unsigned size) {
// todo: synchronize access
pthread_mutex_lock(&mutex);
if (Verbose) {
fprintf(stderr, "try %d; count: %d; limit: %d\n",
@ -164,20 +268,26 @@ class System: public vm::System {
}
if (count + size > limit) {
pthread_mutex_unlock(&mutex);
return 0;
} else {
uintptr_t* up = static_cast<uintptr_t*>
(malloc(size + sizeof(uintptr_t)));
if (up == 0) {
pthread_mutex_unlock(&mutex);
vm::abort(this);
} else {
*up = size;
count += *up;
pthread_mutex_unlock(&mutex);
return up + 1;
}
}
uintptr_t* up = static_cast<uintptr_t*>(malloc(size + sizeof(uintptr_t)));
if (up == 0) abort();
*up = size;
count += *up;
return up + 1;
}
virtual void free(const void* p) {
// todo: synchronize access
pthread_mutex_lock(&mutex);
if (p) {
const uintptr_t* up = static_cast<const uintptr_t*>(p) - 1;
@ -193,10 +303,15 @@ class System: public vm::System {
::free(const_cast<uintptr_t*>(up));
}
pthread_mutex_unlock(&mutex);
}
virtual Status start(Thread*) {
return 1;
virtual Status start(Thread* t) {
pthread_t thread;
int rv = pthread_create(&thread, 0, run, t);
assert(this, rv == 0);
return 0;
}
virtual Status make(vm::System::Monitor** m) {
@ -232,6 +347,7 @@ class System: public vm::System {
::abort();
}
pthread_mutex_t mutex;
unsigned limit;
unsigned count;
};

View File

@ -21,7 +21,7 @@ class System {
virtual bool tryAcquire(void* id) = 0;
virtual void acquire(void* id) = 0;
virtual void release(void* id) = 0;
virtual void wait(void* id) = 0;
virtual void wait(void* id, int64_t time) = 0;
virtual void notify(void* id) = 0;
virtual void notifyAll(void* id) = 0;
virtual void dispose() = 0;

View File

@ -995,7 +995,7 @@ writeSubtypeAssertions(Output* out, Object* o)
{
for (Object* p = typeSubtypes(o); p; p = cdr(p)) {
Object* st = car(p);
out->write(" or objectClass(o) == arrayBodyUnsafe");
out->write(" or objectClass(t, o) == arrayBodyUnsafe");
out->write("(t, t->vm->types, Machine::");
out->write(capitalize(typeName(st)));
out->write("Type)");
@ -1035,8 +1035,8 @@ writeAccessor(Output* out, Object* member, Object* offset, bool unsafe = false)
if (unsafe) {
out->write(" assert(t, true);");
} else {
out->write(" assert(t, t->vm->unsafe or objectClass(o) == 0 or ");
out->write("objectClass(o) == arrayBodyUnsafe");
out->write(" assert(t, t->vm->unsafe or ");
out->write("objectClass(t, o) == arrayBodyUnsafe");
out->write("(t, t->vm->types, Machine::");
out->write(capitalize(::typeName(memberOwner(member))));
out->write("Type)");
@ -1320,7 +1320,8 @@ writeConstructors(Output* out, Object* declarations)
writeOffset(out, typeOffset(o), true);
out->write(");\n");
out->write(" objectClass(o) = arrayBody(t, t->vm->types, Machine::");
out->write(" cast<object>(o, 0) ");
out->write("= arrayBody(t, t->vm->types, Machine::");
out->write(capitalize(typeName(o)));
out->write("Type);\n");
@ -1538,7 +1539,7 @@ writeInitializations(Output* out, Object* declarations)
out->write("t->vm->types = allocate(t, pad((");
out->write(count);
out->write(" * sizeof(void*)) + 4 + sizeof(void*)));\n");
out->write("objectClass(t->vm->types) = 0;\n");
out->write("cast<object>(t->vm->types, 0) = 0;\n");
out->write("arrayLength(t, t->vm->types) = ");
out->write(count);
out->write(";\n");

View File

@ -33,12 +33,6 @@ void set(Thread*, object&, object);
object makeString(Thread*, const char*, ...);
object makeByteArray(Thread*, const char*, ...);
object&
objectClass(object o)
{
return cast<object>(o, 0);
}
enum FieldCode {
VoidField,
ByteField,
@ -163,6 +157,12 @@ class Thread : public JNIEnv {
object heap[HeapSizeInWords];
};
inline object
objectClass(Thread*, object o)
{
return mask(cast<object>(o, 0));
}
#include "type-declarations.cpp"
#include "type-constructors.cpp"
@ -489,8 +489,8 @@ collect(Machine* m, Heap::CollectionType type)
virtual unsigned sizeInWords(void* p) {
Thread* t = m->rootThread;
p = m->heap->follow(p);
object class_ = m->heap->follow(objectClass(p));
p = m->heap->follow(mask(p));
object class_ = m->heap->follow(objectClass(t, p));
unsigned n = divide(classFixedSize(t, class_), BytesPerWord);
@ -505,8 +505,8 @@ collect(Machine* m, Heap::CollectionType type)
virtual void walk(void* p, Heap::Walker* w) {
Thread* t = m->rootThread;
p = m->heap->follow(p);
object class_ = m->heap->follow(objectClass(p));
p = m->heap->follow(mask(p));
object class_ = m->heap->follow(objectClass(t, p));
object objectMask = m->heap->follow(classObjectMask(t, class_));
if (objectMask) {
@ -599,7 +599,7 @@ enter(Thread* t, Thread::State s)
t->vm->exclusive = t;
while (t->vm->activeCount > 1) {
t->vm->stateLock->wait(t);
t->vm->stateLock->wait(t, 0);
}
} break;
@ -639,7 +639,7 @@ enter(Thread* t, Thread::State s)
case Thread::NoState:
case Thread::IdleState: {
while (t->vm->exclusive) {
t->vm->stateLock->wait(t);
t->vm->stateLock->wait(t, 0);
}
++ t->vm->activeCount;
@ -669,7 +669,7 @@ enter(Thread* t, Thread::State s)
t->state = s;
while (t->vm->liveCount > 1) {
t->vm->stateLock->wait(t);
t->vm->stateLock->wait(t, 0);
}
} break;
@ -762,7 +762,7 @@ make(Thread* t, object class_)
unsigned
objectSize(Thread* t, object o)
{
object class_ = objectClass(o);
object class_ = objectClass(t, o);
unsigned n = divide(classFixedSize(t, class_), BytesPerWord);
@ -921,8 +921,8 @@ makeUnsatisfiedLinkError(Thread* t, object message)
inline bool
isLongOrDouble(Thread* t, object o)
{
return objectClass(o) == arrayBody(t, t->vm->types, Machine::LongType)
or objectClass(o) == arrayBody(t, t->vm->types, Machine::DoubleType);
return objectClass(t, o) == arrayBody(t, t->vm->types, Machine::LongType)
or objectClass(t, o) == arrayBody(t, t->vm->types, Machine::DoubleType);
}
unsigned
@ -1139,10 +1139,10 @@ instanceOf(Thread* t, object class_, object o)
return false;
}
if (objectClass(class_)
if (objectClass(t, class_)
== arrayBody(t, t->vm->types, Machine::InterfaceType))
{
for (object oc = objectClass(o); oc; oc = classSuper(t, oc)) {
for (object oc = objectClass(t, o); oc; oc = classSuper(t, oc)) {
object itable = classInterfaceTable(t, oc);
for (unsigned i = 0; i < arrayLength(t, itable); i += 2) {
if (arrayBody(t, itable, i) == class_) {
@ -1151,7 +1151,7 @@ instanceOf(Thread* t, object class_, object o)
}
}
} else {
for (object oc = objectClass(o); oc; oc = classSuper(t, oc)) {
for (object oc = objectClass(t, o); oc; oc = classSuper(t, oc)) {
if (oc == class_) {
return true;
}
@ -1165,7 +1165,7 @@ object
findInterfaceMethod(Thread* t, object method, object o)
{
object interface = methodClass(t, method);
object itable = classInterfaceTable(t, objectClass(o));
object itable = classInterfaceTable(t, objectClass(t, o));
for (unsigned i = 0; i < arrayLength(t, itable); i += 2) {
if (arrayBody(t, itable, i) == interface) {
return arrayBody(t, arrayBody(t, itable, i + 1),
@ -1185,7 +1185,7 @@ findMethod(Thread* t, object method, object class_)
inline object
findVirtualMethod(Thread* t, object method, object o)
{
return findMethod(t, method, objectClass(o));
return findMethod(t, method, objectClass(t, o));
}
bool
@ -1336,7 +1336,8 @@ parsePool(Thread* t, Stream& s)
for (unsigned i = 0; i < poolCount; ++i) {
object o = arrayBody(t, pool, i);
if (objectClass(o) == arrayBody(t, t->vm->types, Machine::IntArrayType)) {
if (objectClass(t, o) == arrayBody(t, t->vm->types, Machine::IntArrayType))
{
switch (intArrayBody(t, o, 0)) {
case CONSTANT_Class: {
set(t, arrayBody(t, pool, i),
@ -1361,7 +1362,8 @@ parsePool(Thread* t, Stream& s)
for (unsigned i = 0; i < poolCount; ++i) {
object o = arrayBody(t, pool, i);
if (objectClass(o) == arrayBody(t, t->vm->types, Machine::IntArrayType)) {
if (objectClass(t, o) == arrayBody(t, t->vm->types, Machine::IntArrayType))
{
switch (intArrayBody(t, o, 0)) {
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
@ -2064,7 +2066,8 @@ inline object
resolveClass(Thread* t, object pool, unsigned index)
{
object o = arrayBody(t, pool, index);
if (objectClass(o) == arrayBody(t, t->vm->types, Machine::ByteArrayType)) {
if (objectClass(t, o) == arrayBody(t, t->vm->types, Machine::ByteArrayType))
{
PROTECT(t, pool);
o = resolveClass(t, o);
@ -2079,7 +2082,8 @@ inline object
resolveClass(Thread* t, object container, object& (*class_)(Thread*, object))
{
object o = class_(t, container);
if (objectClass(o) == arrayBody(t, t->vm->types, Machine::ByteArrayType)) {
if (objectClass(t, o) == arrayBody(t, t->vm->types, Machine::ByteArrayType))
{
PROTECT(t, container);
o = resolveClass(t, o);
@ -2095,7 +2099,8 @@ resolve(Thread* t, object pool, unsigned index,
object (*find)(Thread*, object, object))
{
object o = arrayBody(t, pool, index);
if (objectClass(o) == arrayBody(t, t->vm->types, Machine::ReferenceType)) {
if (objectClass(t, o) == arrayBody(t, t->vm->types, Machine::ReferenceType))
{
PROTECT(t, pool);
object class_ = resolveClass(t, o, referenceClass);
@ -2179,7 +2184,7 @@ makeNativeMethodData(Thread* t, object method, void* function, bool builtin)
inline object
resolveNativeMethodData(Thread* t, object method)
{
if (objectClass(methodCode(t, method))
if (objectClass(t, methodCode(t, method))
== arrayBody(t, t->vm->types, Machine::ByteArrayType))
{
object data = 0;
@ -2333,7 +2338,7 @@ toString(JNIEnv* e, jobject this_)
object s = makeString
(t, "%s@%p",
&byteArrayBody(t, className(t, objectClass(*this_)), 0),
&byteArrayBody(t, className(t, objectClass(t, *this_)), 0),
*this_);
pushSafe(t, s);
@ -2493,13 +2498,13 @@ Thread::Thread(Machine* m):
#include "type-initializations.cpp"
object arrayClass = arrayBody(t, t->vm->types, Machine::ArrayType);
set(t, objectClass(t->vm->types), arrayClass);
set(t, cast<object>(t->vm->types, 0), arrayClass);
object classClass = arrayBody(t, m->types, Machine::ClassType);
set(t, objectClass(classClass), classClass);
set(t, cast<object>(classClass, 0), classClass);
object intArrayClass = arrayBody(t, m->types, Machine::IntArrayType);
set(t, objectClass(intArrayClass), classClass);
set(t, cast<object>(intArrayClass, 0), classClass);
m->unsafe = false;
@ -2668,7 +2673,7 @@ run(Thread* t)
case arraylength: {
object array = pop(t);
if (LIKELY(array)) {
if (objectClass(array)
if (objectClass(t, array)
== arrayBody(t, t->vm->types, Machine::ObjectArrayType))
{
push(t, makeInt(t, objectArrayLength(t, array)));
@ -2832,7 +2837,7 @@ run(Thread* t)
if (not instanceOf(t, class_, stack[sp - 1])) {
object message = makeString
(t, "%s as %s",
&byteArrayBody(t, className(t, objectClass(stack[sp - 1])), 0),
&byteArrayBody(t, className(t, objectClass(t, stack[sp - 1])), 0),
&byteArrayBody(t, className(t, class_), 0));
exception = makeClassCastException(t, message);
goto throw_;
@ -3639,6 +3644,26 @@ run(Thread* t)
push(t, makeLong(t, longValue(t, a) ^ longValue(t, b)));
} goto loop;
// case monitorenter: {
// object o = pop(t);
// if (LIKELY(o)) {
// objectMonitor(t, o)->acquire(t);
// } else {
// exception = makeNullPointerException(t);
// goto throw_;
// }
// } goto loop;
// case monitorexit: {
// object o = pop(t);
// if (LIKELY(o)) {
// objectMonitor(t, o)->release(t);
// } else {
// exception = makeNullPointerException(t);
// goto throw_;
// }
// } goto loop;
case new_: {
uint8_t index1 = codeBody(t, code, ip++);
uint8_t index2 = codeBody(t, code, ip++);
@ -3948,7 +3973,7 @@ run(Thread* t)
arrayBody(t, codePool(t, code), exceptionHandlerCatchType(eh) - 1);
if (catchType == 0 or
(objectClass(catchType)
(objectClass(t, catchType)
== arrayBody(t, t->vm->types, Machine::ClassType) and
instanceOf(t, catchType, exception)))
{
@ -3970,7 +3995,7 @@ run(Thread* t)
}
fprintf(stderr, "%s", &byteArrayBody
(t, className(t, objectClass(exception)), 0));
(t, className(t, objectClass(t, exception)), 0));
if (throwableMessage(t, exception)) {
object m = throwableMessage(t, exception);
@ -3994,17 +4019,17 @@ run(Thread* t)
int line = lineNumber
(t, stackTraceElementMethod(t, e), stackTraceElementIp(t, e));
fprintf(stderr, " at %s.%s", class_, method);
fprintf(stderr, " at %s.%s ", class_, method);
switch (line) {
case NativeLine:
fprintf(stderr, "(native)\n");
break;
case UnknownLine:
fprintf(stderr, "(unknown)\n");
fprintf(stderr, "(unknown line)\n");
break;
default:
fprintf(stderr, "(%d)\n", line);
fprintf(stderr, "(line %d)\n", line);
}
}
}