corda/src/classpath-avian.cpp

847 lines
24 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. */
#include "avian/machine.h"
#include "avian/classpath-common.h"
#include "avian/process.h"
2013-02-20 05:56:05 +00:00
#include <avian/util/runtime-array.h>
using namespace vm;
namespace {
namespace local {
class MyClasspath : public Classpath {
public:
2014-07-11 15:50:18 +00:00
MyClasspath(Allocator* allocator) : allocator(allocator)
{
}
2014-07-11 15:47:57 +00:00
virtual GcJclass* makeJclass(Thread* t, GcClass* class_)
{
2014-06-26 02:17:27 +00:00
return vm::makeJclass(t, class_);
}
2014-07-11 15:47:57 +00:00
virtual GcString* makeString(Thread* t,
object array,
int32_t offset,
int32_t length)
{
2014-06-28 23:24:24 +00:00
return vm::makeString(t, array, offset, length, 0);
}
2014-07-11 15:47:57 +00:00
virtual GcThread* makeThread(Thread* t, Thread* parent)
{
2014-06-28 23:24:24 +00:00
GcThreadGroup* group;
if (parent) {
2014-06-28 23:24:24 +00:00
group = parent->javaThread->group();
} else {
2014-06-28 23:24:24 +00:00
group = makeThreadGroup(t, 0, 0, 0);
}
const unsigned NewState = 0;
const unsigned NormalPriority = 5;
2014-07-11 15:47:57 +00:00
return vm::makeThread(t,
0,
0,
0,
0,
0,
NewState,
NormalPriority,
0,
0,
0,
roots(t)->appLoader(),
0,
0,
group,
0);
}
virtual object makeJMethod(Thread* t, GcMethod* vmMethod)
{
PROTECT(t, vmMethod);
GcJmethod* jmethod = makeJmethod(t, vmMethod, false);
return vmMethod->name()->body()[0] == '<'
2014-07-11 15:47:57 +00:00
? (object)makeJconstructor(t, jmethod)
: (object)jmethod;
}
2014-07-11 15:47:57 +00:00
virtual GcMethod* getVMMethod(Thread* t, object jmethod)
{
2014-05-29 04:17:25 +00:00
return objectClass(t, jmethod) == type(t, GcJmethod::Type)
2014-07-11 15:47:57 +00:00
? cast<GcJmethod>(t, jmethod)->vmMethod()
: cast<GcJconstructor>(t, jmethod)->method()->vmMethod();
}
2014-07-11 15:47:57 +00:00
virtual object makeJField(Thread* t, GcField* vmField)
{
return makeJfield(t, vmField, false);
}
2014-07-11 15:47:57 +00:00
virtual GcField* getVMField(Thread* t UNUSED, GcJfield* jfield)
{
2014-06-27 00:17:46 +00:00
return jfield->vmField();
}
2014-07-11 15:50:18 +00:00
virtual void clearInterrupted(Thread*)
{
// ignore
}
2014-07-11 15:50:18 +00:00
virtual void runThread(Thread* t)
{
2014-07-11 15:47:57 +00:00
GcMethod* method = resolveMethod(t,
roots(t)->bootLoader(),
"java/lang/Thread",
"run",
"(Ljava/lang/Thread;)V");
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
t->m->processor->invoke(t, method, 0, t->javaThread);
}
2014-07-11 15:47:57 +00:00
virtual void resolveNative(Thread* t, GcMethod* method)
{
vm::resolveNative(t, method);
}
2014-07-11 15:50:18 +00:00
virtual void interceptMethods(Thread*)
{
// ignore
}
2014-07-11 15:50:18 +00:00
virtual void preBoot(Thread*)
2013-02-22 21:41:24 +00:00
{
// ignore
}
virtual bool mayInitClasses()
{
return true;
}
2014-07-11 15:50:18 +00:00
virtual void boot(Thread*)
{
// ignore
}
2014-07-11 15:50:18 +00:00
virtual const char* bootClasspath()
{
return AVIAN_CLASSPATH;
}
2014-07-11 15:50:18 +00:00
virtual object makeDirectByteBuffer(Thread* t, void* p, jlong capacity)
2013-02-22 18:55:01 +00:00
{
2014-07-11 15:47:57 +00:00
GcClass* c
= resolveClass(t, roots(t)->bootLoader(), "java/nio/DirectByteBuffer");
2013-02-22 18:55:01 +00:00
PROTECT(t, c);
object instance = makeNew(t, c);
PROTECT(t, instance);
2014-05-29 04:17:25 +00:00
GcMethod* constructor = resolveMethod(t, c, "<init>", "(JI)V");
2013-02-22 18:55:01 +00:00
2014-07-11 15:50:18 +00:00
t->m->processor->invoke(t,
constructor,
instance,
reinterpret_cast<int64_t>(p),
static_cast<int32_t>(capacity));
2013-02-22 18:55:01 +00:00
return instance;
}
2014-07-11 15:50:18 +00:00
virtual void* getDirectBufferAddress(Thread* t, object b)
2013-02-22 18:55:01 +00:00
{
PROTECT(t, b);
2014-06-28 23:24:24 +00:00
GcField* field = resolveField(t, objectClass(t, b), "address", "J");
2013-02-22 18:55:01 +00:00
2014-07-11 15:47:57 +00:00
return reinterpret_cast<void*>(fieldAtOffset<int64_t>(b, field->offset()));
2013-02-22 18:55:01 +00:00
}
2014-07-11 15:50:18 +00:00
virtual int64_t getDirectBufferCapacity(Thread* t, object b)
2013-02-22 18:55:01 +00:00
{
PROTECT(t, b);
2014-07-11 15:47:57 +00:00
GcField* field = resolveField(t, objectClass(t, b), "capacity", "I");
2013-02-22 18:55:01 +00:00
2014-06-28 23:24:24 +00:00
return fieldAtOffset<int32_t>(b, field->offset());
2013-02-22 18:55:01 +00:00
}
2014-06-28 23:24:24 +00:00
virtual bool canTailCall(Thread* t UNUSED,
2014-05-29 04:17:25 +00:00
GcMethod*,
2014-06-28 23:24:24 +00:00
GcByteArray* calleeClassName,
GcByteArray* calleeMethodName,
GcByteArray*)
{
// we can't tail call System.load[Library] or
// Runtime.load[Library] due to their use of
// ClassLoader.getCaller, which gets confused if we elide stack
// frames.
return (
(strcmp("loadLibrary",
2014-06-28 23:24:24 +00:00
reinterpret_cast<char*>(calleeMethodName->body().begin()))
and strcmp("load",
2014-07-11 15:47:57 +00:00
reinterpret_cast<char*>(calleeMethodName->body().begin())))
or (strcmp("java/lang/System",
reinterpret_cast<char*>(calleeClassName->body().begin()))
and strcmp(
"java/lang/Runtime",
reinterpret_cast<char*>(calleeClassName->body().begin()))));
}
virtual GcClassLoader* libraryClassLoader(Thread* t, GcMethod* caller)
{
return (caller->class_() == type(t, Gc::ClassLoaderType)
and t->libraryLoadStack)
? t->libraryLoadStack->classLoader
: caller->class_()->loader();
}
2014-07-11 15:50:18 +00:00
virtual void shutDown(Thread*)
{
// ignore
}
2014-07-11 15:50:18 +00:00
virtual void dispose()
{
allocator->free(this, sizeof(*this));
}
Allocator* allocator;
};
2014-07-11 15:47:57 +00:00
void enumerateThreads(Thread* t,
Thread* x,
GcArray* array,
unsigned* index,
unsigned limit)
{
if (*index < limit) {
array->setBodyElement(t, *index, x->javaThread);
2014-07-11 15:50:18 +00:00
++(*index);
if (x->peer)
enumerateThreads(t, x->peer, array, index, limit);
2014-07-11 15:50:18 +00:00
if (x->child)
enumerateThreads(t, x->child, array, index, limit);
}
}
2014-07-11 15:50:18 +00:00
} // namespace local
2014-07-11 15:50:18 +00:00
} // namespace
namespace vm {
2014-07-11 15:50:18 +00:00
Classpath* makeClasspath(System*,
Allocator* allocator,
const char*,
const char*)
{
return new (allocator->allocate(sizeof(local::MyClasspath)))
2014-07-11 15:50:18 +00:00
local::MyClasspath(allocator);
}
2014-07-11 15:50:18 +00:00
} // namespace vm
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Object_toString(Thread* t, object, uintptr_t* arguments)
{
object this_ = reinterpret_cast<object>(arguments[0]);
unsigned hash = objectHash(t, this_);
2014-07-11 15:47:57 +00:00
GcString* s = makeString(
t, "%s@0x%x", objectClass(t, this_)->name()->body().begin(), hash);
return reinterpret_cast<int64_t>(s);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Object_getVMClass(Thread* t, object, uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
return reinterpret_cast<int64_t>(
objectClass(t, reinterpret_cast<object>(arguments[0])));
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Object_wait(Thread* t, object, uintptr_t* arguments)
{
object this_ = reinterpret_cast<object>(arguments[0]);
2014-07-11 15:50:18 +00:00
int64_t milliseconds;
memcpy(&milliseconds, arguments + 1, 8);
vm::wait(t, this_, milliseconds);
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Object_notify(Thread* t, object, uintptr_t* arguments)
{
object this_ = reinterpret_cast<object>(arguments[0]);
notify(t, this_);
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Object_notifyAll(Thread* t, object, uintptr_t* arguments)
{
object this_ = reinterpret_cast<object>(arguments[0]);
notifyAll(t, this_);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Object_hashCode(Thread* t, object, uintptr_t* arguments)
{
object this_ = reinterpret_cast<object>(arguments[0]);
return objectHash(t, this_);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Object_clone(Thread* t, object, uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
return reinterpret_cast<int64_t>(
clone(t, reinterpret_cast<object>(arguments[0])));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_io_ObjectInputStream_makeInstance(Thread* t,
object,
uintptr_t* arguments)
{
2014-05-29 04:17:25 +00:00
GcClass* c = cast<GcClass>(t, reinterpret_cast<object>(arguments[0]));
return reinterpret_cast<int64_t>(make(t, c));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_avian_LegacyObjectInputStream_makeInstance(Thread* t,
object,
uintptr_t* arguments)
{
return Avian_java_io_ObjectInputStream_makeInstance(t, NULL, arguments);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Field_getPrimitive(Thread* t,
object,
uintptr_t* arguments)
{
object instance = reinterpret_cast<object>(arguments[0]);
int code = arguments[1];
int offset = arguments[2];
switch (code) {
2014-07-11 15:50:18 +00:00
case ByteField:
2013-02-11 00:38:51 +00:00
return fieldAtOffset<int8_t>(instance, offset);
2014-07-11 15:50:18 +00:00
case BooleanField:
2013-02-11 00:38:51 +00:00
return fieldAtOffset<uint8_t>(instance, offset);
2014-07-11 15:50:18 +00:00
case CharField:
2013-02-11 00:38:51 +00:00
return fieldAtOffset<uint16_t>(instance, offset);
2014-07-11 15:50:18 +00:00
case ShortField:
2013-02-11 00:38:51 +00:00
return fieldAtOffset<int16_t>(instance, offset);
2014-07-11 15:50:18 +00:00
case IntField:
2013-02-11 00:38:51 +00:00
return fieldAtOffset<int32_t>(instance, offset);
2014-07-11 15:50:18 +00:00
case LongField:
2013-02-11 00:38:51 +00:00
return fieldAtOffset<int64_t>(instance, offset);
2014-07-11 15:50:18 +00:00
case FloatField:
2013-02-11 00:38:51 +00:00
return fieldAtOffset<uint32_t>(instance, offset);
2014-07-11 15:50:18 +00:00
case DoubleField:
2013-02-11 00:38:51 +00:00
return fieldAtOffset<uint64_t>(instance, offset);
default:
abort(t);
}
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Field_getObject(Thread*,
object,
uintptr_t* arguments)
{
object instance = reinterpret_cast<object>(arguments[0]);
int offset = arguments[1];
2013-02-11 00:38:51 +00:00
return reinterpret_cast<int64_t>(fieldAtOffset<object>(instance, offset));
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Field_setPrimitive(Thread* t,
object,
uintptr_t* arguments)
{
object instance = reinterpret_cast<object>(arguments[0]);
int code = arguments[1];
int offset = arguments[2];
2014-07-11 15:50:18 +00:00
int64_t value;
memcpy(&value, arguments + 3, 8);
switch (code) {
case ByteField:
2013-02-11 00:38:51 +00:00
fieldAtOffset<int8_t>(instance, offset) = static_cast<int8_t>(value);
break;
case BooleanField:
2013-02-11 00:38:51 +00:00
fieldAtOffset<uint8_t>(instance, offset) = static_cast<uint8_t>(value);
break;
case CharField:
2013-02-11 00:38:51 +00:00
fieldAtOffset<uint16_t>(instance, offset) = static_cast<uint16_t>(value);
break;
case ShortField:
2013-02-11 00:38:51 +00:00
fieldAtOffset<int16_t>(instance, offset) = static_cast<int16_t>(value);
break;
2014-07-11 15:50:18 +00:00
case IntField:
2013-02-11 00:38:51 +00:00
fieldAtOffset<int32_t>(instance, offset) = static_cast<int32_t>(value);
break;
2014-07-11 15:50:18 +00:00
case LongField:
2013-02-11 00:38:51 +00:00
fieldAtOffset<int64_t>(instance, offset) = static_cast<int64_t>(value);
break;
2014-07-11 15:50:18 +00:00
case FloatField:
2013-02-11 00:38:51 +00:00
fieldAtOffset<uint32_t>(instance, offset) = static_cast<uint32_t>(value);
break;
2014-07-11 15:50:18 +00:00
case DoubleField:
2013-02-11 00:38:51 +00:00
fieldAtOffset<uint64_t>(instance, offset) = static_cast<uint64_t>(value);
break;
default:
abort(t);
}
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Field_setObject(Thread* t,
object,
uintptr_t* arguments)
{
object instance = reinterpret_cast<object>(arguments[0]);
int offset = arguments[1];
object value = reinterpret_cast<object>(arguments[2]);
2014-06-26 02:17:27 +00:00
setField(t, instance, offset, value);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Constructor_make(Thread* t,
object,
uintptr_t* arguments)
{
2014-05-29 04:17:25 +00:00
GcClass* c = cast<GcClass>(t, reinterpret_cast<object>(arguments[0]));
return reinterpret_cast<int64_t>(make(t, c));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Method_getCaller(Thread* t, object, uintptr_t*)
{
return reinterpret_cast<int64_t>(getCaller(t, 2));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Method_invoke(Thread* t,
object,
uintptr_t* arguments)
{
2014-05-29 04:17:25 +00:00
GcMethod* method = cast<GcMethod>(t, reinterpret_cast<object>(arguments[0]));
object instance = reinterpret_cast<object>(arguments[1]);
object args = reinterpret_cast<object>(arguments[2]);
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_RESOURCE0(t, {
2014-07-11 15:50:18 +00:00
if (t->exception) {
GcThrowable* exception = t->exception;
t->exception = makeThrowable(
t, GcInvocationTargetException::Type, 0, 0, exception);
}
});
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
2014-05-29 04:17:25 +00:00
unsigned returnCode = method->returnCode();
2014-07-11 15:50:18 +00:00
return reinterpret_cast<int64_t>(translateInvokeResult(
t, returnCode, t->m->processor->invokeArray(t, method, instance, args)));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Array_getLength(Thread* t,
object,
uintptr_t* arguments)
{
object array = reinterpret_cast<object>(arguments[0]);
if (LIKELY(array)) {
2014-05-29 04:17:25 +00:00
unsigned elementSize = objectClass(t, array)->arrayElementSize();
if (LIKELY(elementSize)) {
2013-02-11 00:38:51 +00:00
return fieldAtOffset<uintptr_t>(array, BytesPerWord);
} else {
2014-05-29 04:17:25 +00:00
throwNew(t, GcIllegalArgumentException::Type);
}
} else {
2014-05-29 04:17:25 +00:00
throwNew(t, GcNullPointerException::Type);
}
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_reflect_Array_makeObjectArray(Thread* t,
object,
uintptr_t* arguments)
{
2014-07-11 15:47:57 +00:00
GcJclass* elementType
= cast<GcJclass>(t, reinterpret_cast<object>(arguments[0]));
int length = arguments[1];
2014-07-11 15:47:57 +00:00
return reinterpret_cast<int64_t>(
makeObjectArray(t, elementType->vmClass(), length));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Float_floatToRawIntBits(Thread*,
object,
uintptr_t* arguments)
{
return static_cast<int32_t>(*arguments);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Float_intBitsToFloat(Thread*, object, uintptr_t* arguments)
{
return static_cast<int32_t>(*arguments);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Double_doubleToRawLongBits(Thread*,
object,
uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
int64_t v;
memcpy(&v, arguments, 8);
return v;
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Double_longBitsToDouble(Thread*,
object,
uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
int64_t v;
memcpy(&v, arguments, 8);
return v;
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_String_intern(Thread* t, object, uintptr_t* arguments)
{
object this_ = reinterpret_cast<object>(arguments[0]);
return reinterpret_cast<int64_t>(intern(t, this_));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_java_lang_System_getVMProperties(Thread* t, object, uintptr_t*)
{
object array
2014-05-29 04:17:25 +00:00
= makeObjectArray(t, type(t, GcString::Type), t->m->propertyCount);
PROTECT(t, array);
for (unsigned i = 0; i < t->m->propertyCount; ++i) {
2014-06-28 23:24:24 +00:00
GcString* s = makeString(t, "%s", t->m->properties[i]);
reinterpret_cast<GcArray*>(array)->setBodyElement(t, i, s);
}
return reinterpret_cast<int64_t>(array);
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_System_arraycopy(Thread* t, object, uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
arrayCopy(t,
reinterpret_cast<object>(arguments[0]),
arguments[1],
reinterpret_cast<object>(arguments[2]),
arguments[3],
arguments[4]);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_System_identityHashCode(Thread* t,
object,
uintptr_t* arguments)
{
object o = reinterpret_cast<object>(arguments[0]);
if (LIKELY(o)) {
return objectHash(t, o);
} else {
2014-05-29 04:17:25 +00:00
throwNew(t, GcNullPointerException::Type);
}
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_java_lang_ClassLoader_getCaller(Thread* t, object, uintptr_t*)
{
2014-07-11 15:47:57 +00:00
return reinterpret_cast<int64_t>(getJClass(t, getCaller(t, 2)->class_()));
}
extern "C" AVIAN_EXPORT void JNICALL
Avian_java_lang_ClassLoader_load(Thread* t, object, uintptr_t* arguments)
{
GcString* name = cast<GcString>(t, reinterpret_cast<object>(arguments[0]));
Thread::LibraryLoadStack stack(
t,
2014-07-11 15:47:57 +00:00
cast<GcJclass>(t, reinterpret_cast<object>(arguments[1]))
->vmClass()
->loader());
bool mapName = arguments[2];
unsigned length = name->length(t);
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, n, length + 1);
stringChars(t, name, RUNTIME_ARRAY_BODY(n));
loadLibrary(t, "", RUNTIME_ARRAY_BODY(n), mapName, true);
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Runtime_gc(Thread* t, object, uintptr_t*)
{
collect(t, Heap::MajorCollection);
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Runtime_addShutdownHook(Thread* t,
object,
uintptr_t* arguments)
{
object hook = reinterpret_cast<object>(arguments[1]);
PROTECT(t, hook);
ACQUIRE(t, t->m->shutdownLock);
GcPair* p = makePair(t, hook, roots(t)->shutdownHooks());
2014-06-26 02:17:27 +00:00
// sequence point, for gc (don't recombine statements)
roots(t)->setShutdownHooks(t, p);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Throwable_trace(Thread* t, object, uintptr_t* arguments)
{
return reinterpret_cast<int64_t>(getTrace(t, arguments[0]));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Throwable_resolveTrace(Thread* t,
object,
uintptr_t* arguments)
{
object trace = reinterpret_cast<object>(*arguments);
PROTECT(t, trace);
unsigned length = objectArrayLength(t, trace);
2014-05-29 04:17:25 +00:00
GcClass* elementType = type(t, GcStackTraceElement::Type);
object array = makeObjectArray(t, elementType, length);
PROTECT(t, array);
for (unsigned i = 0; i < length; ++i) {
2014-07-11 15:47:57 +00:00
GcStackTraceElement* ste = makeStackTraceElement(
t, cast<GcTraceElement>(t, objectArrayBody(t, trace, i)));
reinterpret_cast<GcArray*>(array)->setBodyElement(t, i, ste);
}
return reinterpret_cast<int64_t>(array);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Thread_currentThread(Thread* t, object, uintptr_t*)
{
return reinterpret_cast<int64_t>(t->javaThread);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Thread_doStart(Thread* t, object, uintptr_t* arguments)
{
2014-07-11 15:47:57 +00:00
return reinterpret_cast<int64_t>(
startThread(t, cast<GcThread>(t, reinterpret_cast<object>(*arguments))));
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Thread_interrupt(Thread* t, object, uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
int64_t peer;
memcpy(&peer, arguments, 8);
threadInterrupt(t, reinterpret_cast<Thread*>(peer)->javaThread);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Thread_interrupted(Thread* t, object, uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
int64_t peer;
memcpy(&peer, arguments, 8);
2014-07-11 15:50:18 +00:00
return threadIsInterrupted(
t, reinterpret_cast<Thread*>(peer)->javaThread, true);
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Thread_getStackTrace(Thread* t,
object,
uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
int64_t peer;
memcpy(&peer, arguments, 8);
if (reinterpret_cast<Thread*>(peer) == t) {
return reinterpret_cast<int64_t>(makeTrace(t));
} else {
2014-07-11 15:50:18 +00:00
return reinterpret_cast<int64_t>(
t->m->processor->getStackTrace(t, reinterpret_cast<Thread*>(peer)));
}
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Thread_activeCount(Thread* t, object, uintptr_t*)
{
return t->m->liveCount;
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Thread_enumerate(Thread* t, object, uintptr_t* arguments)
{
GcArray* array = cast<GcArray>(t, reinterpret_cast<object>(*arguments));
ACQUIRE_RAW(t, t->m->stateLock);
2014-07-11 15:47:57 +00:00
unsigned count = min(t->m->liveCount,
objectArrayLength(t, reinterpret_cast<object>(array)));
unsigned index = 0;
2014-06-26 02:17:27 +00:00
local::enumerateThreads(t, t->m->rootThread, array, &index, count);
return count;
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_java_lang_Thread_holdsLock(Thread* t, object, uintptr_t* arguments)
{
GcMonitor* m
= objectMonitor(t, reinterpret_cast<object>(arguments[0]), false);
return m and m->owner() == t;
}
extern "C" AVIAN_EXPORT void JNICALL
2014-07-11 15:50:18 +00:00
Avian_java_lang_Thread_yield(Thread* t, object, uintptr_t*)
2011-02-02 01:14:32 +00:00
{
t->m->system->yield();
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_avian_Atomic_getOffset(Thread* t, object, uintptr_t* arguments)
{
2014-07-11 15:47:57 +00:00
return cast<GcJfield>(t, reinterpret_cast<object>(arguments[0]))
->vmField()
->offset();
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_sun_misc_Unsafe_objectFieldOffset(Thread* t,
object,
uintptr_t* arguments)
{
2014-07-11 15:47:57 +00:00
return cast<GcJfield>(t, reinterpret_cast<object>(arguments[1]))
->vmField()
->offset();
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_avian_Atomic_compareAndSwapObject(Thread* t,
object,
uintptr_t* arguments)
{
object target = reinterpret_cast<object>(arguments[0]);
2014-07-11 15:50:18 +00:00
int64_t offset;
memcpy(&offset, arguments + 1, 8);
uintptr_t expect = arguments[3];
uintptr_t update = arguments[4];
2014-07-11 15:50:18 +00:00
bool success = atomicCompareAndSwap(
&fieldAtOffset<uintptr_t>(target, offset), expect, update);
if (success) {
mark(t, target, offset);
}
return success;
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_avian_Classes_isAssignableFrom(Thread* t,
object,
uintptr_t* arguments)
{
2014-05-29 04:17:25 +00:00
GcClass* this_ = cast<GcClass>(t, reinterpret_cast<object>(arguments[0]));
GcClass* that = cast<GcClass>(t, reinterpret_cast<object>(arguments[1]));
if (LIKELY(that)) {
return vm::isAssignableFrom(t, this_, that);
} else {
2014-05-29 04:17:25 +00:00
throwNew(t, GcNullPointerException::Type);
}
}
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_avian_Classes_getVMClass(Thread* t, object, uintptr_t* arguments)
{
2014-07-11 15:50:18 +00:00
return reinterpret_cast<int64_t>(
objectClass(t, reinterpret_cast<object>(arguments[0])));
}
2013-02-22 18:55:01 +00:00
extern "C" AVIAN_EXPORT int64_t JNICALL
2014-07-11 15:50:18 +00:00
Avian_avian_Classes_makeMethod(Thread* t, object, uintptr_t* arguments)
2013-02-22 18:55:01 +00:00
{
GcMethod* method = cast<GcMethod>(
t,
cast<GcArray>(t,
cast<GcJclass>(t, reinterpret_cast<object>(arguments[0]))
->vmClass()
->methodTable())->body()[arguments[1]]);
2013-02-22 18:55:01 +00:00
PROTECT(t, method);
2014-07-11 15:47:57 +00:00
GcClass* c
= resolveClass(t, roots(t)->bootLoader(), "java/lang/reflect/Method");
2013-02-22 18:55:01 +00:00
PROTECT(t, c);
object instance = makeNew(t, c);
PROTECT(t, instance);
2014-05-29 04:17:25 +00:00
GcMethod* constructor = resolveMethod(t, c, "<init>", "(Lavian/VMMethod;)V");
2013-02-22 18:55:01 +00:00
t->m->processor->invoke(t, constructor, instance, method);
if (method->name()->body()[0] == '<') {
object oldInstance = instance;
2013-02-22 18:55:01 +00:00
2014-07-11 15:47:57 +00:00
c = resolveClass(
t, roots(t)->bootLoader(), "java/lang/reflect/Constructor");
2013-02-22 18:55:01 +00:00
object instance = makeNew(t, c);
2014-07-11 15:47:57 +00:00
GcMethod* constructor
= resolveMethod(t, c, "<init>", "(Ljava/lang/Method;)V");
2013-02-22 18:55:01 +00:00
t->m->processor->invoke(t, constructor, instance, oldInstance);
2013-02-22 18:55:01 +00:00
}
return reinterpret_cast<uintptr_t>(instance);
}