mirror of
https://github.com/corda/corda.git
synced 2025-03-17 17:45:17 +00:00
rework VM exception handling; throw OOMEs when appropriate
This rather large commit modifies the VM to use non-local returns to throw exceptions instead of simply setting Thread::exception and returning frame-by-frame as it used to. This has several benefits: * Functions no longer need to check Thread::exception after each call which might throw an exception (which would be especially tedious and error-prone now that any function which allocates objects directly or indirectly might throw an OutOfMemoryError) * There's no need to audit the code for calls to functions which previously did not throw exceptions but later do * Performance should be improved slightly due to both the reduced need for conditionals and because undwinding now occurs in a single jump instead of a series of returns The main disadvantages are: * Slightly higher overhead for entering and leaving the VM via the JNI and JDK methods * Non-local returns can make the code harder to read * We must be careful to register destructors for stack-allocated resources with the Thread so they can be called prior to a non-local return The non-local return implementation is similar to setjmp/longjmp, except it uses continuation-passing style to avoid the need for cooperation from the C/C++ compiler. Native C++ exceptions would have also been an option, but that would introduce a dependence on libstdc++, which we're trying to avoid for portability reasons. Finally, this commit ensures that the VM throws an OutOfMemoryError instead of aborting when it reaches its memory ceiling. Currently, we treat the ceiling as a soft limit and temporarily exceed it as necessary to allow garbage collection and certain internal allocations to succeed, but refuse to allocate any Java objects until the heap size drops back below the ceiling.
This commit is contained in:
parent
5da8b96931
commit
afabe8e07e
@ -92,8 +92,6 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code,
|
||||
(t, root(t, Machine::BootLoader),
|
||||
makeByteArray(t, "%.*s", nameSize - 6, name), true);
|
||||
|
||||
if (t->exception) return 0;
|
||||
|
||||
PROTECT(t, c);
|
||||
|
||||
if (classMethodTable(t, c)) {
|
||||
@ -138,8 +136,6 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code,
|
||||
if (objectClass(t, o) == type(t, Machine::ReferenceType)) {
|
||||
o = resolveClass
|
||||
(t, root(t, Machine::BootLoader), referenceName(t, o));
|
||||
|
||||
if (t->exception) return 0;
|
||||
|
||||
set(t, addendumPool(t, addendum),
|
||||
SingletonBody + (index * BytesPerWord), o);
|
||||
@ -360,9 +356,9 @@ offset(object a, uintptr_t* b)
|
||||
}
|
||||
|
||||
void
|
||||
writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code,
|
||||
unsigned codeCapacity, const char* className,
|
||||
const char* methodName, const char* methodSpec)
|
||||
writeBootImage2(Thread* t, FILE* out, BootImage* image, uint8_t* code,
|
||||
unsigned codeCapacity, const char* className,
|
||||
const char* methodName, const char* methodSpec)
|
||||
{
|
||||
Zone zone(t->m->system, t->m->heap, 64 * 1024);
|
||||
|
||||
@ -373,8 +369,6 @@ writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code,
|
||||
object constants = makeCodeImage
|
||||
(t, &zone, image, code, codeMap, className, methodName, methodSpec);
|
||||
|
||||
if (t->exception) return;
|
||||
|
||||
PROTECT(t, constants);
|
||||
|
||||
// this map will not be used when the bootimage is loaded, so
|
||||
@ -505,6 +499,23 @@ writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code,
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
writeBootImage(Thread* t, uintptr_t* arguments)
|
||||
{
|
||||
FILE* out = reinterpret_cast<FILE*>(arguments[0]);
|
||||
BootImage* image = reinterpret_cast<BootImage*>(arguments[1]);
|
||||
uint8_t* code = reinterpret_cast<uint8_t*>(arguments[2]);
|
||||
unsigned codeCapacity = arguments[3];
|
||||
const char* className = reinterpret_cast<const char*>(arguments[4]);
|
||||
const char* methodName = reinterpret_cast<const char*>(arguments[5]);
|
||||
const char* methodSpec = reinterpret_cast<const char*>(arguments[6]);
|
||||
|
||||
writeBootImage2
|
||||
(t, out, image, code, codeCapacity, className, methodName, methodSpec);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int
|
||||
@ -540,15 +551,22 @@ main(int ac, const char** av)
|
||||
return -1;
|
||||
}
|
||||
|
||||
writeBootImage
|
||||
(t, output, &image, code, CodeCapacity,
|
||||
(ac > 3 ? av[3] : 0), (ac > 4 ? av[4] : 0), (ac > 5 ? av[5] : 0));
|
||||
uintptr_t arguments[] = { reinterpret_cast<uintptr_t>(output),
|
||||
reinterpret_cast<uintptr_t>(&image),
|
||||
reinterpret_cast<uintptr_t>(code),
|
||||
CodeCapacity,
|
||||
reinterpret_cast<uintptr_t>(ac > 3 ? av[3] : 0),
|
||||
reinterpret_cast<uintptr_t>(ac > 4 ? av[4] : 0),
|
||||
reinterpret_cast<uintptr_t>(ac > 5 ? av[5] : 0) };
|
||||
|
||||
run(t, writeBootImage, arguments);
|
||||
|
||||
fclose(output);
|
||||
|
||||
if (t->exception) {
|
||||
printTrace(t, t->exception);
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -33,15 +33,9 @@ search(Thread* t, object loader, object name,
|
||||
replace('.', '/', s);
|
||||
}
|
||||
|
||||
object r = op(t, loader, n);
|
||||
if (t->exception) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return reinterpret_cast<int64_t>(r);
|
||||
return reinterpret_cast<int64_t>(op(t, loader, n));
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
return 0;
|
||||
throwNew(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +75,7 @@ Avian_avian_SystemClassLoader_resourceExists
|
||||
object name = reinterpret_cast<object>(arguments[1]);
|
||||
|
||||
if (LIKELY(name)) {
|
||||
RUNTIME_ARRAY(char, n, stringLength(t, name) + 1);
|
||||
THREAD_RUNTIME_ARRAY(t, char, n, stringLength(t, name) + 1);
|
||||
stringChars(t, name, RUNTIME_ARRAY_BODY(n));
|
||||
|
||||
unsigned length;
|
||||
@ -92,8 +86,7 @@ Avian_avian_SystemClassLoader_resourceExists
|
||||
|
||||
return r;
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
return 0;
|
||||
throwNew(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,17 +107,16 @@ Avian_avian_Machine_dumpHeap
|
||||
object outputFile = reinterpret_cast<object>(*arguments);
|
||||
|
||||
unsigned length = stringLength(t, outputFile);
|
||||
char n[length + 1];
|
||||
stringChars(t, outputFile, n);
|
||||
FILE* out = vm::fopen(n, "wb");
|
||||
THREAD_RUNTIME_ARRAY(t, char, n, length + 1);
|
||||
stringChars(t, outputFile, RUNTIME_ARRAY_BODY(n));
|
||||
FILE* out = vm::fopen(RUNTIME_ARRAY_BODY(n), "wb");
|
||||
if (out) {
|
||||
{ ENTER(t, Thread::ExclusiveState);
|
||||
dumpHeap(t, out);
|
||||
}
|
||||
fclose(out);
|
||||
} else {
|
||||
object message = makeString(t, "file not found: %s", n);
|
||||
t->exception = makeThrowable(t, Machine::RuntimeExceptionType, message);
|
||||
throwNew(t, Machine::RuntimeExceptionType, "file not found: %s", n);
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +138,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_getContentLength
|
||||
object path = reinterpret_cast<object>(*arguments);
|
||||
|
||||
if (LIKELY(path)) {
|
||||
RUNTIME_ARRAY(char, p, stringLength(t, path) + 1);
|
||||
THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 1);
|
||||
stringChars(t, path, RUNTIME_ARRAY_BODY(p));
|
||||
|
||||
System::Region* r = t->m->bootFinder->find(RUNTIME_ARRAY_BODY(p));
|
||||
@ -170,7 +162,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_open
|
||||
object path = reinterpret_cast<object>(*arguments);
|
||||
|
||||
if (LIKELY(path)) {
|
||||
RUNTIME_ARRAY(char, p, stringLength(t, path) + 1);
|
||||
THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 1);
|
||||
stringChars(t, path, RUNTIME_ARRAY_BODY(p));
|
||||
|
||||
System::Region* r = t->m->bootFinder->find(RUNTIME_ARRAY_BODY(p));
|
||||
@ -180,8 +172,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_open
|
||||
|
||||
return reinterpret_cast<int64_t>(r);
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
return 0;
|
||||
throwNew(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,9 +60,7 @@ class MyClasspath : public Classpath {
|
||||
(t, root(t, Machine::BootLoader), "java/lang/Thread", "run",
|
||||
"(Ljava/lang/Thread;)V");
|
||||
|
||||
if (t->exception == 0) {
|
||||
t->m->processor->invoke(t, method, 0, t->javaThread);
|
||||
}
|
||||
t->m->processor->invoke(t, method, 0, t->javaThread);
|
||||
}
|
||||
|
||||
virtual void
|
||||
@ -134,9 +132,8 @@ extern "C" JNIEXPORT int64_t JNICALL
|
||||
Avian_java_lang_Object_getVMClass
|
||||
(Thread* t, object, uintptr_t* arguments)
|
||||
{
|
||||
object o = reinterpret_cast<object>(arguments[0]);
|
||||
|
||||
return reinterpret_cast<int64_t>(objectClass(t, o));
|
||||
return reinterpret_cast<int64_t>
|
||||
(objectClass(t, reinterpret_cast<object>(arguments[0])));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
@ -307,12 +304,16 @@ Avian_java_lang_reflect_Method_invoke
|
||||
object instance = reinterpret_cast<object>(arguments[1]);
|
||||
object args = reinterpret_cast<object>(arguments[2]);
|
||||
|
||||
object v = t->m->processor->invokeArray(t, method, instance, args);
|
||||
if (t->exception) {
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::InvocationTargetExceptionType, 0, 0, t->exception);
|
||||
}
|
||||
return reinterpret_cast<int64_t>(v);
|
||||
THREAD_RESOURCE0(t, {
|
||||
if (t->exception) {
|
||||
object exception = t->exception;
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::InvocationTargetExceptionType, 0, 0, exception);
|
||||
}
|
||||
});
|
||||
|
||||
return reinterpret_cast<int64_t>
|
||||
(t->m->processor->invokeArray(t, method, instance, args));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT int64_t JNICALL
|
||||
@ -327,12 +328,11 @@ Avian_java_lang_reflect_Array_getLength
|
||||
if (LIKELY(elementSize)) {
|
||||
return cast<uintptr_t>(array, BytesPerWord);
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::IllegalArgumentExceptionType);
|
||||
throwNew(t, Machine::IllegalArgumentExceptionType);
|
||||
}
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
throwNew(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT int64_t JNICALL
|
||||
@ -394,7 +394,7 @@ Avian_java_lang_System_getVMProperty
|
||||
PROTECT(t, found);
|
||||
|
||||
unsigned length = stringLength(t, name);
|
||||
RUNTIME_ARRAY(char, n, length + 1);
|
||||
THREAD_RUNTIME_ARRAY(t, char, n, length + 1);
|
||||
stringChars(t, name, RUNTIME_ARRAY_BODY(n));
|
||||
|
||||
int64_t r = 0;
|
||||
@ -439,8 +439,7 @@ Avian_java_lang_System_identityHashCode
|
||||
if (LIKELY(o)) {
|
||||
return objectHash(t, o);
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
return 0;
|
||||
throwNew(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,7 +451,7 @@ Avian_java_lang_Runtime_load
|
||||
bool mapName = arguments[1];
|
||||
|
||||
unsigned length = stringLength(t, name);
|
||||
RUNTIME_ARRAY(char, n, length + 1);
|
||||
THREAD_RUNTIME_ARRAY(t, char, n, length + 1);
|
||||
stringChars(t, name, RUNTIME_ARRAY_BODY(n));
|
||||
|
||||
loadLibrary(t, "", RUNTIME_ARRAY_BODY(n), mapName, true);
|
||||
@ -622,11 +621,13 @@ Avian_avian_Classes_defineVMClass
|
||||
|
||||
uint8_t* buffer = static_cast<uint8_t*>
|
||||
(t->m->heap->allocate(length));
|
||||
memcpy(buffer, &byteArrayBody(t, b, offset), length);
|
||||
object c = defineClass(t, loader, buffer, length);
|
||||
t->m->heap->free(buffer, length);
|
||||
|
||||
THREAD_RESOURCE2(t, uint8_t*, buffer, int, length,
|
||||
t->m->heap->free(buffer, length));
|
||||
|
||||
return reinterpret_cast<int64_t>(c);
|
||||
memcpy(buffer, &byteArrayBody(t, b, offset), length);
|
||||
|
||||
return reinterpret_cast<int64_t>(defineClass(t, loader, buffer, length));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
@ -648,8 +649,7 @@ Avian_avian_Classes_isAssignableFrom
|
||||
if (LIKELY(that)) {
|
||||
return vm::isAssignableFrom(t, this_, that);
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
return 0;
|
||||
throwNew(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,9 +101,7 @@ arrayCopy(Thread* t, object src, int32_t srcOffset, object dst,
|
||||
|
||||
return;
|
||||
} else {
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::IndexOutOfBoundsExceptionType);
|
||||
return;
|
||||
throwNew(t, Machine::IndexOutOfBoundsExceptionType);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
@ -111,11 +109,11 @@ arrayCopy(Thread* t, object src, int32_t srcOffset, object dst,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
throwNew(t, Machine::NullPointerExceptionType);
|
||||
return;
|
||||
}
|
||||
|
||||
t->exception = makeThrowable(t, Machine::ArrayStoreExceptionType);
|
||||
throwNew(t, Machine::ArrayStoreExceptionType);
|
||||
}
|
||||
|
||||
void
|
||||
@ -158,6 +156,7 @@ loadLibrary(Thread* t, const char* path, const char* name, bool mapName,
|
||||
{
|
||||
ACQUIRE(t, t->m->classLock);
|
||||
|
||||
char* mappedName;
|
||||
unsigned nameLength = strlen(name);
|
||||
if (mapName) {
|
||||
const char* builtins = findProperty(t, "avian.builtins");
|
||||
@ -186,15 +185,22 @@ loadLibrary(Thread* t, const char* path, const char* name, bool mapName,
|
||||
const char* suffix = t->m->system->librarySuffix();
|
||||
unsigned mappedNameLength = nameLength + strlen(prefix) + strlen(suffix);
|
||||
|
||||
char* mappedName = static_cast<char*>
|
||||
mappedName = static_cast<char*>
|
||||
(t->m->heap->allocate(mappedNameLength + 1));
|
||||
|
||||
snprintf(mappedName, mappedNameLength + 1, "%s%s%s", prefix, name, suffix);
|
||||
|
||||
name = mappedName;
|
||||
nameLength = mappedNameLength;
|
||||
} else {
|
||||
mappedName = 0;
|
||||
}
|
||||
|
||||
THREAD_RESOURCE2
|
||||
(t, char*, mappedName, unsigned, nameLength, if (mappedName) {
|
||||
t->m->heap->free(mappedName, nameLength + 1);
|
||||
});
|
||||
|
||||
System::Library* lib = 0;
|
||||
for (Tokenizer tokenizer(path, t->m->system->pathSeparator());
|
||||
tokenizer.hasMore();)
|
||||
@ -202,7 +208,7 @@ loadLibrary(Thread* t, const char* path, const char* name, bool mapName,
|
||||
Tokenizer::Token token(tokenizer.next());
|
||||
|
||||
unsigned fullNameLength = token.length + 1 + nameLength;
|
||||
RUNTIME_ARRAY(char, fullName, fullNameLength + 1);
|
||||
THREAD_RUNTIME_ARRAY(t, char, fullName, fullNameLength + 1);
|
||||
|
||||
snprintf(RUNTIME_ARRAY_BODY(fullName), fullNameLength + 1,
|
||||
"%*s/%s", token.length, token.s, name);
|
||||
@ -220,13 +226,8 @@ loadLibrary(Thread* t, const char* path, const char* name, bool mapName,
|
||||
runOnLoadIfFound(t, lib);
|
||||
}
|
||||
} else {
|
||||
object message = makeString(t, "library not found: %s", name);
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::UnsatisfiedLinkErrorType, message);
|
||||
}
|
||||
|
||||
if (mapName) {
|
||||
t->m->heap->free(name, nameLength + 1);
|
||||
throwNew(t, Machine::UnsatisfiedLinkErrorType, "library not found: %s",
|
||||
name);
|
||||
}
|
||||
|
||||
return lib;
|
||||
@ -264,7 +265,7 @@ makeStackTraceElement(Thread* t, object e)
|
||||
object class_ = className(t, methodClass(t, traceElementMethod(t, e)));
|
||||
PROTECT(t, class_);
|
||||
|
||||
RUNTIME_ARRAY(char, s, byteArrayLength(t, class_));
|
||||
THREAD_RUNTIME_ARRAY(t, char, s, byteArrayLength(t, class_));
|
||||
replace('/', '.', RUNTIME_ARRAY_BODY(s),
|
||||
reinterpret_cast<char*>(&byteArrayBody(t, class_, 0)));
|
||||
class_ = makeString(t, "%s", s);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,7 +23,7 @@
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define THREAD_STACK 2216
|
||||
#define THREAD_STACK 2232
|
||||
|
||||
#if defined __MINGW32__ || defined __CYGWIN32__
|
||||
|
||||
@ -306,7 +306,7 @@ LOCAL(vmJumpAndInvoke_argumentTest):
|
||||
|
||||
#elif defined __i386__
|
||||
|
||||
#define THREAD_STACK 2144
|
||||
#define THREAD_STACK 2152
|
||||
|
||||
#define CALLEE_SAVED_REGISTER_FOOTPRINT 16
|
||||
|
||||
|
560
src/compile.cpp
560
src/compile.cpp
File diff suppressed because it is too large
Load Diff
@ -10,11 +10,11 @@
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define THREAD_CONTINUATION 2224
|
||||
#define THREAD_CONTINUATION 2240
|
||||
#define THREAD_EXCEPTION 80
|
||||
#define THREAD_EXCEPTION_STACK_ADJUSTMENT 2232
|
||||
#define THREAD_EXCEPTION_OFFSET 2240
|
||||
#define THREAD_EXCEPTION_HANDLER 2248
|
||||
#define THREAD_EXCEPTION_STACK_ADJUSTMENT 2248
|
||||
#define THREAD_EXCEPTION_OFFSET 2256
|
||||
#define THREAD_EXCEPTION_HANDLER 2264
|
||||
|
||||
#define CONTINUATION_NEXT 8
|
||||
#define CONTINUATION_ADDRESS 32
|
||||
@ -89,11 +89,11 @@ LOCAL(vmInvoke_exit):
|
||||
|
||||
#elif defined __i386__
|
||||
|
||||
#define THREAD_CONTINUATION 2148
|
||||
#define THREAD_CONTINUATION 2156
|
||||
#define THREAD_EXCEPTION 44
|
||||
#define THREAD_EXCEPTION_STACK_ADJUSTMENT 2152
|
||||
#define THREAD_EXCEPTION_OFFSET 2156
|
||||
#define THREAD_EXCEPTION_HANDLER 2160
|
||||
#define THREAD_EXCEPTION_STACK_ADJUSTMENT 2160
|
||||
#define THREAD_EXCEPTION_OFFSET 2164
|
||||
#define THREAD_EXCEPTION_HANDLER 2168
|
||||
|
||||
#define CONTINUATION_NEXT 4
|
||||
#define CONTINUATION_ADDRESS 16
|
||||
|
31
src/heap.cpp
31
src/heap.cpp
@ -68,6 +68,7 @@ void assert(Context*, bool);
|
||||
|
||||
System* system(Context*);
|
||||
void* tryAllocate(Context* c, unsigned size);
|
||||
void* allocate(Context* c, unsigned size);
|
||||
void free(Context* c, const void* p, unsigned size);
|
||||
void outOfMemory(Context*);
|
||||
|
||||
@ -360,7 +361,9 @@ class Segment {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
outOfMemory(context);
|
||||
data = static_cast<uintptr_t*>
|
||||
(local::allocate
|
||||
(context, (footprint(capacity_)) * BytesPerWord));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1710,7 +1713,7 @@ collect(Context* c)
|
||||
}
|
||||
|
||||
void*
|
||||
tryAllocate(Context* c, unsigned size)
|
||||
allocate(Context* c, unsigned size, bool limit)
|
||||
{
|
||||
ACQUIRE(c->lock);
|
||||
|
||||
@ -1718,7 +1721,7 @@ tryAllocate(Context* c, unsigned size)
|
||||
size = pad(size) + 2 * BytesPerWord;
|
||||
}
|
||||
|
||||
if (size + c->count < c->limit) {
|
||||
if ((not limit) or size + c->count < c->limit) {
|
||||
void* p = c->system->tryAllocate(size);
|
||||
if (p) {
|
||||
c->count += size;
|
||||
@ -1735,6 +1738,18 @@ tryAllocate(Context* c, unsigned size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void*
|
||||
tryAllocate(Context* c, unsigned size)
|
||||
{
|
||||
return allocate(c, size, true);
|
||||
}
|
||||
|
||||
void*
|
||||
allocate(Context* c, unsigned size)
|
||||
{
|
||||
return allocate(c, size, false);
|
||||
}
|
||||
|
||||
void
|
||||
free(Context* c, const void* p, unsigned size)
|
||||
{
|
||||
@ -1787,16 +1802,16 @@ class MyHeap: public Heap {
|
||||
c.immortalHeapEnd = start + sizeInWords;
|
||||
}
|
||||
|
||||
virtual bool limitExceeded() {
|
||||
return c.count > c.limit;
|
||||
}
|
||||
|
||||
virtual void* tryAllocate(unsigned size) {
|
||||
return local::tryAllocate(&c, size);
|
||||
}
|
||||
|
||||
virtual void* allocate(unsigned size) {
|
||||
void* p = local::tryAllocate(&c, size);
|
||||
if (p == 0) {
|
||||
c.client->outOfMemory();
|
||||
}
|
||||
return p;
|
||||
return local::allocate(&c, size);
|
||||
}
|
||||
|
||||
virtual void free(const void* p, unsigned size) {
|
||||
|
@ -54,6 +54,7 @@ class Heap: public Allocator {
|
||||
|
||||
virtual void setClient(Client* client) = 0;
|
||||
virtual void setImmortalHeap(uintptr_t* start, unsigned sizeInWords) = 0;
|
||||
virtual bool limitExceeded() = 0;
|
||||
virtual void collect(CollectionType type, unsigned footprint) = 0;
|
||||
virtual void* allocateFixed(Allocator* allocator, unsigned sizeInWords,
|
||||
bool objectMask, unsigned* totalInBytes) = 0;
|
||||
|
@ -375,7 +375,7 @@ popFrame(Thread* t)
|
||||
if (UNLIKELY(methodVmFlags(t, method) & ClassInitFlag)
|
||||
and t->classInitList
|
||||
and t->classInitList->class_ == methodClass(t, method))
|
||||
{
|
||||
{
|
||||
t->classInitList->pop();
|
||||
|
||||
postInitClass(t, methodClass(t, method));
|
||||
@ -435,7 +435,7 @@ checkStack(Thread* t, object method)
|
||||
+ codeMaxStack(t, methodCode(t, method))
|
||||
> StackSizeInWords / 2))
|
||||
{
|
||||
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||
throwNew(t, Machine::StackOverflowErrorType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -570,9 +570,9 @@ invokeNativeSlow(Thread* t, object method, void* function)
|
||||
}
|
||||
unsigned count = methodParameterCount(t, method) + 2;
|
||||
|
||||
RUNTIME_ARRAY(uintptr_t, args, footprint);
|
||||
THREAD_RUNTIME_ARRAY(t, uintptr_t, args, footprint);
|
||||
unsigned argOffset = 0;
|
||||
RUNTIME_ARRAY(uint8_t, types, count);
|
||||
THREAD_RUNTIME_ARRAY(t, uint8_t, types, count);
|
||||
unsigned typeOffset = 0;
|
||||
|
||||
RUNTIME_ARRAY_BODY(args)[argOffset++] = reinterpret_cast<uintptr_t>(t);
|
||||
@ -613,6 +613,10 @@ invokeNativeSlow(Thread* t, object method, void* function)
|
||||
|
||||
{ ENTER(t, Thread::IdleState);
|
||||
|
||||
bool noThrow = t->checkpoint->noThrow;
|
||||
t->checkpoint->noThrow = true;
|
||||
THREAD_RESOURCE(t, bool, noThrow, t->checkpoint->noThrow = noThrow);
|
||||
|
||||
result = t->m->system->call
|
||||
(function,
|
||||
RUNTIME_ARRAY_BODY(args),
|
||||
@ -633,7 +637,9 @@ invokeNativeSlow(Thread* t, object method, void* function)
|
||||
popFrame(t);
|
||||
|
||||
if (UNLIKELY(t->exception)) {
|
||||
return VoidField;
|
||||
object exception = t->exception;
|
||||
t->exception = 0;
|
||||
throw_(t, exception);
|
||||
}
|
||||
|
||||
pushResult(t, returnCode, result, true);
|
||||
@ -648,10 +654,6 @@ invokeNative(Thread* t, object method)
|
||||
|
||||
resolveNative(t, method);
|
||||
|
||||
if (UNLIKELY(t->exception)) {
|
||||
return VoidField;
|
||||
}
|
||||
|
||||
object native = methodRuntimeDataNative(t, getMethodRuntimeData(t, method));
|
||||
if (nativeFast(t, native)) {
|
||||
pushFrame(t, method);
|
||||
@ -673,10 +675,6 @@ invokeNative(Thread* t, object method)
|
||||
|
||||
popFrame(t);
|
||||
|
||||
if (UNLIKELY(t->exception)) {
|
||||
return VoidField;
|
||||
}
|
||||
|
||||
pushResult(t, methodReturnCode(t, method), result, false);
|
||||
|
||||
return methodReturnCode(t, method);
|
||||
@ -802,10 +800,8 @@ pushField(Thread* t, object target, object field)
|
||||
}
|
||||
|
||||
object
|
||||
interpret(Thread* t)
|
||||
interpret3(Thread* t, const int base)
|
||||
{
|
||||
const int base = t->frame;
|
||||
|
||||
unsigned instruction = nop;
|
||||
unsigned& ip = t->ip;
|
||||
unsigned& sp = t->sp;
|
||||
@ -858,10 +854,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushObject(t, objectArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString
|
||||
(t, "%d not in [0,%d)", index, objectArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, objectArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -881,10 +876,9 @@ interpret(Thread* t)
|
||||
{
|
||||
set(t, array, ArrayBody + (index * BytesPerWord), value);
|
||||
} else {
|
||||
object message = makeString
|
||||
(t, "%d not in [0,%d)", index, objectArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, objectArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -924,13 +918,11 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
pushObject(t, makeObjectArray(t, class_, count));
|
||||
} else {
|
||||
object message = makeString(t, "%d", count);
|
||||
exception = makeThrowable
|
||||
(t, Machine::NegativeArraySizeExceptionType, message);
|
||||
(t, Machine::NegativeArraySizeExceptionType, "%d", count);
|
||||
goto throw_;
|
||||
}
|
||||
} goto loop;
|
||||
@ -995,10 +987,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushInt(t, booleanArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
booleanArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType,
|
||||
"%d not in [0,%d)", index, booleanArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1008,11 +999,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushInt(t, byteArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
byteArrayLength(t, array));
|
||||
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType,
|
||||
"%d not in [0,%d)", index, byteArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
}
|
||||
@ -1035,10 +1024,9 @@ interpret(Thread* t)
|
||||
{
|
||||
booleanArrayBody(t, array, index) = value;
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
booleanArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType,
|
||||
"%d not in [0,%d)", index, booleanArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1047,10 +1035,9 @@ interpret(Thread* t)
|
||||
{
|
||||
byteArrayBody(t, array, index) = value;
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
byteArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType,
|
||||
"%d not in [0,%d)", index, byteArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
}
|
||||
@ -1074,10 +1061,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushInt(t, charArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
charArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, charArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1097,10 +1083,9 @@ interpret(Thread* t)
|
||||
{
|
||||
charArrayBody(t, array, index) = value;
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
charArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, charArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1117,13 +1102,11 @@ interpret(Thread* t)
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
if (not instanceOf(t, class_, peekObject(t, sp - 1))) {
|
||||
object message = makeString
|
||||
(t, "%s as %s",
|
||||
exception = makeThrowable
|
||||
(t, Machine::ClassCastExceptionType, "%s as %s",
|
||||
&byteArrayBody
|
||||
(t, className(t, objectClass(t, peekObject(t, sp - 1))), 0),
|
||||
&byteArrayBody(t, className(t, class_), 0));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ClassCastExceptionType, message);
|
||||
goto throw_;
|
||||
}
|
||||
}
|
||||
@ -1158,10 +1141,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushLong(t, doubleArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
doubleArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, doubleArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1181,10 +1163,9 @@ interpret(Thread* t)
|
||||
{
|
||||
memcpy(&doubleArrayBody(t, array, index), &value, sizeof(uint64_t));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
doubleArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, doubleArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1360,10 +1341,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushInt(t, floatArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
floatArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, floatArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1383,10 +1363,9 @@ interpret(Thread* t)
|
||||
{
|
||||
memcpy(&floatArrayBody(t, array, index), &value, sizeof(uint32_t));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
floatArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, floatArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1476,7 +1455,6 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object field = resolveField(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0);
|
||||
|
||||
@ -1511,7 +1489,6 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object field = resolveField(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
assert(t, fieldFlags(t, field) & ACC_STATIC);
|
||||
|
||||
@ -1592,10 +1569,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushInt(t, intArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
intArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, intArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1622,10 +1598,9 @@ interpret(Thread* t)
|
||||
{
|
||||
intArrayBody(t, array, index) = value;
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
intArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, intArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -1874,7 +1849,6 @@ interpret(Thread* t)
|
||||
|
||||
if (peekObject(t, sp - 1)) {
|
||||
object class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
if (instanceOf(t, class_, popObject(t))) {
|
||||
pushInt(t, 1);
|
||||
@ -1893,7 +1867,6 @@ interpret(Thread* t)
|
||||
ip += 2;
|
||||
|
||||
object method = resolveMethod(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
unsigned parameterFootprint = methodParameterFootprint(t, method);
|
||||
if (LIKELY(peekObject(t, sp - parameterFootprint))) {
|
||||
@ -1910,7 +1883,6 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object method = resolveMethod(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
unsigned parameterFootprint = methodParameterFootprint(t, method);
|
||||
if (LIKELY(peekObject(t, sp - parameterFootprint))) {
|
||||
@ -1935,7 +1907,6 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object method = resolveMethod(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
PROTECT(t, method);
|
||||
|
||||
if (UNLIKELY(classInit(t, methodClass(t, method), 3))) goto invoke;
|
||||
@ -1947,7 +1918,6 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object method = resolveMethod(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
unsigned parameterFootprint = methodParameterFootprint(t, method);
|
||||
if (LIKELY(peekObject(t, sp - parameterFootprint))) {
|
||||
@ -2096,10 +2066,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushLong(t, longArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
longArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, longArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -2126,10 +2095,9 @@ interpret(Thread* t)
|
||||
{
|
||||
longArrayBody(t, array, index) = value;
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
longArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, longArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -2170,7 +2138,6 @@ interpret(Thread* t)
|
||||
if (objectClass(t, v) == type(t, Machine::ReferenceType)) {
|
||||
object class_ = resolveClassInPool
|
||||
(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
pushObject(t, getJClass(t, class_));
|
||||
} else if (objectClass(t, v) == type(t, Machine::ClassType)) {
|
||||
@ -2389,16 +2356,14 @@ interpret(Thread* t)
|
||||
uint8_t dimensions = codeBody(t, code, ip++);
|
||||
|
||||
object class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
PROTECT(t, class_);
|
||||
|
||||
int32_t counts[dimensions];
|
||||
for (int i = dimensions - 1; i >= 0; --i) {
|
||||
counts[i] = popInt(t);
|
||||
if (UNLIKELY(counts[i] < 0)) {
|
||||
object message = makeString(t, "%d", counts[i]);
|
||||
exception = makeThrowable
|
||||
(t, Machine::NegativeArraySizeExceptionType, message);
|
||||
(t, Machine::NegativeArraySizeExceptionType, "%d", counts[i]);
|
||||
goto throw_;
|
||||
}
|
||||
}
|
||||
@ -2416,7 +2381,6 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
PROTECT(t, class_);
|
||||
|
||||
if (UNLIKELY(classInit(t, class_, 3))) goto invoke;
|
||||
@ -2470,9 +2434,8 @@ interpret(Thread* t)
|
||||
|
||||
pushObject(t, array);
|
||||
} else {
|
||||
object message = makeString(t, "%d", count);
|
||||
exception = makeThrowable
|
||||
(t, Machine::NegativeArraySizeExceptionType, message);
|
||||
(t, Machine::NegativeArraySizeExceptionType, "%d", count);
|
||||
goto throw_;
|
||||
}
|
||||
} goto loop;
|
||||
@ -2491,7 +2454,6 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object field = resolveField(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0);
|
||||
PROTECT(t, field);
|
||||
@ -2582,7 +2544,6 @@ interpret(Thread* t)
|
||||
uint16_t index = codeReadInt16(t, code, ip);
|
||||
|
||||
object field = resolveField(t, frameMethod(t, frame), index - 1);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
assert(t, fieldFlags(t, field) & ACC_STATIC);
|
||||
|
||||
@ -2683,10 +2644,9 @@ interpret(Thread* t)
|
||||
{
|
||||
pushInt(t, shortArrayBody(t, array, index));
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
shortArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, shortArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -2706,10 +2666,9 @@ interpret(Thread* t)
|
||||
{
|
||||
shortArrayBody(t, array, index) = value;
|
||||
} else {
|
||||
object message = makeString(t, "%d not in [0,%d)", index,
|
||||
shortArrayLength(t, array));
|
||||
exception = makeThrowable
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, message);
|
||||
(t, Machine::ArrayIndexOutOfBoundsExceptionType, "%d not in [0,%d)",
|
||||
index, shortArrayLength(t, array));
|
||||
goto throw_;
|
||||
}
|
||||
} else {
|
||||
@ -2771,7 +2730,6 @@ interpret(Thread* t)
|
||||
|
||||
resolveClass(t, classLoader(t, methodClass(t, frameMethod(t, frame))),
|
||||
className(t, class_));
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
ip -= 3;
|
||||
} goto loop;
|
||||
@ -2822,11 +2780,8 @@ interpret(Thread* t)
|
||||
invoke: {
|
||||
if (methodFlags(t, code) & ACC_NATIVE) {
|
||||
invokeNative(t, code);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
} else {
|
||||
checkStack(t, code);
|
||||
if (UNLIKELY(exception)) goto throw_;
|
||||
|
||||
pushFrame(t, code);
|
||||
}
|
||||
} goto loop;
|
||||
@ -2851,6 +2806,39 @@ interpret(Thread* t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
interpret2(vm::Thread* t, uintptr_t* arguments)
|
||||
{
|
||||
int base = arguments[0];
|
||||
bool* success = reinterpret_cast<bool*>(arguments[1]);
|
||||
|
||||
object r = interpret3(static_cast<Thread*>(t), base);
|
||||
*success = true;
|
||||
return reinterpret_cast<uint64_t>(r);
|
||||
}
|
||||
|
||||
object
|
||||
interpret(Thread* t)
|
||||
{
|
||||
const int base = t->frame;
|
||||
|
||||
while (true) {
|
||||
bool success = false;
|
||||
uintptr_t arguments[] = { base, reinterpret_cast<uintptr_t>(&success) };
|
||||
|
||||
uint64_t r = run(t, interpret2, arguments);
|
||||
if (success) {
|
||||
if (t->exception) {
|
||||
object exception = t->exception;
|
||||
t->exception = 0;
|
||||
throw_(t, exception);
|
||||
} else {
|
||||
return reinterpret_cast<object>(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pushArguments(Thread* t, object this_, const char* spec, bool indirectObjects,
|
||||
va_list a)
|
||||
@ -2957,47 +2945,45 @@ invoke(Thread* t, object method)
|
||||
if (methodFlags(t, method) & ACC_NATIVE) {
|
||||
unsigned returnCode = invokeNative(t, method);
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
switch (returnCode) {
|
||||
case ByteField:
|
||||
case BooleanField:
|
||||
case CharField:
|
||||
case ShortField:
|
||||
case FloatField:
|
||||
case IntField:
|
||||
result = makeInt(t, popInt(t));
|
||||
break;
|
||||
switch (returnCode) {
|
||||
case ByteField:
|
||||
case BooleanField:
|
||||
case CharField:
|
||||
case ShortField:
|
||||
case FloatField:
|
||||
case IntField:
|
||||
result = makeInt(t, popInt(t));
|
||||
break;
|
||||
|
||||
case LongField:
|
||||
case DoubleField:
|
||||
result = makeLong(t, popLong(t));
|
||||
break;
|
||||
case LongField:
|
||||
case DoubleField:
|
||||
result = makeLong(t, popLong(t));
|
||||
break;
|
||||
|
||||
case ObjectField:
|
||||
result = popObject(t);
|
||||
break;
|
||||
case ObjectField:
|
||||
result = popObject(t);
|
||||
break;
|
||||
|
||||
case VoidField:
|
||||
result = 0;
|
||||
break;
|
||||
case VoidField:
|
||||
result = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
abort(t);
|
||||
};
|
||||
}
|
||||
default:
|
||||
abort(t);
|
||||
};
|
||||
} else {
|
||||
checkStack(t, method);
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
pushFrame(t, method);
|
||||
result = interpret(t);
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
popFrame(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
pushFrame(t, method);
|
||||
|
||||
if (UNLIKELY(t->exception)) {
|
||||
return 0;
|
||||
result = interpret(t);
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
popFrame(t);
|
||||
} else {
|
||||
object exception = t->exception;
|
||||
t->exception = 0;
|
||||
throw_(t, exception);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -3155,8 +3141,7 @@ class MyProcessor: public Processor {
|
||||
if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1
|
||||
> StackSizeInWords / 2))
|
||||
{
|
||||
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||
return 0;
|
||||
throwNew(t, Machine::StackOverflowErrorType);
|
||||
}
|
||||
|
||||
const char* spec = reinterpret_cast<char*>
|
||||
@ -3180,8 +3165,7 @@ class MyProcessor: public Processor {
|
||||
if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1
|
||||
> StackSizeInWords / 2))
|
||||
{
|
||||
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||
return 0;
|
||||
throwNew(t, Machine::StackOverflowErrorType);
|
||||
}
|
||||
|
||||
const char* spec = reinterpret_cast<char*>
|
||||
@ -3204,8 +3188,7 @@ class MyProcessor: public Processor {
|
||||
if (UNLIKELY(t->sp + parameterFootprint(vmt, methodSpec, false)
|
||||
> StackSizeInWords / 2))
|
||||
{
|
||||
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||
return 0;
|
||||
throwNew(t, Machine::StackOverflowErrorType);
|
||||
}
|
||||
|
||||
pushArguments(t, this_, methodSpec, false, arguments);
|
||||
@ -3213,13 +3196,9 @@ class MyProcessor: public Processor {
|
||||
object method = resolveMethod
|
||||
(t, loader, className, methodName, methodSpec);
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0));
|
||||
assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0));
|
||||
|
||||
return ::invoke(t, method);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return ::invoke(t, method);
|
||||
}
|
||||
|
||||
virtual object getStackTrace(vm::Thread*, vm::Thread*) {
|
||||
|
633
src/jnienv.cpp
633
src/jnienv.cpp
File diff suppressed because it is too large
Load Diff
371
src/machine.cpp
371
src/machine.cpp
@ -83,7 +83,7 @@ dispose(Thread* t, Thread* o, bool remove)
|
||||
expect(t, find(t->m->rootThread, o));
|
||||
|
||||
unsigned c = count(t->m->rootThread, o);
|
||||
RUNTIME_ARRAY(Thread*, threads, c);
|
||||
THREAD_RUNTIME_ARRAY(t, Thread*, threads, c);
|
||||
fill(t->m->rootThread, o, RUNTIME_ARRAY_BODY(threads));
|
||||
#endif
|
||||
|
||||
@ -578,7 +578,14 @@ postCollect(Thread* t)
|
||||
}
|
||||
|
||||
t->heapOffset = 0;
|
||||
t->heapIndex = 0;
|
||||
|
||||
if (t->m->heap->limitExceeded()) {
|
||||
// if we're out of memory, pretend the thread-local heap is
|
||||
// already full so we don't make things worse:
|
||||
t->heapIndex = ThreadHeapSizeInWords;
|
||||
} else {
|
||||
t->heapIndex = 0;
|
||||
}
|
||||
|
||||
if (t->flags & Thread::UseBackupHeapFlag) {
|
||||
memset(t->backupHeap, 0, ThreadBackupHeapSizeInBytes);
|
||||
@ -592,6 +599,17 @@ postCollect(Thread* t)
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
invoke(Thread* t, uintptr_t* arguments)
|
||||
{
|
||||
object m = reinterpret_cast<object>(arguments[0]);
|
||||
object o = reinterpret_cast<object>(arguments[1]);
|
||||
|
||||
t->m->processor->invoke(t, m, o);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
finalizeObject(Thread* t, object o)
|
||||
{
|
||||
@ -604,7 +622,11 @@ finalizeObject(Thread* t, object o)
|
||||
and vm::strcmp(reinterpret_cast<const int8_t*>("()V"),
|
||||
&byteArrayBody(t, methodSpec(t, m), 0)) == 0)
|
||||
{
|
||||
t->m->processor->invoke(t, m, o);
|
||||
uintptr_t arguments[] = { reinterpret_cast<uintptr_t>(m),
|
||||
reinterpret_cast<uintptr_t>(o) };
|
||||
|
||||
run(t, invoke, arguments);
|
||||
|
||||
t->exception = 0;
|
||||
return;
|
||||
}
|
||||
@ -613,21 +635,6 @@ finalizeObject(Thread* t, object o)
|
||||
abort(t);
|
||||
}
|
||||
|
||||
object
|
||||
makeByteArray(Thread* t, const char* format, va_list a)
|
||||
{
|
||||
const int Size = 256;
|
||||
char buffer[Size];
|
||||
|
||||
int r = vm::vsnprintf(buffer, Size - 1, format, a);
|
||||
expect(t, r >= 0 and r < Size - 1);
|
||||
|
||||
object s = makeByteArray(t, strlen(buffer) + 1);
|
||||
memcpy(&byteArrayBody(t, s, 0), buffer, byteArrayLength(t, s));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned
|
||||
readByte(Stream& s, unsigned* value)
|
||||
{
|
||||
@ -858,6 +865,9 @@ parsePool(Thread* t, Stream& s)
|
||||
if (count) {
|
||||
uint32_t* index = static_cast<uint32_t*>(t->m->heap->allocate(count * 4));
|
||||
|
||||
THREAD_RESOURCE2(t, uint32_t*, index, unsigned, count,
|
||||
t->m->heap->free(index, count * 4));
|
||||
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
index[i] = s.position();
|
||||
|
||||
@ -889,6 +899,7 @@ parsePool(Thread* t, Stream& s)
|
||||
s.skip(8);
|
||||
++ i;
|
||||
break;
|
||||
|
||||
case CONSTANT_Double:
|
||||
singletonSetBit(t, pool, count, i);
|
||||
singletonSetBit(t, pool, count, i + 1);
|
||||
@ -911,8 +922,6 @@ parsePool(Thread* t, Stream& s)
|
||||
i += parsePoolEntry(t, s, index, pool, i);
|
||||
}
|
||||
|
||||
t->m->heap->free(index, count * 4);
|
||||
|
||||
s.setPosition(end);
|
||||
}
|
||||
|
||||
@ -960,7 +969,6 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool)
|
||||
PROTECT(t, name);
|
||||
|
||||
object interface = resolveClass(t, classLoader(t, class_), name);
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
|
||||
PROTECT(t, interface);
|
||||
|
||||
@ -981,7 +989,6 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool)
|
||||
unsigned i = 0;
|
||||
for (HashMapIterator it(t, map); it.hasMore();) {
|
||||
object interface = tripleSecond(t, it.next());
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
|
||||
set(t, interfaceTable, ArrayBody + (i * BytesPerWord), interface);
|
||||
++ i;
|
||||
@ -1028,7 +1035,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool)
|
||||
object addendum = 0;
|
||||
PROTECT(t, addendum);
|
||||
|
||||
RUNTIME_ARRAY(uint8_t, staticTypes, count);
|
||||
THREAD_RUNTIME_ARRAY(t, uint8_t, staticTypes, count);
|
||||
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
unsigned flags = s.read2();
|
||||
@ -2046,6 +2053,7 @@ boot(Thread* t)
|
||||
setRoot(t, Machine::BootstrapClassMap, makeHashMap(t, 0, 0));
|
||||
|
||||
setRoot(t, Machine::StringMap, makeWeakHashMap(t, 0, 0));
|
||||
|
||||
m->processor->boot(t, 0);
|
||||
|
||||
{ object bootCode = makeCode(t, 0, 0, 0, 0, 0, 0, 1);
|
||||
@ -2162,6 +2170,67 @@ class HeapClient: public Heap::Client {
|
||||
Machine* m;
|
||||
};
|
||||
|
||||
void
|
||||
doCollect(Thread* t, Heap::CollectionType type)
|
||||
{
|
||||
#ifdef VM_STRESS
|
||||
bool stress = (t->flags |= Thread::StressFlag);
|
||||
if (not stress) atomicOr(&(t->flags), Thread::StressFlag);
|
||||
#endif
|
||||
|
||||
Machine* m = t->m;
|
||||
|
||||
m->unsafe = true;
|
||||
m->heap->collect(type, footprint(m->rootThread));
|
||||
m->unsafe = false;
|
||||
|
||||
postCollect(m->rootThread);
|
||||
|
||||
killZombies(t, m->rootThread);
|
||||
|
||||
for (unsigned i = 0; i < m->heapPoolIndex; ++i) {
|
||||
m->heap->free(m->heapPool[i], ThreadHeapSizeInBytes);
|
||||
}
|
||||
m->heapPoolIndex = 0;
|
||||
|
||||
if (m->heap->limitExceeded()) {
|
||||
// if we're out of memory, disallow further allocations of fixed
|
||||
// objects:
|
||||
m->fixedFootprint = FixedFootprintThresholdInBytes;
|
||||
} else {
|
||||
m->fixedFootprint = 0;
|
||||
}
|
||||
|
||||
#ifdef VM_STRESS
|
||||
if (not stress) atomicAnd(&(t->flags), ~Thread::StressFlag);
|
||||
#endif
|
||||
|
||||
object f = t->m->finalizeQueue;
|
||||
t->m->finalizeQueue = 0;
|
||||
for (; f; f = finalizerNext(t, f)) {
|
||||
void (*function)(Thread*, object);
|
||||
memcpy(&function, &finalizerFinalize(t, f), BytesPerWord);
|
||||
if (function) {
|
||||
function(t, finalizerTarget(t, f));
|
||||
} else {
|
||||
setRoot(t, Machine::ObjectsToFinalize, makePair
|
||||
(t, finalizerTarget(t, f), root(t, Machine::ObjectsToFinalize)));
|
||||
}
|
||||
}
|
||||
|
||||
if (root(t, Machine::ObjectsToFinalize) and m->finalizeThread == 0) {
|
||||
m->finalizeThread = m->processor->makeThread
|
||||
(m, root(t, Machine::FinalizerThread), m->rootThread);
|
||||
|
||||
addThread(t, m->finalizeThread);
|
||||
|
||||
if (not startThread(t, m->finalizeThread)) {
|
||||
removeThread(t, m->finalizeThread);
|
||||
m->finalizeThread = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace vm {
|
||||
@ -2663,7 +2732,9 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type,
|
||||
> ThreadHeapSizeInWords)
|
||||
{
|
||||
t->heap = 0;
|
||||
if (t->m->heapPoolIndex < ThreadHeapPoolSize) {
|
||||
if ((not t->m->heap->limitExceeded())
|
||||
and t->m->heapPoolIndex < ThreadHeapPoolSize)
|
||||
{
|
||||
t->heap = static_cast<uintptr_t*>
|
||||
(t->m->heap->tryAllocate(ThreadHeapSizeInBytes));
|
||||
|
||||
@ -2694,6 +2765,10 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type,
|
||||
// vmPrintTrace(t);
|
||||
collect(t, Heap::MinorCollection);
|
||||
}
|
||||
|
||||
if (t->m->heap->limitExceeded()) {
|
||||
throw_(t, root(t, Machine::OutOfMemoryError));
|
||||
}
|
||||
} while (type == Machine::MovableAllocation
|
||||
and t->heapIndex + ceiling(sizeInBytes, BytesPerWord)
|
||||
> ThreadHeapSizeInWords);
|
||||
@ -2759,12 +2834,51 @@ makeNewGeneral(Thread* t, object class_)
|
||||
return instance;
|
||||
}
|
||||
|
||||
void NO_RETURN
|
||||
throw_(Thread* t, object e)
|
||||
{
|
||||
assert(t, t->exception == 0);
|
||||
|
||||
Thread::Checkpoint* checkpoint = t->checkpoint;
|
||||
|
||||
expect(t, not checkpoint->noThrow);
|
||||
|
||||
t->exception = e;
|
||||
|
||||
while (t->resource != checkpoint->resource) {
|
||||
Thread::Resource* r = t->resource;
|
||||
t->resource = r->next;
|
||||
r->release();
|
||||
}
|
||||
|
||||
t->protector = checkpoint->protector;
|
||||
|
||||
checkpoint->unwind();
|
||||
|
||||
abort(t);
|
||||
}
|
||||
|
||||
object
|
||||
makeByteArray(Thread* t, const char* format, va_list a)
|
||||
{
|
||||
const int Size = 256;
|
||||
char buffer[Size];
|
||||
|
||||
int r = vm::vsnprintf(buffer, Size - 1, format, a);
|
||||
expect(t, r >= 0 and r < Size - 1);
|
||||
|
||||
object s = makeByteArray(t, strlen(buffer) + 1);
|
||||
memcpy(&byteArrayBody(t, s, 0), buffer, byteArrayLength(t, s));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
object
|
||||
makeByteArray(Thread* t, const char* format, ...)
|
||||
{
|
||||
va_list a;
|
||||
va_start(a, format);
|
||||
object s = ::makeByteArray(t, format, a);
|
||||
object s = makeByteArray(t, format, a);
|
||||
va_end(a);
|
||||
|
||||
return s;
|
||||
@ -2775,7 +2889,7 @@ makeString(Thread* t, const char* format, ...)
|
||||
{
|
||||
va_list a;
|
||||
va_start(a, format);
|
||||
object s = ::makeByteArray(t, format, a);
|
||||
object s = makeByteArray(t, format, a);
|
||||
va_end(a);
|
||||
|
||||
return t->m->classpath->makeString(t, s, 0, byteArrayLength(t, s) - 1);
|
||||
@ -2880,6 +2994,16 @@ stringUTFChars(Thread* t, object string, unsigned start, unsigned length,
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
resolveBootstrap(Thread* t, uintptr_t* arguments)
|
||||
{
|
||||
object name = reinterpret_cast<object>(arguments[0]);
|
||||
|
||||
resolveSystemClass(t, root(t, Machine::BootLoader), name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool
|
||||
isAssignableFrom(Thread* t, object a, object b)
|
||||
{
|
||||
@ -2890,8 +3014,9 @@ isAssignableFrom(Thread* t, object a, object b)
|
||||
|
||||
if (classFlags(t, a) & ACC_INTERFACE) {
|
||||
if (classVmFlags(t, b) & BootstrapFlag) {
|
||||
resolveSystemClass(t, root(t, Machine::BootLoader), className(t, b));
|
||||
if (UNLIKELY(t->exception)) {
|
||||
uintptr_t arguments[] = { reinterpret_cast<uintptr_t>(className(t, b)) };
|
||||
|
||||
if (run(t, resolveBootstrap, arguments) == 0) {
|
||||
t->exception = 0;
|
||||
return false;
|
||||
}
|
||||
@ -3084,7 +3209,6 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size)
|
||||
if (super) {
|
||||
object sc = resolveClass
|
||||
(t, loader, referenceName(t, singletonObject(t, pool, super - 1)));
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
set(t, class_, ClassSuper, sc);
|
||||
|
||||
@ -3094,16 +3218,12 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size)
|
||||
}
|
||||
|
||||
parseInterfaceTable(t, s, class_, pool);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
parseFieldTable(t, s, class_, pool);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
parseMethodTable(t, s, class_, pool);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
parseAttributeTable(t, s, class_, pool);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
object vtable = classVirtualTable(t, class_);
|
||||
unsigned vtableLength = (vtable ? arrayLength(t, vtable) : 0);
|
||||
@ -3160,7 +3280,7 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_)
|
||||
if (byteArrayBody(t, spec, 0) == '[') {
|
||||
class_ = resolveArrayClass(t, loader, spec, throw_);
|
||||
} else {
|
||||
RUNTIME_ARRAY(char, file, byteArrayLength(t, spec) + 6);
|
||||
THREAD_RUNTIME_ARRAY(t, char, file, byteArrayLength(t, spec) + 6);
|
||||
memcpy(RUNTIME_ARRAY_BODY(file),
|
||||
&byteArrayBody(t, spec, 0),
|
||||
byteArrayLength(t, spec) - 1);
|
||||
@ -3177,27 +3297,27 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_)
|
||||
fprintf(stderr, "parsing %s\n", &byteArrayBody(t, spec, 0));
|
||||
}
|
||||
|
||||
// parse class file
|
||||
class_ = parseClass(t, loader, region->start(), region->length());
|
||||
region->dispose();
|
||||
{ THREAD_RESOURCE(t, System::Region*, region, region->dispose());
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
if (Verbose) {
|
||||
fprintf(stderr, "done parsing %s: %p\n",
|
||||
&byteArrayBody(t, spec, 0),
|
||||
class_);
|
||||
}
|
||||
// parse class file
|
||||
class_ = parseClass(t, loader, region->start(), region->length());
|
||||
}
|
||||
|
||||
object bootstrapClass = hashMapFind
|
||||
(t, root(t, Machine::BootstrapClassMap), spec, byteArrayHash,
|
||||
byteArrayEqual);
|
||||
if (Verbose) {
|
||||
fprintf(stderr, "done parsing %s: %p\n",
|
||||
&byteArrayBody(t, spec, 0),
|
||||
class_);
|
||||
}
|
||||
|
||||
if (bootstrapClass) {
|
||||
PROTECT(t, bootstrapClass);
|
||||
|
||||
updateBootstrapClass(t, bootstrapClass, class_);
|
||||
class_ = bootstrapClass;
|
||||
}
|
||||
object bootstrapClass = hashMapFind
|
||||
(t, root(t, Machine::BootstrapClassMap), spec, byteArrayHash,
|
||||
byteArrayEqual);
|
||||
|
||||
if (bootstrapClass) {
|
||||
PROTECT(t, bootstrapClass);
|
||||
|
||||
updateBootstrapClass(t, bootstrapClass, class_);
|
||||
class_ = bootstrapClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3206,10 +3326,9 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_)
|
||||
PROTECT(t, class_);
|
||||
|
||||
hashMapInsert(t, classLoaderMap(t, loader), spec, class_, byteArrayHash);
|
||||
} else if (throw_ and t->exception == 0) {
|
||||
object message = makeString(t, "%s", &byteArrayBody(t, spec, 0));
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::ClassNotFoundExceptionType, message);
|
||||
} else if (throw_) {
|
||||
throwNew(t, Machine::ClassNotFoundExceptionType, "%s",
|
||||
&byteArrayBody(t, spec, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3265,28 +3384,24 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
|
||||
}
|
||||
}
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
object method = findVirtualMethod
|
||||
(t, root(t, Machine::LoadClassMethod), objectClass(t, loader));
|
||||
object method = findVirtualMethod
|
||||
(t, root(t, Machine::LoadClassMethod), objectClass(t, loader));
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
PROTECT(t, method);
|
||||
PROTECT(t, method);
|
||||
|
||||
THREAD_RUNTIME_ARRAY(t, char, s, byteArrayLength(t, spec));
|
||||
replace('/', '.', RUNTIME_ARRAY_BODY(s), reinterpret_cast<char*>
|
||||
(&byteArrayBody(t, spec, 0)));
|
||||
|
||||
RUNTIME_ARRAY(char, s, byteArrayLength(t, spec));
|
||||
replace('/', '.', RUNTIME_ARRAY_BODY(s), reinterpret_cast<char*>
|
||||
(&byteArrayBody(t, spec, 0)));
|
||||
object specString = makeString(t, "%s", RUNTIME_ARRAY_BODY(s));
|
||||
|
||||
object specString = makeString(t, "%s", RUNTIME_ARRAY_BODY(s));
|
||||
|
||||
object jc = t->m->processor->invoke(t, method, loader, specString);
|
||||
if (LIKELY(jc and t->exception == 0)) {
|
||||
c = jclassVmClass(t, jc);
|
||||
}
|
||||
}
|
||||
object jc = t->m->processor->invoke(t, method, loader, specString);
|
||||
if (LIKELY(jc)) {
|
||||
c = jclassVmClass(t, jc);
|
||||
}
|
||||
}
|
||||
|
||||
if (LIKELY(c and t->exception == 0)) {
|
||||
if (LIKELY(c)) {
|
||||
PROTECT(t, c);
|
||||
|
||||
ACQUIRE(t, t->m->classLock);
|
||||
@ -3301,13 +3416,8 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
|
||||
|
||||
return c;
|
||||
} else {
|
||||
if (t->exception == 0) {
|
||||
object message = makeString(t, "%s", &byteArrayBody(t, spec, 0));
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::ClassNotFoundExceptionType, message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
throwNew(t, Machine::ClassNotFoundExceptionType, "%s",
|
||||
&byteArrayBody(t, spec, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3325,13 +3435,10 @@ resolveMethod(Thread* t, object class_, const char* methodName,
|
||||
|
||||
object method = findMethodInClass(t, class_, name, spec);
|
||||
|
||||
if (t->exception == 0 and method == 0) {
|
||||
object message = makeString
|
||||
(t, "%s %s not found in %s", methodName, methodSpec,
|
||||
&byteArrayBody(t, className(t, class_), 0));
|
||||
|
||||
t->exception = makeThrowable(t, Machine::NoSuchMethodErrorType, message);
|
||||
return 0;
|
||||
if (method == 0) {
|
||||
throwNew(t, Machine::NoSuchMethodErrorType, "%s %s not found in %s",
|
||||
methodName, methodSpec, &byteArrayBody
|
||||
(t, className(t, class_), 0));
|
||||
} else {
|
||||
return method;
|
||||
}
|
||||
@ -3358,13 +3465,9 @@ resolveField(Thread* t, object class_, const char* fieldName,
|
||||
field = findFieldInClass(t, c, name, spec);
|
||||
}
|
||||
|
||||
if (t->exception == 0 and field == 0) {
|
||||
object message = makeString
|
||||
(t, "%s %s not found in %s", fieldName, fieldSpec,
|
||||
&byteArrayBody(t, className(t, class_), 0));
|
||||
|
||||
t->exception = makeThrowable(t, Machine::NoSuchFieldErrorType, message);
|
||||
return 0;
|
||||
if (field == 0) {
|
||||
throwNew(t, Machine::NoSuchFieldErrorType, "%s %s not found in %s",
|
||||
fieldName, fieldSpec, &byteArrayBody(t, className(t, class_), 0));
|
||||
} else {
|
||||
return field;
|
||||
}
|
||||
@ -3413,11 +3516,8 @@ preInitClass(Thread* t, object c)
|
||||
t->m->classLock->wait(t->systemThread, 0);
|
||||
}
|
||||
} else if (classVmFlags(t, c) & InitErrorFlag) {
|
||||
object message = makeString
|
||||
(t, "%s", &byteArrayBody(t, className(t, c), 0));
|
||||
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::NoClassDefFoundErrorType, message);
|
||||
throwNew(t, Machine::NoClassDefFoundErrorType, "%s",
|
||||
&byteArrayBody(t, className(t, c), 0));
|
||||
} else {
|
||||
classVmFlags(t, c) |= InitFlag;
|
||||
return true;
|
||||
@ -3434,13 +3534,13 @@ postInitClass(Thread* t, object c)
|
||||
ACQUIRE(t, t->m->classLock);
|
||||
|
||||
if (t->exception) {
|
||||
object exception = t->exception;
|
||||
t->exception = 0;
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::ExceptionInInitializerErrorType, 0, 0, exception);
|
||||
|
||||
classVmFlags(t, c) |= NeedInitFlag | InitErrorFlag;
|
||||
classVmFlags(t, c) &= ~InitFlag;
|
||||
|
||||
object exception = t->exception;
|
||||
t->exception = 0;
|
||||
|
||||
throwNew(t, Machine::ExceptionInInitializerErrorType, 0, 0, exception);
|
||||
} else {
|
||||
classVmFlags(t, c) &= ~(NeedInitFlag | InitFlag);
|
||||
}
|
||||
@ -3453,11 +3553,11 @@ initClass(Thread* t, object c)
|
||||
PROTECT(t, c);
|
||||
|
||||
if (preInitClass(t, c)) {
|
||||
OBJECT_RESOURCE(t, c, postInitClass(t, c));
|
||||
|
||||
Thread::ClassInitStack stack(t, c);
|
||||
|
||||
t->m->processor->invoke(t, classInitializer(t, c), 0);
|
||||
|
||||
postInitClass(t, c);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3496,7 +3596,6 @@ resolveObjectArrayClass(Thread* t, object loader, object elementClass)
|
||||
}
|
||||
|
||||
object arrayClass = resolveClass(t, loader, spec);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
set(t, getClassRuntimeData(t, elementClass), ClassRuntimeDataArrayClass,
|
||||
arrayClass);
|
||||
@ -3509,7 +3608,6 @@ makeObjectArray(Thread* t, object elementClass, unsigned count)
|
||||
{
|
||||
object arrayClass = resolveObjectArrayClass
|
||||
(t, classLoader(t, elementClass), elementClass);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
PROTECT(t, arrayClass);
|
||||
|
||||
@ -3692,57 +3790,16 @@ collect(Thread* t, Heap::CollectionType type)
|
||||
{
|
||||
ENTER(t, Thread::ExclusiveState);
|
||||
|
||||
#ifdef VM_STRESS
|
||||
bool stress = (t->flags |= Thread::StressFlag);
|
||||
if (not stress) atomicOr(&(t->flags), Thread::StressFlag);
|
||||
#endif
|
||||
|
||||
Machine* m = t->m;
|
||||
|
||||
m->unsafe = true;
|
||||
m->heap->collect(type, footprint(m->rootThread));
|
||||
m->unsafe = false;
|
||||
|
||||
postCollect(m->rootThread);
|
||||
|
||||
killZombies(t, m->rootThread);
|
||||
|
||||
for (unsigned i = 0; i < m->heapPoolIndex; ++i) {
|
||||
m->heap->free(m->heapPool[i], ThreadHeapSizeInBytes);
|
||||
}
|
||||
m->heapPoolIndex = 0;
|
||||
|
||||
m->fixedFootprint = 0;
|
||||
|
||||
#ifdef VM_STRESS
|
||||
if (not stress) atomicAnd(&(t->flags), ~Thread::StressFlag);
|
||||
#endif
|
||||
|
||||
object f = t->m->finalizeQueue;
|
||||
t->m->finalizeQueue = 0;
|
||||
for (; f; f = finalizerNext(t, f)) {
|
||||
void (*function)(Thread*, object);
|
||||
memcpy(&function, &finalizerFinalize(t, f), BytesPerWord);
|
||||
if (function) {
|
||||
function(t, finalizerTarget(t, f));
|
||||
} else {
|
||||
setRoot(t, Machine::ObjectsToFinalize, makePair
|
||||
(t, finalizerTarget(t, f), root(t, Machine::ObjectsToFinalize)));
|
||||
}
|
||||
if (t->m->heap->limitExceeded()) {
|
||||
type = Heap::MajorCollection;
|
||||
}
|
||||
|
||||
if (root(t, Machine::ObjectsToFinalize) and m->finalizeThread == 0) {
|
||||
object javaThread = t->m->classpath->makeThread(t, m->rootThread);
|
||||
threadDaemon(t, javaThread) = true;
|
||||
doCollect(t, type);
|
||||
|
||||
m->finalizeThread = m->processor->makeThread(m, javaThread, m->rootThread);
|
||||
|
||||
addThread(t, m->finalizeThread);
|
||||
|
||||
if (not startThread(t, m->finalizeThread)) {
|
||||
removeThread(t, m->finalizeThread);
|
||||
m->finalizeThread = 0;
|
||||
}
|
||||
if (t->m->heap->limitExceeded()) {
|
||||
// try once more, giving the heap a chance to squeeze everything
|
||||
// into the smallest possible space:
|
||||
doCollect(t, Heap::MajorCollection);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3762,7 +3819,7 @@ walk(Thread* t, Heap::Walker* w, object o, unsigned start)
|
||||
= (arrayElementSize ?
|
||||
cast<uintptr_t>(o, fixedSize - BytesPerWord) : 0);
|
||||
|
||||
RUNTIME_ARRAY(uint32_t, mask, intArrayLength(t, objectMask));
|
||||
THREAD_RUNTIME_ARRAY(t, uint32_t, mask, intArrayLength(t, objectMask));
|
||||
memcpy(RUNTIME_ARRAY_BODY(mask), &intArrayBody(t, objectMask, 0),
|
||||
intArrayLength(t, objectMask) * 4);
|
||||
|
||||
@ -3836,7 +3893,7 @@ printTrace(Thread* t, object exception)
|
||||
|
||||
if (throwableMessage(t, e)) {
|
||||
object m = throwableMessage(t, e);
|
||||
RUNTIME_ARRAY(char, message, stringLength(t, m) + 1);
|
||||
THREAD_RUNTIME_ARRAY(t, char, message, stringLength(t, m) + 1);
|
||||
stringChars(t, m, RUNTIME_ARRAY_BODY(message));
|
||||
fprintf(stderr, ": %s\n", RUNTIME_ARRAY_BODY(message));
|
||||
} else {
|
||||
|
390
src/machine.h
390
src/machine.h
@ -37,6 +37,56 @@
|
||||
|
||||
#define ENTER(t, state) StateResource MAKE_NAME(stateResource_) (t, state)
|
||||
|
||||
#define THREAD_RESOURCE0(t, releaseBody) \
|
||||
class MAKE_NAME(Resource_): public Thread::Resource { \
|
||||
public: \
|
||||
MAKE_NAME(Resource_)(Thread* t): Resource(t) { } \
|
||||
~MAKE_NAME(Resource_)() { releaseBody; } \
|
||||
virtual void release() \
|
||||
{ this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); } \
|
||||
} MAKE_NAME(resource_)(t);
|
||||
|
||||
#define OBJECT_RESOURCE(t, name, releaseBody) \
|
||||
class MAKE_NAME(Resource_): public Thread::Resource { \
|
||||
public: \
|
||||
MAKE_NAME(Resource_)(Thread* t, object name): \
|
||||
Resource(t), name(name), protector(t, &(this->name)) { } \
|
||||
~MAKE_NAME(Resource_)() { releaseBody; } \
|
||||
virtual void release() \
|
||||
{ this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); } \
|
||||
\
|
||||
private: \
|
||||
object name; \
|
||||
Thread::SingleProtector protector; \
|
||||
} MAKE_NAME(resource_)(t, name);
|
||||
|
||||
#define THREAD_RESOURCE(t, type, name, releaseBody) \
|
||||
class MAKE_NAME(Resource_): public Thread::Resource { \
|
||||
public: \
|
||||
MAKE_NAME(Resource_)(Thread* t, type name): \
|
||||
Resource(t), name(name) { } \
|
||||
~MAKE_NAME(Resource_)() { releaseBody; } \
|
||||
virtual void release() \
|
||||
{ this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); } \
|
||||
\
|
||||
private: \
|
||||
type name; \
|
||||
} MAKE_NAME(resource_)(t, name);
|
||||
|
||||
#define THREAD_RESOURCE2(t, type1, name1, type2, name2, releaseBody) \
|
||||
class MAKE_NAME(Resource_): public Thread::Resource { \
|
||||
public: \
|
||||
MAKE_NAME(Resource_)(Thread* t, type1 name1, type2 name2): \
|
||||
Resource(t), name1(name1), name2(name2) { } \
|
||||
~MAKE_NAME(Resource_)() { releaseBody; } \
|
||||
virtual void release() \
|
||||
{ this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); } \
|
||||
\
|
||||
private: \
|
||||
type1 name1; \
|
||||
type2 name2; \
|
||||
} MAKE_NAME(resource_)(t, name1, name2);
|
||||
|
||||
namespace vm {
|
||||
|
||||
const bool Verbose = false;
|
||||
@ -1201,10 +1251,12 @@ class Machine {
|
||||
MethodRuntimeDataTable,
|
||||
JNIMethodTable,
|
||||
ShutdownHooks,
|
||||
FinalizerThread,
|
||||
ObjectsToFinalize,
|
||||
NullPointerException,
|
||||
ArithmeticException,
|
||||
ArrayIndexOutOfBoundsException,
|
||||
OutOfMemoryError,
|
||||
VirtualFileFinders,
|
||||
VirtualFiles
|
||||
};
|
||||
@ -1280,15 +1332,23 @@ inline void stress(Thread* t);
|
||||
|
||||
#endif // not VM_STRESS
|
||||
|
||||
void
|
||||
runJavaThread(Thread* t);
|
||||
uint64_t
|
||||
runThread(Thread*, uintptr_t*);
|
||||
|
||||
void
|
||||
runFinalizeThread(Thread* t);
|
||||
uint64_t
|
||||
run(Thread* t, uint64_t (*function)(Thread*, uintptr_t*),
|
||||
uintptr_t* arguments);
|
||||
|
||||
void
|
||||
checkDaemon(Thread* t);
|
||||
|
||||
extern "C" uint64_t
|
||||
vmRun(uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments,
|
||||
void* checkpoint);
|
||||
|
||||
extern "C" void
|
||||
vmRun_returnAddress();
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
enum State {
|
||||
@ -1336,9 +1396,26 @@ class Thread {
|
||||
object* p;
|
||||
};
|
||||
|
||||
class ClassInitStack {
|
||||
class Resource {
|
||||
public:
|
||||
Resource(Thread* t): t(t), next(t->resource) {
|
||||
t->resource = this;
|
||||
}
|
||||
|
||||
~Resource() {
|
||||
t->resource = next;
|
||||
}
|
||||
|
||||
virtual void release() = 0;
|
||||
|
||||
Thread* t;
|
||||
Resource* next;
|
||||
};
|
||||
|
||||
class ClassInitStack: public Resource {
|
||||
public:
|
||||
ClassInitStack(Thread* t, object class_):
|
||||
Resource(t),
|
||||
next(t->classInitStack),
|
||||
class_(class_),
|
||||
protector(t, &(this->class_))
|
||||
@ -1347,7 +1424,11 @@ class Thread {
|
||||
}
|
||||
|
||||
~ClassInitStack() {
|
||||
protector.t->classInitStack = next;
|
||||
t->classInitStack = next;
|
||||
}
|
||||
|
||||
virtual void release() {
|
||||
this->ClassInitStack::~ClassInitStack();
|
||||
}
|
||||
|
||||
ClassInitStack* next;
|
||||
@ -1355,6 +1436,50 @@ class Thread {
|
||||
SingleProtector protector;
|
||||
};
|
||||
|
||||
class Checkpoint {
|
||||
public:
|
||||
Checkpoint(Thread* t):
|
||||
t(t),
|
||||
next(t->checkpoint),
|
||||
resource(t->resource),
|
||||
protector(t->protector),
|
||||
noThrow(false)
|
||||
{
|
||||
t->checkpoint = this;
|
||||
}
|
||||
|
||||
~Checkpoint() {
|
||||
t->checkpoint = next;
|
||||
}
|
||||
|
||||
virtual void NO_RETURN unwind() = 0;
|
||||
|
||||
Thread* t;
|
||||
Checkpoint* next;
|
||||
Resource* resource;
|
||||
Protector* protector;
|
||||
bool noThrow;
|
||||
};
|
||||
|
||||
class RunCheckpoint: public Checkpoint {
|
||||
public:
|
||||
RunCheckpoint(Thread* t):
|
||||
Checkpoint(t),
|
||||
stack(0),
|
||||
base(0)
|
||||
{ }
|
||||
|
||||
virtual void unwind() {
|
||||
void* stack = this->stack;
|
||||
this->stack = 0;
|
||||
expect(t->m->system, stack);
|
||||
vmJump(voidPointer(vmRun_returnAddress), base, stack, t, 0, 0);
|
||||
}
|
||||
|
||||
void* stack;
|
||||
void* base;
|
||||
};
|
||||
|
||||
class Runnable: public System::Runnable {
|
||||
public:
|
||||
Runnable(Thread* t): t(t) { }
|
||||
@ -1366,18 +1491,10 @@ class Thread {
|
||||
virtual void run() {
|
||||
enterActiveState(t);
|
||||
|
||||
t->m->localThread->set(t);
|
||||
vm::run(t, runThread, 0);
|
||||
|
||||
checkDaemon(t);
|
||||
|
||||
if (t == t->m->finalizeThread) {
|
||||
runFinalizeThread(t);
|
||||
} else if (t->javaThread) {
|
||||
runJavaThread(t);
|
||||
|
||||
if (t->exception) {
|
||||
printTrace(t, t->exception);
|
||||
}
|
||||
if (t->exception) {
|
||||
printTrace(t, t->exception);
|
||||
}
|
||||
|
||||
t->exit();
|
||||
@ -1416,6 +1533,8 @@ class Thread {
|
||||
unsigned heapOffset;
|
||||
Protector* protector;
|
||||
ClassInitStack* classInitStack;
|
||||
Resource* resource;
|
||||
Checkpoint* checkpoint;
|
||||
Runnable runnable;
|
||||
uintptr_t* defaultHeap;
|
||||
uintptr_t* heap;
|
||||
@ -1448,6 +1567,38 @@ class Classpath {
|
||||
dispose() = 0;
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
template <class T>
|
||||
class RuntimeArray: public Thread::Resource {
|
||||
public:
|
||||
RuntimeArray(Thread* t, unsigned size):
|
||||
Resource(t),
|
||||
body(static_cast<T*>(t->m->heap->allocate(size * sizeof(T)))),
|
||||
size(size)
|
||||
{ }
|
||||
|
||||
~RuntimeArray() {
|
||||
t->m->heap->free(body, size * sizeof(T));
|
||||
}
|
||||
|
||||
virtual void release() {
|
||||
RuntimeArray::~RuntimeArray();
|
||||
}
|
||||
|
||||
T* body;
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
# define THREAD_RUNTIME_ARRAY(thread, type, name, size) \
|
||||
RuntimeArray<type> name(thread, size);
|
||||
|
||||
#else // not _MSC_VER
|
||||
|
||||
# define THREAD_RUNTIME_ARRAY(thread, type, name, size) type name[size];
|
||||
|
||||
#endif // not _MSC_VER
|
||||
|
||||
Classpath*
|
||||
makeClasspath(System* system, Allocator* allocator, const char* javaHome,
|
||||
const char* embedPrefix);
|
||||
@ -1469,16 +1620,21 @@ enterActiveState(Thread* t)
|
||||
enter(t, Thread::ActiveState);
|
||||
}
|
||||
|
||||
class StateResource {
|
||||
class StateResource: public Thread::Resource {
|
||||
public:
|
||||
StateResource(Thread* t, Thread::State state): t(t), oldState(t->state) {
|
||||
StateResource(Thread* t, Thread::State state):
|
||||
Resource(t), oldState(t->state)
|
||||
{
|
||||
enter(t, state);
|
||||
}
|
||||
|
||||
~StateResource() { enter(t, oldState); }
|
||||
|
||||
virtual void release() {
|
||||
this->StateResource::~StateResource();
|
||||
}
|
||||
|
||||
private:
|
||||
Thread* t;
|
||||
Thread::State oldState;
|
||||
};
|
||||
|
||||
@ -1553,33 +1709,43 @@ release(Thread* t, System::Monitor* m)
|
||||
m->release(t->systemThread);
|
||||
}
|
||||
|
||||
class MonitorResource {
|
||||
class MonitorResource: public Thread::Resource {
|
||||
public:
|
||||
MonitorResource(Thread* t, System::Monitor* m): t(t), m(m) {
|
||||
MonitorResource(Thread* t, System::Monitor* m):
|
||||
Resource(t), m(m)
|
||||
{
|
||||
acquire(t, m);
|
||||
}
|
||||
|
||||
~MonitorResource() {
|
||||
release(t, m);
|
||||
vm::release(t, m);
|
||||
}
|
||||
|
||||
virtual void release() {
|
||||
this->MonitorResource::~MonitorResource();
|
||||
}
|
||||
|
||||
private:
|
||||
Thread* t;
|
||||
System::Monitor* m;
|
||||
};
|
||||
|
||||
class RawMonitorResource {
|
||||
class RawMonitorResource: public Thread::Resource {
|
||||
public:
|
||||
RawMonitorResource(Thread* t, System::Monitor* m): t(t), m(m) {
|
||||
RawMonitorResource(Thread* t, System::Monitor* m):
|
||||
Resource(t), m(m)
|
||||
{
|
||||
m->acquire(t->systemThread);
|
||||
}
|
||||
|
||||
~RawMonitorResource() {
|
||||
release(t, m);
|
||||
vm::release(t, m);
|
||||
}
|
||||
|
||||
virtual void release() {
|
||||
this->RawMonitorResource::~RawMonitorResource();
|
||||
}
|
||||
|
||||
private:
|
||||
Thread* t;
|
||||
System::Monitor* m;
|
||||
};
|
||||
|
||||
@ -1622,8 +1788,12 @@ class FixedAllocator: public Allocator {
|
||||
return p;
|
||||
}
|
||||
|
||||
virtual void free(const void*, unsigned) {
|
||||
abort(s);
|
||||
virtual void free(const void* p, unsigned size) {
|
||||
if (p >= base and static_cast<const uint8_t*>(p) + size == base + offset) {
|
||||
offset -= size;
|
||||
} else {
|
||||
abort(s);
|
||||
}
|
||||
}
|
||||
|
||||
System* s;
|
||||
@ -1667,7 +1837,6 @@ allocateSmall(Thread* t, unsigned sizeInBytes)
|
||||
|
||||
object o = reinterpret_cast<object>(t->heap + t->heapIndex);
|
||||
t->heapIndex += ceiling(sizeInBytes, BytesPerWord);
|
||||
cast<object>(o, 0) = 0;
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -1745,12 +1914,39 @@ instanceOf(Thread* t, object class_, object o);
|
||||
|
||||
#include "type-declarations.cpp"
|
||||
|
||||
inline uint64_t
|
||||
run(Thread* t, uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments)
|
||||
{
|
||||
ENTER(t, Thread::ActiveState);
|
||||
Thread::RunCheckpoint checkpoint(t);
|
||||
return vmRun(function, arguments, &checkpoint);
|
||||
}
|
||||
|
||||
inline void
|
||||
runJavaThread(Thread* t)
|
||||
{
|
||||
t->m->classpath->runThread(t);
|
||||
}
|
||||
|
||||
void
|
||||
runFinalizeThread(Thread* t);
|
||||
|
||||
inline uint64_t
|
||||
runThread(Thread* t, uintptr_t*)
|
||||
{
|
||||
t->m->localThread->set(t);
|
||||
|
||||
checkDaemon(t);
|
||||
|
||||
if (t == t->m->finalizeThread) {
|
||||
runFinalizeThread(t);
|
||||
} else if (t->javaThread) {
|
||||
runJavaThread(t);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline bool
|
||||
startThread(Thread* t, Thread* p)
|
||||
{
|
||||
@ -1820,17 +2016,12 @@ checkDaemon(Thread* t)
|
||||
}
|
||||
}
|
||||
|
||||
inline Thread*
|
||||
attachThread(Machine* m, bool daemon)
|
||||
inline uint64_t
|
||||
initAttachedThread(Thread* t, uintptr_t* arguments)
|
||||
{
|
||||
Thread* t = m->processor->makeThread(m, 0, m->rootThread);
|
||||
m->system->attach(&(t->runnable));
|
||||
bool daemon = arguments[0];
|
||||
|
||||
addThread(t, t);
|
||||
|
||||
enter(t, Thread::ActiveState);
|
||||
|
||||
t->javaThread = m->classpath->makeThread(t, m->rootThread);
|
||||
t->javaThread = t->m->classpath->makeThread(t, t->m->rootThread);
|
||||
|
||||
threadPeer(t, t->javaThread) = reinterpret_cast<jlong>(t);
|
||||
|
||||
@ -1840,11 +2031,30 @@ attachThread(Machine* m, bool daemon)
|
||||
registerDaemon(t);
|
||||
}
|
||||
|
||||
enter(t, Thread::IdleState);
|
||||
t->m->localThread->set(t);
|
||||
|
||||
m->localThread->set(t);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return t;
|
||||
inline Thread*
|
||||
attachThread(Machine* m, bool daemon)
|
||||
{
|
||||
Thread* t = m->processor->makeThread(m, 0, m->rootThread);
|
||||
m->system->attach(&(t->runnable));
|
||||
|
||||
addThread(t, t);
|
||||
|
||||
uintptr_t arguments[] = { daemon };
|
||||
|
||||
enter(t, Thread::ActiveState);
|
||||
|
||||
if (run(t, initAttachedThread, arguments)) {
|
||||
enter(t, Thread::IdleState);
|
||||
return t;
|
||||
} else {
|
||||
t->exit();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline object&
|
||||
@ -1939,6 +2149,9 @@ make(Thread* t, object class_)
|
||||
}
|
||||
}
|
||||
|
||||
object
|
||||
makeByteArray(Thread* t, const char* format, va_list a);
|
||||
|
||||
object
|
||||
makeByteArray(Thread* t, const char* format, ...);
|
||||
|
||||
@ -2248,12 +2461,8 @@ inline object
|
||||
resolveMethod(Thread* t, object loader, const char* className,
|
||||
const char* methodName, const char* methodSpec)
|
||||
{
|
||||
object class_ = resolveClass(t, loader, className);
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
return resolveMethod(t, class_, methodName, methodSpec);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return resolveMethod
|
||||
(t, resolveClass(t, loader, className), methodName, methodSpec);
|
||||
}
|
||||
|
||||
object
|
||||
@ -2264,12 +2473,8 @@ inline object
|
||||
resolveField(Thread* t, object loader, const char* className,
|
||||
const char* fieldName, const char* fieldSpec)
|
||||
{
|
||||
object class_ = resolveClass(t, loader, className);
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
return resolveField(t, class_, fieldName, fieldSpec);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return resolveField
|
||||
(t, resolveClass(t, loader, className), fieldName, fieldSpec);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2347,6 +2552,48 @@ makeThrowable
|
||||
return result;
|
||||
}
|
||||
|
||||
inline object
|
||||
makeThrowable(Thread* t, Machine::Type type, const char* format, va_list a)
|
||||
{
|
||||
object s = makeByteArray(t, format, a);
|
||||
|
||||
object message = t->m->classpath->makeString
|
||||
(t, s, 0, byteArrayLength(t, s) - 1);
|
||||
|
||||
return makeThrowable(t, type, message);
|
||||
}
|
||||
|
||||
inline object
|
||||
makeThrowable(Thread* t, Machine::Type type, const char* format, ...)
|
||||
{
|
||||
va_list a;
|
||||
va_start(a, format);
|
||||
object r = makeThrowable(t, type, format, a);
|
||||
va_end(a);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void NO_RETURN
|
||||
throw_(Thread* t, object e);
|
||||
|
||||
inline void NO_RETURN
|
||||
throwNew(Thread* t, Machine::Type type)
|
||||
{
|
||||
throw_(t, makeThrowable(t, type));
|
||||
}
|
||||
|
||||
inline void NO_RETURN
|
||||
throwNew(Thread* t, Machine::Type type, const char* format, ...)
|
||||
{
|
||||
va_list a;
|
||||
va_start(a, format);
|
||||
object r = makeThrowable(t, type, format, a);
|
||||
va_end(a);
|
||||
|
||||
throw_(t, r);
|
||||
}
|
||||
|
||||
object
|
||||
findInHierarchyOrNull(Thread* t, object class_, object name, object spec,
|
||||
object (*find)(Thread*, object, object, object));
|
||||
@ -2359,12 +2606,10 @@ findInHierarchy(Thread* t, object class_, object name, object spec,
|
||||
object o = findInHierarchyOrNull(t, class_, name, spec, find);
|
||||
|
||||
if (o == 0) {
|
||||
object message = makeString
|
||||
(t, "%s %s not found in %s",
|
||||
&byteArrayBody(t, name, 0),
|
||||
&byteArrayBody(t, spec, 0),
|
||||
&byteArrayBody(t, className(t, class_), 0));
|
||||
t->exception = makeThrowable(t, errorType, message);
|
||||
throwNew(t, errorType, "%s %s not found in %s",
|
||||
&byteArrayBody(t, name, 0),
|
||||
&byteArrayBody(t, spec, 0),
|
||||
&byteArrayBody(t, className(t, class_), 0));
|
||||
}
|
||||
|
||||
return o;
|
||||
@ -2390,8 +2635,7 @@ findMethodOrNull(Thread* t, object class_, const char* name, const char* spec)
|
||||
inline object
|
||||
findVirtualMethod(Thread* t, object method, object class_)
|
||||
{
|
||||
return arrayBody(t, classVirtualTable(t, class_),
|
||||
methodOffset(t, method));
|
||||
return arrayBody(t, classVirtualTable(t, class_), methodOffset(t, method));
|
||||
}
|
||||
|
||||
inline object
|
||||
@ -2403,8 +2647,8 @@ findInterfaceMethod(Thread* t, object method, object class_)
|
||||
object itable = classInterfaceTable(t, class_);
|
||||
for (unsigned i = 0; i < arrayLength(t, itable); i += 2) {
|
||||
if (arrayBody(t, itable, i) == interface) {
|
||||
return arrayBody(t, arrayBody(t, itable, i + 1),
|
||||
methodOffset(t, method));
|
||||
return arrayBody
|
||||
(t, arrayBody(t, itable, i + 1), methodOffset(t, method));
|
||||
}
|
||||
}
|
||||
abort(t);
|
||||
@ -2849,10 +3093,10 @@ wait(Thread* t, object o, int64_t milliseconds)
|
||||
bool interrupted = monitorWait(t, m, milliseconds);
|
||||
|
||||
if (interrupted) {
|
||||
t->exception = makeThrowable(t, Machine::InterruptedExceptionType);
|
||||
throwNew(t, Machine::InterruptedExceptionType);
|
||||
}
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::IllegalMonitorStateExceptionType);
|
||||
throwNew(t, Machine::IllegalMonitorStateExceptionType);
|
||||
}
|
||||
|
||||
if (DebugMonitors) {
|
||||
@ -2881,7 +3125,7 @@ notify(Thread* t, object o)
|
||||
if (m and monitorOwner(t, m) == t) {
|
||||
monitorNotify(t, m);
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::IllegalMonitorStateExceptionType);
|
||||
throwNew(t, Machine::IllegalMonitorStateExceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2898,7 +3142,7 @@ notifyAll(Thread* t, object o)
|
||||
if (m and monitorOwner(t, m) == t) {
|
||||
monitorNotifyAll(t, m);
|
||||
} else {
|
||||
t->exception = makeThrowable(t, Machine::IllegalMonitorStateExceptionType);
|
||||
throwNew(t, Machine::IllegalMonitorStateExceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3057,7 +3301,6 @@ resolveClassInObject(Thread* t, object loader, object container,
|
||||
PROTECT(t, container);
|
||||
|
||||
o = resolveClass(t, loader, o);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
set(t, container, classOffset, o);
|
||||
}
|
||||
@ -3072,7 +3315,6 @@ resolveClassInPool(Thread* t, object loader, object method, unsigned index)
|
||||
PROTECT(t, method);
|
||||
|
||||
o = resolveClass(t, loader, referenceName(t, o));
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
set(t, codePool(t, methodCode(t, method)),
|
||||
SingletonBody + (index * BytesPerWord), o);
|
||||
@ -3101,12 +3343,10 @@ resolve(Thread* t, object loader, object method, unsigned index,
|
||||
PROTECT(t, reference);
|
||||
|
||||
object class_ = resolveClassInObject(t, loader, o, ReferenceClass);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
o = findInHierarchy
|
||||
(t, class_, referenceName(t, reference), referenceSpec(t, reference),
|
||||
find, errorType);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
|
||||
set(t, codePool(t, methodCode(t, method)),
|
||||
SingletonBody + (index * BytesPerWord), o);
|
||||
@ -3236,9 +3476,7 @@ primitiveClass(Thread* t, char name)
|
||||
case 'S': return type(t, Machine::JshortType);
|
||||
case 'V': return type(t, Machine::JvoidType);
|
||||
case 'Z': return type(t, Machine::JbooleanType);
|
||||
default:
|
||||
t->exception = makeThrowable(t, Machine::IllegalArgumentExceptionType);
|
||||
return 0;
|
||||
default: throwNew(t, Machine::IllegalArgumentExceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,13 +153,13 @@ resolveNativeMethod(Thread* t, object method, const char* prefix,
|
||||
{
|
||||
unsigned undecoratedSize = prefixLength + jniNameLength(t, method, false);
|
||||
// extra 6 is for code below:
|
||||
RUNTIME_ARRAY(char, undecorated, undecoratedSize + 1 + 6);
|
||||
THREAD_RUNTIME_ARRAY(t, char, undecorated, undecoratedSize + 1 + 6);
|
||||
makeJNIName(t, prefix, prefixLength, RUNTIME_ARRAY_BODY(undecorated) + 1,
|
||||
method, false);
|
||||
|
||||
unsigned decoratedSize = prefixLength + jniNameLength(t, method, true);
|
||||
// extra 6 is for code below:
|
||||
RUNTIME_ARRAY(char, decorated, decoratedSize + 1 + 6);
|
||||
THREAD_RUNTIME_ARRAY(t, char, decorated, decoratedSize + 1 + 6);
|
||||
makeJNIName(t, prefix, prefixLength, RUNTIME_ARRAY_BODY(decorated) + 1,
|
||||
method, true);
|
||||
|
||||
@ -232,20 +232,13 @@ resolveNative(Thread* t, object method)
|
||||
|
||||
initClass(t, methodClass(t, method));
|
||||
|
||||
if (LIKELY(t->exception == 0)
|
||||
and methodRuntimeDataNative(t, getMethodRuntimeData(t, method)) == 0)
|
||||
{
|
||||
if (methodRuntimeDataNative(t, getMethodRuntimeData(t, method)) == 0) {
|
||||
object native = resolveNativeMethod(t, method);
|
||||
if (UNLIKELY(native == 0)) {
|
||||
object message = makeString
|
||||
(t, "%s.%s%s",
|
||||
&byteArrayBody(t, className(t, methodClass(t, method)), 0),
|
||||
&byteArrayBody(t, methodName(t, method), 0),
|
||||
&byteArrayBody(t, methodSpec(t, method), 0));
|
||||
|
||||
t->exception = makeThrowable
|
||||
(t, Machine::UnsatisfiedLinkErrorType, message);
|
||||
return;
|
||||
throwNew(t, Machine::UnsatisfiedLinkErrorType, "%s.%s%s",
|
||||
&byteArrayBody(t, className(t, methodClass(t, method)), 0),
|
||||
&byteArrayBody(t, methodName(t, method), 0),
|
||||
&byteArrayBody(t, methodSpec(t, method), 0));
|
||||
}
|
||||
|
||||
PROTECT(t, native);
|
||||
|
128
src/x86.S
128
src/x86.S
@ -23,6 +23,10 @@
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define CHECKPOINT_THREAD 8
|
||||
#define CHECKPOINT_STACK 48
|
||||
#define CHECKPOINT_BASE 56
|
||||
|
||||
#ifdef __MINGW32__
|
||||
.globl GLOBAL(detectFeature)
|
||||
GLOBAL(detectFeature):
|
||||
@ -173,6 +177,48 @@ GLOBAL(vmJump):
|
||||
movq %r8,%rsp
|
||||
movq %r9,%rbx
|
||||
jmp *%rcx
|
||||
|
||||
#define VMRUN_FRAME_SIZE 80
|
||||
|
||||
.globl GLOBAL(vmRun)
|
||||
GLOBAL(vmRun):
|
||||
// %rcx: function
|
||||
// %rdx: arguments
|
||||
// %r8 : checkpoint
|
||||
pushq %rbp
|
||||
movq %rsp,%rbp
|
||||
subq $VMRUN_FRAME_SIZE,%rsp
|
||||
|
||||
movq %rbx,16(%rsp)
|
||||
movq %r12,24(%rsp)
|
||||
movq %r13,32(%rsp)
|
||||
movq %r14,40(%rsp)
|
||||
movq %r15,48(%rsp)
|
||||
movq %rsi,56(%rsp)
|
||||
movq %rdi,64(%rsp)
|
||||
|
||||
movq %rsp,CHECKPOINT_STACK(%rcx)
|
||||
movq %rbp,CHECKPOINT_BASE(%rcx)
|
||||
|
||||
movq %rcx,%r11
|
||||
movq CHECKPOINT_THREAD(%rdx),%rcx
|
||||
|
||||
call *%r11
|
||||
|
||||
.globl GLOBAL(vmRun_returnAddress)
|
||||
GLOBAL(vmRun_returnAddress):
|
||||
|
||||
movq 16(%rsp),%rbx
|
||||
movq 24(%rsp),%r12
|
||||
movq 32(%rsp),%r13
|
||||
movq 40(%rsp),%r14
|
||||
movq 48(%rsp),%r15
|
||||
movq 56(%rsp),%rsi
|
||||
movq 64(%rsp),%rdi
|
||||
|
||||
addq $VMRUN_FRAME_SIZE,%rsp
|
||||
popq %rbp
|
||||
ret
|
||||
|
||||
#else // not __MINGW32__
|
||||
.globl GLOBAL(detectFeature)
|
||||
@ -314,10 +360,52 @@ GLOBAL(vmJump):
|
||||
movq %r9,%rdx
|
||||
jmp *%rdi
|
||||
|
||||
#define VMRUN_FRAME_SIZE 64
|
||||
|
||||
.globl GLOBAL(vmRun)
|
||||
GLOBAL(vmRun):
|
||||
// %rdi: function
|
||||
// %rsi: arguments
|
||||
// %rdx: checkpoint
|
||||
pushq %rbp
|
||||
movq %rsp,%rbp
|
||||
subq $VMRUN_FRAME_SIZE,%rsp
|
||||
|
||||
movq %rbx,16(%rsp)
|
||||
movq %r12,24(%rsp)
|
||||
movq %r13,32(%rsp)
|
||||
movq %r14,40(%rsp)
|
||||
movq %r15,48(%rsp)
|
||||
|
||||
movq %rsp,CHECKPOINT_STACK(%rdx)
|
||||
movq %rbp,CHECKPOINT_BASE(%rdx)
|
||||
|
||||
movq %rdi,%r11
|
||||
movq CHECKPOINT_THREAD(%rdx),%rdi
|
||||
|
||||
call *%r11
|
||||
|
||||
.globl GLOBAL(vmRun_returnAddress)
|
||||
GLOBAL(vmRun_returnAddress):
|
||||
|
||||
movq 16(%rsp),%rbx
|
||||
movq 24(%rsp),%r12
|
||||
movq 32(%rsp),%r13
|
||||
movq 40(%rsp),%r14
|
||||
movq 48(%rsp),%r15
|
||||
|
||||
addq $VMRUN_FRAME_SIZE,%rsp
|
||||
popq %rbp
|
||||
ret
|
||||
|
||||
#endif // not __MINGW32__
|
||||
|
||||
#elif defined __i386__
|
||||
|
||||
#define CHECKPOINT_THREAD 4
|
||||
#define CHECKPOINT_STACK 24
|
||||
#define CHECKPOINT_BASE 28
|
||||
|
||||
.globl GLOBAL(detectFeature)
|
||||
GLOBAL(detectFeature):
|
||||
pushl %ebp
|
||||
@ -432,4 +520,42 @@ GLOBAL(vmJump):
|
||||
movl 12(%esp),%esp
|
||||
jmp *%esi
|
||||
|
||||
#endif //def __x86_64__
|
||||
#define VMRUN_FRAME_SIZE 32
|
||||
|
||||
.globl GLOBAL(vmRun)
|
||||
GLOBAL(vmRun):
|
||||
// 8(%ebp): function
|
||||
// 12(%ebp): arguments
|
||||
// 16(%ebp): checkpoint
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
subl $VMRUN_FRAME_SIZE,%esp
|
||||
|
||||
movl %ebx,8(%esp)
|
||||
movl %esi,12(%esp)
|
||||
movl %edi,16(%esp)
|
||||
|
||||
movl 12(%ebp),%eax
|
||||
movl %eax,4(%esp)
|
||||
|
||||
movl 16(%ebp),%ecx
|
||||
movl CHECKPOINT_THREAD(%ecx),%eax
|
||||
movl %eax,0(%esp)
|
||||
|
||||
movl %esp,CHECKPOINT_STACK(%ecx)
|
||||
movl %ebp,CHECKPOINT_BASE(%ecx)
|
||||
|
||||
call *8(%ebp)
|
||||
|
||||
.globl GLOBAL(vmRun_returnAddress)
|
||||
GLOBAL(vmRun_returnAddress):
|
||||
|
||||
movl 8(%esp),%ebx
|
||||
movl 12(%esp),%esi
|
||||
movl 16(%esp),%edi
|
||||
|
||||
addl $VMRUN_FRAME_SIZE,%esp
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
#endif // __i386__
|
||||
|
62
test/OutOfMemory.java
Normal file
62
test/OutOfMemory.java
Normal file
@ -0,0 +1,62 @@
|
||||
public class OutOfMemory {
|
||||
// assume a 128MB heap size:
|
||||
private static final int Padding = 120 * 1024 * 1024;
|
||||
|
||||
private static class Node {
|
||||
Object value;
|
||||
Node next;
|
||||
}
|
||||
|
||||
private static void bigObjects() {
|
||||
Object[] root = null;
|
||||
while (true) {
|
||||
Object[] x = new Object[1024 * 1024];
|
||||
x[0] = root;
|
||||
root = x;
|
||||
}
|
||||
}
|
||||
|
||||
private static void littleObjects() {
|
||||
byte[] padding = new byte[Padding];
|
||||
Node root = null;
|
||||
while (true) {
|
||||
Node x = new Node();
|
||||
x.next = root;
|
||||
root = x;
|
||||
}
|
||||
}
|
||||
|
||||
private static void bigAndLittleObjects() {
|
||||
byte[] padding = new byte[Padding];
|
||||
Node root = null;
|
||||
while (true) {
|
||||
Node x = new Node();
|
||||
x.value = new Object[1024 * 1024];
|
||||
x.next = root;
|
||||
root = x;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
bigObjects();
|
||||
throw new RuntimeException();
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
littleObjects();
|
||||
throw new RuntimeException();
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
bigAndLittleObjects();
|
||||
throw new RuntimeException();
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user