2010-09-10 21:05:29 +00:00
|
|
|
/* 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
|
|
|
|
|
2010-09-20 23:31:23 +00:00
|
|
|
#include "tokenizer.h"
|
|
|
|
|
2010-09-10 21:05:29 +00:00
|
|
|
namespace vm {
|
|
|
|
|
2010-09-20 23:31:23 +00:00
|
|
|
object
|
2010-09-10 21:05:29 +00:00
|
|
|
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
|
2010-09-14 16:49:41 +00:00
|
|
|
(t, type(t, Machine::ThrowableType), methodClass(t, method))
|
2010-09-10 21:05:29 +00:00
|
|
|
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);
|
|
|
|
|
2010-09-14 16:49:41 +00:00
|
|
|
if (v.trace == 0) v.trace = makeObjectArray(t, 0);
|
2010-09-10 21:05:29 +00:00
|
|
|
|
|
|
|
return v.trace;
|
|
|
|
}
|
|
|
|
|
2010-09-20 23:31:23 +00:00
|
|
|
bool
|
2010-09-10 21:05:29 +00:00
|
|
|
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))));
|
|
|
|
}
|
|
|
|
|
2010-09-20 23:31:23 +00:00
|
|
|
void
|
2010-09-10 21:05:29 +00:00
|
|
|
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 {
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-10 02:38:12 +00:00
|
|
|
t->exception = makeThrowable
|
2010-09-10 21:05:29 +00:00
|
|
|
(t, Machine::IndexOutOfBoundsExceptionType);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-10 02:38:12 +00:00
|
|
|
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
2010-09-10 21:05:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-10 02:38:12 +00:00
|
|
|
t->exception = makeThrowable(t, Machine::ArrayStoreExceptionType);
|
2010-09-10 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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*
|
2010-09-20 23:31:23 +00:00
|
|
|
loadLibrary(Thread* t, const char* name)
|
2010-09-10 21:05:29 +00:00
|
|
|
{
|
|
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
|
|
|
|
System::Library* last = t->m->libraries;
|
|
|
|
for (System::Library* lib = t->m->libraries; lib; lib = lib->next()) {
|
2010-09-20 23:31:23 +00:00
|
|
|
if (lib->name() and ::strcmp(lib->name(), name) == 0) {
|
2010-09-10 21:05:29 +00:00
|
|
|
// already loaded
|
|
|
|
return lib;
|
|
|
|
}
|
|
|
|
last = lib;
|
|
|
|
}
|
|
|
|
|
|
|
|
System::Library* lib;
|
2010-09-22 19:58:46 +00:00
|
|
|
if (t->m->system->success(t->m->system->load(&lib, name))) {
|
2010-09-10 21:05:29 +00:00
|
|
|
last->setNext(lib);
|
2010-09-20 23:31:23 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
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:
|
2010-09-20 23:31:23 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
RUNTIME_ARRAY(char, fullName, fullNameLength + 1);
|
|
|
|
|
|
|
|
snprintf(RUNTIME_ARRAY_BODY(fullName), fullNameLength + 1,
|
2010-11-07 17:08:04 +00:00
|
|
|
"%*s/%s", token.length, token.s, name);
|
2010-09-20 23:31:23 +00:00
|
|
|
|
|
|
|
lib = loadLibrary(t, RUNTIME_ARRAY_BODY(fullName));
|
|
|
|
if (lib) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lib == 0) {
|
|
|
|
lib = loadLibrary(t, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lib) {
|
2010-09-10 21:05:29 +00:00
|
|
|
if (runOnLoad) {
|
|
|
|
runOnLoadIfFound(t, lib);
|
|
|
|
}
|
2010-09-20 23:31:23 +00:00
|
|
|
} else {
|
2010-09-10 21:05:29 +00:00
|
|
|
object message = makeString(t, "library not found: %s", name);
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-10 02:38:12 +00:00
|
|
|
t->exception = makeThrowable
|
2010-09-10 21:05:29 +00:00
|
|
|
(t, Machine::UnsatisfiedLinkErrorType, message);
|
|
|
|
}
|
2010-09-20 23:31:23 +00:00
|
|
|
|
|
|
|
if (mapName) {
|
|
|
|
t->m->heap->free(name, nameLength + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return lib;
|
2010-09-10 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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_);
|
|
|
|
|
|
|
|
RUNTIME_ARRAY(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
|