mirror of
https://github.com/corda/corda.git
synced 2025-01-07 13:38:47 +00:00
afabe8e07e
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.
292 lines
7.7 KiB
C++
292 lines
7.7 KiB
C++
/* Copyright (c) 2010, Avian Contributors
|
|
|
|
Permission to use, copy, modify, and/or distribute this software
|
|
for any purpose with or without fee is hereby granted, provided
|
|
that the above copyright notice and this permission notice appear
|
|
in all copies.
|
|
|
|
There is NO WARRANTY for this software. See license.txt for
|
|
details. */
|
|
|
|
#ifndef CLASSPATH_COMMON_H
|
|
#define CLASSPATH_COMMON_H
|
|
|
|
#include "tokenizer.h"
|
|
|
|
namespace vm {
|
|
|
|
object
|
|
getTrace(Thread* t, unsigned skipCount)
|
|
{
|
|
class Visitor: public Processor::StackVisitor {
|
|
public:
|
|
Visitor(Thread* t, int skipCount):
|
|
t(t), trace(0), skipCount(skipCount)
|
|
{ }
|
|
|
|
virtual bool visit(Processor::StackWalker* walker) {
|
|
if (skipCount == 0) {
|
|
object method = walker->method();
|
|
if (isAssignableFrom
|
|
(t, type(t, Machine::ThrowableType), methodClass(t, method))
|
|
and vm::strcmp(reinterpret_cast<const int8_t*>("<init>"),
|
|
&byteArrayBody(t, methodName(t, method), 0))
|
|
== 0)
|
|
{
|
|
return true;
|
|
} else {
|
|
trace = makeTrace(t, walker);
|
|
return false;
|
|
}
|
|
} else {
|
|
-- skipCount;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Thread* t;
|
|
object trace;
|
|
unsigned skipCount;
|
|
} v(t, skipCount);
|
|
|
|
t->m->processor->walkStack(t, &v);
|
|
|
|
if (v.trace == 0) v.trace = makeObjectArray(t, 0);
|
|
|
|
return v.trace;
|
|
}
|
|
|
|
bool
|
|
compatibleArrayTypes(Thread* t, object a, object b)
|
|
{
|
|
return classArrayElementSize(t, a)
|
|
and classArrayElementSize(t, b)
|
|
and (a == b
|
|
or (not ((classVmFlags(t, a) & PrimitiveFlag)
|
|
or (classVmFlags(t, b) & PrimitiveFlag))));
|
|
}
|
|
|
|
void
|
|
arrayCopy(Thread* t, object src, int32_t srcOffset, object dst,
|
|
int32_t dstOffset, int32_t length)
|
|
{
|
|
if (LIKELY(src and dst)) {
|
|
if (LIKELY(compatibleArrayTypes
|
|
(t, objectClass(t, src), objectClass(t, dst))))
|
|
{
|
|
unsigned elementSize = classArrayElementSize(t, objectClass(t, src));
|
|
|
|
if (LIKELY(elementSize)) {
|
|
intptr_t sl = cast<uintptr_t>(src, BytesPerWord);
|
|
intptr_t dl = cast<uintptr_t>(dst, BytesPerWord);
|
|
if (LIKELY(length > 0)) {
|
|
if (LIKELY(srcOffset >= 0 and srcOffset + length <= sl and
|
|
dstOffset >= 0 and dstOffset + length <= dl))
|
|
{
|
|
uint8_t* sbody = &cast<uint8_t>(src, ArrayBody);
|
|
uint8_t* dbody = &cast<uint8_t>(dst, ArrayBody);
|
|
if (src == dst) {
|
|
memmove(dbody + (dstOffset * elementSize),
|
|
sbody + (srcOffset * elementSize),
|
|
length * elementSize);
|
|
} else {
|
|
memcpy(dbody + (dstOffset * elementSize),
|
|
sbody + (srcOffset * elementSize),
|
|
length * elementSize);
|
|
}
|
|
|
|
if (classObjectMask(t, objectClass(t, dst))) {
|
|
mark(t, dst, ArrayBody + (dstOffset * BytesPerWord), length);
|
|
}
|
|
|
|
return;
|
|
} else {
|
|
throwNew(t, Machine::IndexOutOfBoundsExceptionType);
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
throwNew(t, Machine::NullPointerExceptionType);
|
|
return;
|
|
}
|
|
|
|
throwNew(t, Machine::ArrayStoreExceptionType);
|
|
}
|
|
|
|
void
|
|
runOnLoadIfFound(Thread* t, System::Library* library)
|
|
{
|
|
void* p = library->resolve("JNI_OnLoad");
|
|
if (p) {
|
|
jint (JNICALL * JNI_OnLoad)(Machine*, void*);
|
|
memcpy(&JNI_OnLoad, &p, sizeof(void*));
|
|
JNI_OnLoad(t->m, 0);
|
|
}
|
|
}
|
|
|
|
System::Library*
|
|
loadLibrary(Thread* t, const char* name)
|
|
{
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
System::Library* last = t->m->libraries;
|
|
for (System::Library* lib = t->m->libraries; lib; lib = lib->next()) {
|
|
if (lib->name() and ::strcmp(lib->name(), name) == 0) {
|
|
// already loaded
|
|
return lib;
|
|
}
|
|
last = lib;
|
|
}
|
|
|
|
System::Library* lib;
|
|
if (t->m->system->success(t->m->system->load(&lib, name))) {
|
|
last->setNext(lib);
|
|
return lib;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
System::Library*
|
|
loadLibrary(Thread* t, const char* path, const char* name, bool mapName,
|
|
bool runOnLoad)
|
|
{
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
char* mappedName;
|
|
unsigned nameLength = strlen(name);
|
|
if (mapName) {
|
|
const char* builtins = findProperty(t, "avian.builtins");
|
|
if (builtins) {
|
|
const char* s = builtins;
|
|
while (*s) {
|
|
if (::strncmp(s, name, nameLength) == 0
|
|
and (s[nameLength] == ',' or s[nameLength] == 0))
|
|
{
|
|
// library is built in to this executable
|
|
if (runOnLoad and not t->m->triedBuiltinOnLoad) {
|
|
t->m->triedBuiltinOnLoad = true;
|
|
// todo: release the classLock before calling this to
|
|
// avoid the possibility of deadlock:
|
|
runOnLoadIfFound(t, t->m->libraries);
|
|
}
|
|
return t->m->libraries;
|
|
} else {
|
|
while (*s and *s != ',') ++ s;
|
|
if (*s) ++ s;
|
|
}
|
|
}
|
|
}
|
|
|
|
const char* prefix = t->m->system->libraryPrefix();
|
|
const char* suffix = t->m->system->librarySuffix();
|
|
unsigned mappedNameLength = nameLength + strlen(prefix) + strlen(suffix);
|
|
|
|
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();)
|
|
{
|
|
Tokenizer::Token token(tokenizer.next());
|
|
|
|
unsigned fullNameLength = token.length + 1 + nameLength;
|
|
THREAD_RUNTIME_ARRAY(t, char, fullName, fullNameLength + 1);
|
|
|
|
snprintf(RUNTIME_ARRAY_BODY(fullName), fullNameLength + 1,
|
|
"%*s/%s", token.length, token.s, name);
|
|
|
|
lib = loadLibrary(t, RUNTIME_ARRAY_BODY(fullName));
|
|
if (lib) break;
|
|
}
|
|
|
|
if (lib == 0) {
|
|
lib = loadLibrary(t, name);
|
|
}
|
|
|
|
if (lib) {
|
|
if (runOnLoad) {
|
|
runOnLoadIfFound(t, lib);
|
|
}
|
|
} else {
|
|
throwNew(t, Machine::UnsatisfiedLinkErrorType, "library not found: %s",
|
|
name);
|
|
}
|
|
|
|
return lib;
|
|
}
|
|
|
|
object
|
|
clone(Thread* t, object o)
|
|
{
|
|
PROTECT(t, o);
|
|
|
|
object class_ = objectClass(t, o);
|
|
unsigned size = baseSize(t, o, class_) * BytesPerWord;
|
|
object clone;
|
|
|
|
if (classArrayElementSize(t, class_)) {
|
|
clone = static_cast<object>(allocate(t, size, classObjectMask(t, class_)));
|
|
memcpy(clone, o, size);
|
|
// clear any object header flags:
|
|
setObjectClass(t, o, objectClass(t, o));
|
|
} else {
|
|
clone = make(t, class_);
|
|
memcpy(reinterpret_cast<void**>(clone) + 1,
|
|
reinterpret_cast<void**>(o) + 1,
|
|
size - BytesPerWord);
|
|
}
|
|
|
|
return clone;
|
|
}
|
|
|
|
object
|
|
makeStackTraceElement(Thread* t, object e)
|
|
{
|
|
PROTECT(t, e);
|
|
|
|
object class_ = className(t, methodClass(t, traceElementMethod(t, e)));
|
|
PROTECT(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);
|
|
|
|
object method = methodName(t, traceElementMethod(t, e));
|
|
PROTECT(t, method);
|
|
|
|
method = t->m->classpath->makeString
|
|
(t, method, 0, byteArrayLength(t, method) - 1);
|
|
|
|
unsigned line = t->m->processor->lineNumber
|
|
(t, traceElementMethod(t, e), traceElementIp(t, e));
|
|
|
|
object file = classSourceFile(t, methodClass(t, traceElementMethod(t, e)));
|
|
file = file ? t->m->classpath->makeString
|
|
(t, file, 0, byteArrayLength(t, file) - 1) : 0;
|
|
|
|
return makeStackTraceElement(t, class_, method, file, line);
|
|
}
|
|
|
|
} // namespace vm
|
|
|
|
#endif//CLASSPATH_COMMON_H
|