corda/src/avian/classpath-common.h

784 lines
22 KiB
C
Raw Normal View History

2014-04-21 02:14:48 +00:00
/* Copyright (c) 2008-2014, 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 <avian/util/string.h>
2013-02-20 05:56:05 +00:00
#include <avian/util/runtime-array.h>
using namespace avian::util;
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) {
2014-05-29 04:17:25 +00:00
GcMethod* method = walker->method();
if (isAssignableFrom
(t, type(t, GcThrowable::Type), method->class_())
and vm::strcmp(reinterpret_cast<const int8_t*>("<init>"),
method->name()->body().begin())
== 0)
{
return true;
} else {
2014-05-29 04:17:25 +00:00
trace = reinterpret_cast<object>(makeTrace(t, walker));
return false;
}
} else {
-- skipCount;
return true;
}
}
Thread* t;
object trace;
unsigned skipCount;
} v(t, skipCount);
t->m->processor->walkStack(t, &v);
2014-07-11 14:38:56 +00:00
if (v.trace == 0) v.trace = reinterpret_cast<object>(makeObjectArray(t, 0));
return v.trace;
}
bool
2014-07-11 14:38:56 +00:00
compatibleArrayTypes(Thread* t, object ao, object bo)
{
2014-07-11 14:38:56 +00:00
GcClass* a = cast<GcClass>(t, ao);
GcClass* b = cast<GcClass>(t, bo);
return a->arrayElementSize()
and b->arrayElementSize()
and (a == b
2014-07-11 14:38:56 +00:00
or (not ((a->vmFlags() & PrimitiveFlag)
or (b->vmFlags() & 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
2014-05-29 04:17:25 +00:00
(t, reinterpret_cast<object>(objectClass(t, src)), reinterpret_cast<object>(objectClass(t, dst)))))
{
2014-05-29 04:17:25 +00:00
unsigned elementSize = objectClass(t, src)->arrayElementSize();
if (LIKELY(elementSize)) {
2013-02-11 00:38:51 +00:00
intptr_t sl = fieldAtOffset<uintptr_t>(src, BytesPerWord);
intptr_t dl = fieldAtOffset<uintptr_t>(dst, BytesPerWord);
if (LIKELY(length > 0)) {
if (LIKELY(srcOffset >= 0 and srcOffset + length <= sl and
dstOffset >= 0 and dstOffset + length <= dl))
{
2013-02-11 00:38:51 +00:00
uint8_t* sbody = &fieldAtOffset<uint8_t>(src, ArrayBody);
uint8_t* dbody = &fieldAtOffset<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);
}
2014-05-29 04:17:25 +00:00
if (objectClass(t, dst)->objectMask()) {
mark(t, dst, ArrayBody + (dstOffset * BytesPerWord), length);
}
return;
} else {
2014-05-29 04:17:25 +00:00
throwNew(t, GcIndexOutOfBoundsException::Type);
}
} else {
return;
}
}
}
} else {
2014-05-29 04:17:25 +00:00
throwNew(t, GcNullPointerException::Type);
return;
}
2014-05-29 04:17:25 +00:00
throwNew(t, GcArrayStoreException::Type);
}
void
runOnLoadIfFound(Thread* t, System::Library* library)
{
void* p = library->resolve("JNI_OnLoad");
#ifdef PLATFORM_WINDOWS
if (p == 0) {
p = library->resolve("_JNI_OnLoad@8");
if (p == 0) {
p = library->resolve("JNI_OnLoad@8");
}
}
#endif
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, bool throw_ = true)
{
ACQUIRE(t, t->m->classLock);
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.
2010-12-27 22:55:23 +00:00
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;
2010-12-09 04:36:02 +00:00
// 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);
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.
2010-12-27 22:55:23 +00:00
mappedName = static_cast<char*>
(t->m->heap->allocate(mappedNameLength + 1));
snprintf(mappedName, mappedNameLength + 1, "%s%s%s", prefix, name, suffix);
name = mappedName;
nameLength = mappedNameLength;
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.
2010-12-27 22:55:23 +00:00
} else {
mappedName = 0;
}
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.
2010-12-27 22:55:23 +00:00
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();)
{
String token(tokenizer.next());
unsigned fullNameLength = token.length + 1 + nameLength;
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.
2010-12-27 22:55:23 +00:00
THREAD_RUNTIME_ARRAY(t, char, fullName, fullNameLength + 1);
snprintf(RUNTIME_ARRAY_BODY(fullName), fullNameLength + 1,
"%.*s/%s", token.length, token.text, 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 if (throw_) {
2014-05-29 04:17:25 +00:00
throwNew(t, GcUnsatisfiedLinkError::Type,
"library not found in %s: %s", path, name);
}
return lib;
}
object
clone(Thread* t, object o)
{
PROTECT(t, o);
2014-05-29 04:17:25 +00:00
GcClass* class_ = objectClass(t, o);
unsigned size = baseSize(t, o, class_) * BytesPerWord;
object clone;
2014-05-29 04:17:25 +00:00
if (class_->arrayElementSize()) {
clone = static_cast<object>(allocate(t, size, class_->objectMask()));
memcpy(clone, o, size);
// clear any object header flags:
setObjectClass(t, o, objectClass(t, o));
2014-05-29 04:17:25 +00:00
} else if (instanceOf(t, type(t, GcCloneable::Type), o)) {
clone = make(t, class_);
memcpy(reinterpret_cast<void**>(clone) + 1,
reinterpret_cast<void**>(o) + 1,
size - BytesPerWord);
} else {
2014-07-11 14:38:56 +00:00
GcByteArray* classNameSlash = objectClass(t, o)->name();
THREAD_RUNTIME_ARRAY(t, char, classNameDot, classNameSlash->length());
replace('/', '.', RUNTIME_ARRAY_BODY(classNameDot),
2014-07-11 14:38:56 +00:00
reinterpret_cast<char*>(classNameSlash->body().begin()));
2014-05-29 04:17:25 +00:00
throwNew(t, GcCloneNotSupportedException::Type, "%s",
RUNTIME_ARRAY_BODY(classNameDot));
}
return clone;
}
2014-07-11 14:38:56 +00:00
GcStackTraceElement*
makeStackTraceElement(Thread* t, GcTraceElement* e)
{
PROTECT(t, e);
2014-07-11 14:38:56 +00:00
GcMethod* method = cast<GcMethod>(t, e->method());
2014-06-02 16:31:57 +00:00
PROTECT(t, method);
2014-07-11 14:38:56 +00:00
GcByteArray* class_name = method->class_()->name();
2014-05-29 04:17:25 +00:00
PROTECT(t, class_name);
2014-07-11 14:38:56 +00:00
THREAD_RUNTIME_ARRAY(t, char, s, class_name->length());
replace('/', '.', RUNTIME_ARRAY_BODY(s),
2014-07-11 14:38:56 +00:00
reinterpret_cast<char*>(class_name->body().begin()));
GcString* class_name_string = makeString(t, "%s", RUNTIME_ARRAY_BODY(s));
PROTECT(t, class_name_string);
2014-07-11 14:38:56 +00:00
GcByteArray* method_name = method->name();
2014-06-02 16:31:57 +00:00
PROTECT(t, method_name);
2014-07-11 14:38:56 +00:00
GcString* method_name_string = t->m->classpath->makeString
(t, reinterpret_cast<object>(method_name), 0, method_name->length() - 1);
PROTECT(t, method_name_string);
unsigned line = t->m->processor->lineNumber
2014-07-11 14:38:56 +00:00
(t, method, e->ip());
2014-07-11 14:38:56 +00:00
GcByteArray* file = method->class_()->sourceFile();
GcString* file_string = file ? t->m->classpath->makeString
(t, reinterpret_cast<object>(file), 0, file->length() - 1) : 0;
2014-07-11 14:38:56 +00:00
return makeStackTraceElement(t, class_name_string, method_name_string, file_string, line);
}
object
translateInvokeResult(Thread* t, unsigned returnCode, object o)
{
switch (returnCode) {
case ByteField:
2014-07-11 14:38:56 +00:00
return reinterpret_cast<object>(makeByte(t, cast<GcInt>(t, o)->value()));
case BooleanField:
2014-07-11 14:38:56 +00:00
return reinterpret_cast<object>(makeBoolean(t, cast<GcInt>(t, o)->value() != 0));
case CharField:
2014-07-11 14:38:56 +00:00
return reinterpret_cast<object>(makeChar(t, cast<GcInt>(t, o)->value()));
case ShortField:
2014-07-11 14:38:56 +00:00
return reinterpret_cast<object>(makeShort(t, cast<GcInt>(t, o)->value()));
case FloatField:
2014-07-11 14:38:56 +00:00
return reinterpret_cast<object>(makeFloat(t, cast<GcInt>(t, o)->value()));
case IntField:
case LongField:
case ObjectField:
case VoidField:
return o;
case DoubleField:
2014-07-11 14:38:56 +00:00
return reinterpret_cast<object>(makeDouble(t, cast<GcLong>(t, o)->value()));
default:
abort(t);
}
}
2014-05-29 04:17:25 +00:00
GcClass*
2014-06-28 21:11:31 +00:00
resolveClassBySpec(Thread* t, GcClassLoader* loader, const char* spec,
unsigned specLength)
{
switch (*spec) {
case 'L': {
THREAD_RUNTIME_ARRAY(t, char, s, specLength - 1);
memcpy(RUNTIME_ARRAY_BODY(s), spec + 1, specLength - 2);
RUNTIME_ARRAY_BODY(s)[specLength - 2] = 0;
2013-02-22 21:50:15 +00:00
return resolveClass(t, loader, RUNTIME_ARRAY_BODY(s));
}
2014-05-29 04:17:25 +00:00
case '[': {
THREAD_RUNTIME_ARRAY(t, char, s, specLength + 1);
memcpy(RUNTIME_ARRAY_BODY(s), spec, specLength);
RUNTIME_ARRAY_BODY(s)[specLength] = 0;
2013-02-22 21:50:15 +00:00
return resolveClass(t, loader, RUNTIME_ARRAY_BODY(s));
}
default:
return primitiveClass(t, *spec);
}
}
GcJclass*
2014-06-28 21:11:31 +00:00
resolveJType(Thread* t, GcClassLoader* loader, const char* spec, unsigned specLength)
2013-02-21 22:37:17 +00:00
{
return getJClass(t, resolveClassBySpec(t, loader, spec, specLength));
}
2014-07-11 14:38:56 +00:00
GcPair*
2014-06-28 21:11:31 +00:00
resolveParameterTypes(Thread* t, GcClassLoader* loader, GcByteArray* spec,
2013-02-21 22:37:17 +00:00
unsigned* parameterCount, unsigned* returnTypeSpec)
{
PROTECT(t, loader);
PROTECT(t, spec);
2014-07-11 14:38:56 +00:00
GcPair* list = 0;
2013-02-21 22:37:17 +00:00
PROTECT(t, list);
unsigned offset = 1;
unsigned count = 0;
2014-06-28 21:11:31 +00:00
while (spec->body()[offset] != ')') {
switch (spec->body()[offset]) {
2013-02-21 22:37:17 +00:00
case 'L': {
unsigned start = offset;
++ offset;
2014-06-28 21:11:31 +00:00
while (spec->body()[offset] != ';') ++ offset;
2013-02-21 22:37:17 +00:00
++ offset;
2014-05-29 04:17:25 +00:00
GcClass* type = resolveClassBySpec
2014-06-28 21:11:31 +00:00
(t, loader, reinterpret_cast<char*>(&spec->body()[start]),
2013-02-21 22:37:17 +00:00
offset - start);
2014-05-29 04:17:25 +00:00
2014-07-11 14:38:56 +00:00
list = makePair(t, reinterpret_cast<object>(type), reinterpret_cast<object>(list));
2013-02-21 22:37:17 +00:00
++ count;
} break;
2014-05-29 04:17:25 +00:00
2013-02-21 22:37:17 +00:00
case '[': {
unsigned start = offset;
2014-06-28 21:11:31 +00:00
while (spec->body()[offset] == '[') ++ offset;
switch (spec->body()[offset]) {
2013-02-21 22:37:17 +00:00
case 'L':
++ offset;
2014-06-28 21:11:31 +00:00
while (spec->body()[offset] != ';') ++ offset;
2013-02-21 22:37:17 +00:00
++ offset;
break;
default:
++ offset;
break;
}
2014-05-29 04:17:25 +00:00
GcClass* type = resolveClassBySpec
2014-06-28 21:11:31 +00:00
(t, loader, reinterpret_cast<char*>(&spec->body()[start]),
2013-02-21 22:37:17 +00:00
offset - start);
2014-05-29 04:17:25 +00:00
2014-07-11 14:38:56 +00:00
list = makePair(t, reinterpret_cast<object>(type), reinterpret_cast<object>(list));
2013-02-21 22:37:17 +00:00
++ count;
} break;
default:
2014-07-11 14:38:56 +00:00
list = makePair
(t, reinterpret_cast<object>(primitiveClass(t, spec->body()[offset])), reinterpret_cast<object>(list));
2013-02-21 22:37:17 +00:00
++ offset;
++ count;
break;
}
}
*parameterCount = count;
*returnTypeSpec = offset + 1;
return list;
}
object
2014-06-28 21:11:31 +00:00
resolveParameterJTypes(Thread* t, GcClassLoader* loader, GcByteArray* spec,
2013-02-21 22:37:17 +00:00
unsigned* parameterCount, unsigned* returnTypeSpec)
{
2014-07-11 14:38:56 +00:00
GcPair* list = resolveParameterTypes
2013-02-21 22:37:17 +00:00
(t, loader, spec, parameterCount, returnTypeSpec);
PROTECT(t, list);
2014-05-29 04:17:25 +00:00
2013-02-21 22:37:17 +00:00
object array = makeObjectArray
2014-05-29 04:17:25 +00:00
(t, type(t, GcJclass::Type), *parameterCount);
2013-02-21 22:37:17 +00:00
PROTECT(t, array);
for (int i = *parameterCount - 1; i >= 0; --i) {
2014-07-11 14:38:56 +00:00
object c = reinterpret_cast<object>(getJClass(t, cast<GcClass>(t, list->first())));
2013-02-21 22:37:17 +00:00
set(t, array, ArrayBody + (i * BytesPerWord), c);
2014-07-11 14:38:56 +00:00
list = cast<GcPair>(t, list->second());
2013-02-21 22:37:17 +00:00
}
return array;
}
object
2014-06-28 21:11:31 +00:00
resolveExceptionJTypes(Thread* t, GcClassLoader* loader, GcMethodAddendum* addendum)
2013-02-21 22:37:17 +00:00
{
2014-06-28 21:11:31 +00:00
if (addendum == 0 or addendum->exceptionTable() == 0) {
2014-05-29 04:17:25 +00:00
return makeObjectArray(t, type(t, GcJclass::Type), 0);
2013-02-21 22:37:17 +00:00
}
PROTECT(t, loader);
PROTECT(t, addendum);
2014-07-11 14:38:56 +00:00
GcShortArray* exceptionTable = cast<GcShortArray>(t, addendum->exceptionTable());
PROTECT(t, exceptionTable);
2013-02-21 22:37:17 +00:00
object array = makeObjectArray
2014-05-29 04:17:25 +00:00
(t, type(t, GcJclass::Type),
2014-07-11 14:38:56 +00:00
exceptionTable->length());
2013-02-21 22:37:17 +00:00
PROTECT(t, array);
2014-07-11 14:38:56 +00:00
for (unsigned i = 0; i < exceptionTable->length(); ++i)
2013-02-21 22:37:17 +00:00
{
2014-07-11 14:38:56 +00:00
uint16_t index = exceptionTable->body()[i] - 1;
2013-02-21 22:37:17 +00:00
2014-07-11 14:38:56 +00:00
object o = singletonObject(t, addendum->pool()->as<GcSingleton>(t), index);
2014-05-29 04:17:25 +00:00
if (objectClass(t, o) == type(t, GcReference::Type)) {
2014-06-29 03:18:22 +00:00
o = reinterpret_cast<object>(resolveClass(t, loader, cast<GcReference>(t, o)->name()));
2013-02-21 22:37:17 +00:00
2014-06-28 21:11:31 +00:00
set(t, reinterpret_cast<object>(addendum->pool()), SingletonBody + (index * BytesPerWord),
2013-02-21 22:37:17 +00:00
o);
}
o = reinterpret_cast<object>(getJClass(t, cast<GcClass>(t, o)));
2013-02-21 22:37:17 +00:00
2014-06-27 00:17:16 +00:00
set(t, reinterpret_cast<object>(array), ArrayBody + (i * BytesPerWord), o);
2013-02-21 22:37:17 +00:00
}
return array;
}
object
2014-05-29 04:17:25 +00:00
invoke(Thread* t, GcMethod* method, object instance, object args)
2013-02-21 22:37:17 +00:00
{
PROTECT(t, method);
PROTECT(t, instance);
PROTECT(t, args);
2014-05-29 04:17:25 +00:00
if (method->flags() & ACC_STATIC) {
2013-02-21 22:37:17 +00:00
instance = 0;
}
if ((args == 0 ? 0 : objectArrayLength(t, args))
2014-05-29 04:17:25 +00:00
!= method->parameterCount())
2013-02-21 22:37:17 +00:00
{
2014-05-29 04:17:25 +00:00
throwNew(t, GcIllegalArgumentException::Type);
2013-02-21 22:37:17 +00:00
}
2014-05-29 04:17:25 +00:00
if (method->parameterCount()) {
unsigned specLength = method->spec()->length();
2013-02-21 22:37:17 +00:00
THREAD_RUNTIME_ARRAY(t, char, spec, specLength);
2013-02-22 21:50:15 +00:00
memcpy(RUNTIME_ARRAY_BODY(spec),
method->spec()->body().begin(), specLength);
2013-02-21 22:37:17 +00:00
unsigned i = 0;
2013-02-22 21:50:15 +00:00
for (MethodSpecIterator it(t, RUNTIME_ARRAY_BODY(spec)); it.hasNext();) {
2014-05-29 04:17:25 +00:00
GcClass* type;
2013-02-21 22:37:17 +00:00
bool objectType = false;
const char* p = it.next();
switch (*p) {
2014-05-29 04:17:25 +00:00
case 'Z': type = vm::type(t, GcBoolean::Type); break;
case 'B': type = vm::type(t, GcByte::Type); break;
case 'S': type = vm::type(t, GcShort::Type); break;
case 'C': type = vm::type(t, GcChar::Type); break;
case 'I': type = vm::type(t, GcInt::Type); break;
case 'F': type = vm::type(t, GcFloat::Type); break;
case 'J': type = vm::type(t, GcLong::Type); break;
case 'D': type = vm::type(t, GcDouble::Type); break;
2013-02-21 22:37:17 +00:00
case 'L':
2013-02-21 22:37:17 +00:00
case '[': {
objectType = true;
unsigned nameLength;
if (*p == 'L') {
++ p;
nameLength = it.s - p;
} else {
nameLength = (it.s - p) + 1;
}
2013-02-21 22:37:17 +00:00
THREAD_RUNTIME_ARRAY(t, char, name, nameLength);
2013-02-22 21:50:15 +00:00
memcpy(RUNTIME_ARRAY_BODY(name), p, nameLength - 1);
RUNTIME_ARRAY_BODY(name)[nameLength - 1] = 0;
2013-02-21 22:37:17 +00:00
type = resolveClass
2014-06-28 21:11:31 +00:00
(t, method->class_()->loader(),
2013-02-22 21:50:15 +00:00
RUNTIME_ARRAY_BODY(name));
2013-02-21 22:37:17 +00:00
} break;
default:
abort();
}
object arg = objectArrayBody(t, args, i++);
if ((arg == 0 and (not objectType))
or (arg and (not instanceOf(t, type, arg))))
{
// fprintf(stderr, "%s is not a %s\n", arg ? &byteArrayBody(t, className(t, objectClass(t, arg)), 0) : reinterpret_cast<const int8_t*>("<null>"), &byteArrayBody(t, className(t, type), 0));
2014-05-29 04:17:25 +00:00
throwNew(t, GcIllegalArgumentException::Type);
2013-02-21 22:37:17 +00:00
}
}
}
initClass(t, method->class_());
2014-05-29 04:17:25 +00:00
unsigned returnCode = method->returnCode();
2013-02-21 22:37:17 +00:00
THREAD_RESOURCE0(t, {
if (t->exception) {
t->exception = makeThrowable
2014-05-29 04:17:25 +00:00
(t, GcInvocationTargetException::Type, 0, 0, t->exception);
set(t, t->exception, InvocationTargetExceptionTarget,
2014-06-28 23:24:24 +00:00
t->exception->cause());
2013-02-21 22:37:17 +00:00
}
});
object result;
if (args) {
result = t->m->processor->invokeArray(t, method, instance, args);
} else {
result = t->m->processor->invoke(t, method, instance);
}
return translateInvokeResult(t, returnCode, result);
}
// only safe to call during bootstrap when there's only one thread
// running:
void
2014-05-29 04:17:25 +00:00
intercept(Thread* t, GcClass* c, const char* name, const char* spec,
void* function, bool updateRuntimeData)
2013-02-21 22:37:17 +00:00
{
2014-05-29 04:17:25 +00:00
GcMethod* m = findMethodOrNull(t, c, name, spec);
2013-02-21 22:37:17 +00:00
if (m) {
PROTECT(t, m);
2014-05-29 04:17:25 +00:00
m->flags() |= ACC_NATIVE;
if (updateRuntimeData) {
2014-05-29 04:17:25 +00:00
GcMethod* clone = methodClone(t, m);
2013-02-21 22:37:17 +00:00
// make clone private to prevent vtable updates at compilation
// time. Otherwise, our interception might be bypassed by calls
// through the vtable.
2014-05-29 04:17:25 +00:00
clone->flags() |= ACC_PRIVATE;
2014-07-11 14:38:56 +00:00
GcNativeIntercept* native = makeNativeIntercept(t, function, true, reinterpret_cast<object>(clone));
2013-02-21 22:37:17 +00:00
PROTECT(t, native);
2014-05-29 04:17:25 +00:00
2014-07-11 14:38:56 +00:00
GcMethodRuntimeData* runtimeData = getMethodRuntimeData(t, m);
2014-05-29 04:17:25 +00:00
set(t, runtimeData, MethodRuntimeDataNative, native);
}
2013-02-21 22:37:17 +00:00
} else {
// If we can't find the method, just ignore it, since ProGuard may
// have stripped it out as unused. Otherwise, the code below can
// be uncommented for debugging purposes.
// fprintf(stderr, "unable to find %s%s in %s\n",
2014-07-11 14:38:56 +00:00
// name, spec, &byteArrayBody(t, c->name(), 0));
2013-02-21 22:37:17 +00:00
// abort(t);
}
}
Finder*
getFinder(Thread* t, const char* name, unsigned nameLength)
{
ACQUIRE(t, t->m->referenceLock);
2014-05-29 04:17:25 +00:00
2014-06-30 01:44:41 +00:00
for (GcFinder* p = roots(t)->virtualFileFinders();
2014-07-11 14:38:56 +00:00
p; p = p->next())
{
2014-07-11 14:38:56 +00:00
if (p->name()->length() == nameLength
and strncmp(reinterpret_cast<const char*>
2014-07-11 14:38:56 +00:00
(p->name()->body().begin()),
name, nameLength))
{
2014-07-11 14:38:56 +00:00
return static_cast<Finder*>(p->finder());
}
}
2014-07-11 14:38:56 +00:00
GcByteArray* n = makeByteArray(t, nameLength + 1);
memcpy(n->body().begin(), name, nameLength);
void* p = t->m->libraries->resolve
2014-07-11 14:38:56 +00:00
(reinterpret_cast<const char*>(n->body().begin()));
if (p) {
uint8_t* (*function)(unsigned*);
memcpy(&function, &p, BytesPerWord);
unsigned size;
uint8_t* data = function(&size);
if (data) {
Finder* f = makeFinder(t->m->system, t->m->heap, data, size);
2014-07-11 14:38:56 +00:00
GcFinder* finder = makeFinder
2014-06-30 01:44:41 +00:00
(t, f, n, roots(t)->virtualFileFinders());
2014-06-30 01:44:41 +00:00
set(t, roots(t), RootsVirtualFileFinders, finder);
return f;
}
}
return 0;
}
object
2014-07-11 14:38:56 +00:00
getDeclaredClasses(Thread* t, GcClass* c, bool publicOnly)
{
2014-07-11 14:38:56 +00:00
GcClassAddendum* addendum = c->addendum();
if (addendum) {
2014-07-11 14:38:56 +00:00
GcArray* table = cast<GcArray>(t, addendum->innerClassTable());
if (table) {
PROTECT(t, table);
unsigned count = 0;
2014-07-11 14:38:56 +00:00
for (unsigned i = 0; i < table->length(); ++i) {
GcInnerClassReference* reference = cast<GcInnerClassReference>(t, table->body()[i]);
GcByteArray* outer = reference->outer();
if (outer and byteArrayEqual(t, reinterpret_cast<object>(outer), reinterpret_cast<object>(c->name()))
and ((not publicOnly)
2014-07-11 14:38:56 +00:00
or (reference->flags() & ACC_PUBLIC)))
{
++ count;
}
}
2014-05-29 04:17:25 +00:00
object result = makeObjectArray(t, type(t, GcJclass::Type), count);
PROTECT(t, result);
2014-07-11 14:38:56 +00:00
for (unsigned i = 0; i < table->length(); ++i) {
GcInnerClassReference* reference = cast<GcInnerClassReference>(t, table->body()[i]);
GcByteArray* outer = reference->outer();
if (outer and byteArrayEqual(t, reinterpret_cast<object>(outer), reinterpret_cast<object>(c->name()))
and ((not publicOnly)
2014-07-11 14:38:56 +00:00
or (reference->flags() & ACC_PUBLIC)))
{
object inner = reinterpret_cast<object>(getJClass(
2014-05-29 04:17:25 +00:00
t,
resolveClass(
t,
2014-07-11 14:38:56 +00:00
c->loader(),
reference->inner())));
2014-05-29 04:17:25 +00:00
-- count;
set(t, result, ArrayBody + (count * BytesPerWord), inner);
}
}
return result;
}
}
2014-05-29 04:17:25 +00:00
return makeObjectArray(t, type(t, GcJclass::Type), 0);
}
GcJclass*
2014-07-11 14:38:56 +00:00
getDeclaringClass(Thread* t, GcClass* c)
{
2014-07-11 14:38:56 +00:00
GcClassAddendum* addendum = c->addendum();
if (addendum) {
2014-07-11 14:38:56 +00:00
GcArray* table = cast<GcArray>(t, addendum->innerClassTable());
if (table) {
2014-07-11 14:38:56 +00:00
for (unsigned i = 0; i < table->length(); ++i) {
GcInnerClassReference* reference = cast<GcInnerClassReference>(t, table->body()[i]);
if (reference->outer() and strcmp
(reference->inner()->body().begin(),
c->name()->body().begin()) == 0)
{
2014-05-29 04:17:25 +00:00
return getJClass(
t,
resolveClass(t,
2014-07-11 14:38:56 +00:00
c->loader(),
reference->outer()));
}
}
}
}
return 0;
}
unsigned
2014-07-11 14:38:56 +00:00
classModifiers(Thread* t, GcClass* c)
{
2014-07-11 14:38:56 +00:00
GcClassAddendum* addendum = c->addendum();
if (addendum) {
2014-07-11 14:38:56 +00:00
GcArray* table = cast<GcArray>(t, addendum->innerClassTable());
if (table) {
2014-07-11 14:38:56 +00:00
for (unsigned i = 0; i < table->length(); ++i) {
GcInnerClassReference* reference = cast<GcInnerClassReference>(t, table->body()[i]);
if (0 == strcmp
2014-07-11 14:38:56 +00:00
(c->name()->body().begin(),
reference->inner()->body().begin()))
{
2014-07-11 14:38:56 +00:00
return reference->flags();
2014-05-29 04:17:25 +00:00
}
}
}
}
2014-07-11 14:38:56 +00:00
return c->flags();
}
} // namespace vm
#endif//CLASSPATH_COMMON_H