mirror of
https://github.com/corda/corda.git
synced 2025-02-10 04:41:35 +00:00
4095 lines
88 KiB
C++
4095 lines
88 KiB
C++
/* 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 MACHINE_H
|
|
#define MACHINE_H
|
|
|
|
#include "avian/common.h"
|
|
#include "java-common.h"
|
|
#include <avian/system/system.h>
|
|
#include <avian/system/signal.h>
|
|
#include <avian/heap/heap.h>
|
|
#include <avian/util/hash.h>
|
|
#include "avian/finder.h"
|
|
#include "avian/processor.h"
|
|
#include "avian/constants.h"
|
|
#include "avian/arch.h"
|
|
|
|
using namespace avian::util;
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
# define JNICALL __stdcall
|
|
#else
|
|
# define JNICALL
|
|
#endif
|
|
|
|
#define PROTECT(thread, name) \
|
|
Thread::SingleProtector MAKE_NAME(protector_) (thread, &name);
|
|
|
|
#define ACQUIRE(t, x) MonitorResource MAKE_NAME(monitorResource_) (t, x)
|
|
|
|
#define ACQUIRE_OBJECT(t, x) \
|
|
ObjectMonitorResource MAKE_NAME(monitorResource_) (t, x)
|
|
|
|
#define ACQUIRE_FIELD_FOR_READ(t, field) \
|
|
FieldReadResource MAKE_NAME(monitorResource_) (t, field)
|
|
|
|
#define ACQUIRE_FIELD_FOR_WRITE(t, field) \
|
|
FieldWriteResource MAKE_NAME(monitorResource_) (t, field)
|
|
|
|
#define ACQUIRE_RAW(t, x) RawMonitorResource MAKE_NAME(monitorResource_) (t, x)
|
|
|
|
#define ENTER(t, state) StateResource MAKE_NAME(stateResource_) (t, state)
|
|
|
|
#define THREAD_RESOURCE0(t, releaseBody) \
|
|
class MAKE_NAME(Resource_): public Thread::AutoResource { \
|
|
public: \
|
|
MAKE_NAME(Resource_)(Thread* t): AutoResource(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::AutoResource { \
|
|
public: \
|
|
MAKE_NAME(Resource_)(Thread* t, object name): \
|
|
AutoResource(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, reinterpret_cast<object>(name));
|
|
|
|
#define THREAD_RESOURCE(t, type, name, releaseBody) \
|
|
class MAKE_NAME(Resource_): public Thread::AutoResource { \
|
|
public: \
|
|
MAKE_NAME(Resource_)(Thread* t, type name): \
|
|
AutoResource(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::AutoResource { \
|
|
public: \
|
|
MAKE_NAME(Resource_)(Thread* t, type1 name1, type2 name2): \
|
|
AutoResource(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;
|
|
const bool DebugRun = false;
|
|
const bool DebugStack = false;
|
|
const bool DebugMonitors = false;
|
|
const bool DebugReferences = false;
|
|
|
|
const bool AbortOnOutOfMemoryError = false;
|
|
|
|
const uintptr_t HashTakenMark = 1;
|
|
const uintptr_t ExtendedMark = 2;
|
|
const uintptr_t FixedMark = 3;
|
|
|
|
const unsigned ThreadHeapSizeInBytes = 64 * 1024;
|
|
const unsigned ThreadHeapSizeInWords = ThreadHeapSizeInBytes / BytesPerWord;
|
|
|
|
const unsigned ThreadBackupHeapSizeInBytes = 2 * 1024;
|
|
const unsigned ThreadBackupHeapSizeInWords
|
|
= ThreadBackupHeapSizeInBytes / BytesPerWord;
|
|
|
|
const unsigned ThreadHeapPoolSize = 64;
|
|
|
|
const unsigned FixedFootprintThresholdInBytes
|
|
= ThreadHeapPoolSize * ThreadHeapSizeInBytes;
|
|
|
|
// number of zombie threads which may accumulate before we force a GC
|
|
// to clean them up:
|
|
const unsigned ZombieCollectionThreshold = 16;
|
|
|
|
enum FieldCode {
|
|
VoidField,
|
|
ByteField,
|
|
CharField,
|
|
DoubleField,
|
|
FloatField,
|
|
IntField,
|
|
LongField,
|
|
ShortField,
|
|
BooleanField,
|
|
ObjectField
|
|
};
|
|
|
|
enum StackTag {
|
|
IntTag, // must be zero
|
|
ObjectTag
|
|
};
|
|
|
|
const int NativeLine = -2;
|
|
const int UnknownLine = -1;
|
|
|
|
// class vmFlags:
|
|
const unsigned ReferenceFlag = 1 << 0;
|
|
const unsigned WeakReferenceFlag = 1 << 1;
|
|
const unsigned NeedInitFlag = 1 << 2;
|
|
const unsigned InitFlag = 1 << 3;
|
|
const unsigned InitErrorFlag = 1 << 4;
|
|
const unsigned PrimitiveFlag = 1 << 5;
|
|
const unsigned BootstrapFlag = 1 << 6;
|
|
const unsigned HasFinalizerFlag = 1 << 7;
|
|
const unsigned LinkFlag = 1 << 8;
|
|
const unsigned HasFinalMemberFlag = 1 << 9;
|
|
const unsigned SingletonFlag = 1 << 10;
|
|
const unsigned ContinuationFlag = 1 << 11;
|
|
|
|
// method vmFlags:
|
|
const unsigned ClassInitFlag = 1 << 0;
|
|
const unsigned ConstructorFlag = 1 << 1;
|
|
|
|
#ifndef JNI_VERSION_1_6
|
|
#define JNI_VERSION_1_6 0x00010006
|
|
#endif
|
|
|
|
typedef Machine JavaVM;
|
|
typedef Thread JNIEnv;
|
|
|
|
struct JNINativeMethod {
|
|
char* name;
|
|
char* signature;
|
|
void* function;
|
|
};
|
|
|
|
struct JavaVMVTable {
|
|
void* reserved0;
|
|
void* reserved1;
|
|
void* reserved2;
|
|
|
|
jint
|
|
(JNICALL *DestroyJavaVM)
|
|
(JavaVM*);
|
|
|
|
jint
|
|
(JNICALL *AttachCurrentThread)
|
|
(JavaVM*, JNIEnv**, void*);
|
|
|
|
jint
|
|
(JNICALL *DetachCurrentThread)
|
|
(JavaVM*);
|
|
|
|
jint
|
|
(JNICALL *GetEnv)
|
|
(JavaVM*, JNIEnv**, jint);
|
|
|
|
jint
|
|
(JNICALL *AttachCurrentThreadAsDaemon)
|
|
(JavaVM*, JNIEnv**, void*);
|
|
};
|
|
|
|
struct JNIEnvVTable {
|
|
void* reserved0;
|
|
void* reserved1;
|
|
void* reserved2;
|
|
void* reserved3;
|
|
|
|
jint
|
|
(JNICALL *GetVersion)
|
|
(JNIEnv*);
|
|
|
|
jclass
|
|
(JNICALL *DefineClass)
|
|
(JNIEnv*, const char*, jobject, const jbyte*, jsize);
|
|
|
|
jclass
|
|
(JNICALL *FindClass)
|
|
(JNIEnv*, const char*);
|
|
|
|
jmethodID
|
|
(JNICALL *FromReflectedMethod)
|
|
(JNIEnv*, jobject);
|
|
|
|
jfieldID
|
|
(JNICALL *FromReflectedField)
|
|
(JNIEnv*, jobject);
|
|
|
|
jobject
|
|
(JNICALL *ToReflectedMethod)
|
|
(JNIEnv*, jclass, jmethodID, jboolean);
|
|
|
|
jclass
|
|
(JNICALL *GetSuperclass)
|
|
(JNIEnv*, jclass);
|
|
|
|
jboolean
|
|
(JNICALL *IsAssignableFrom)
|
|
(JNIEnv*, jclass, jclass);
|
|
|
|
jobject
|
|
(JNICALL *ToReflectedField)
|
|
(JNIEnv*, jclass, jfieldID, jboolean);
|
|
|
|
jint
|
|
(JNICALL *Throw)
|
|
(JNIEnv*, jthrowable);
|
|
|
|
jint
|
|
(JNICALL *ThrowNew)
|
|
(JNIEnv*, jclass, const char*);
|
|
|
|
jthrowable
|
|
(JNICALL *ExceptionOccurred)
|
|
(JNIEnv*);
|
|
|
|
void
|
|
(JNICALL *ExceptionDescribe)
|
|
(JNIEnv*);
|
|
|
|
void
|
|
(JNICALL *ExceptionClear)
|
|
(JNIEnv*);
|
|
|
|
void
|
|
(JNICALL *FatalError)
|
|
(JNIEnv*, const char*);
|
|
|
|
jint
|
|
(JNICALL *PushLocalFrame)
|
|
(JNIEnv*, jint);
|
|
|
|
jobject
|
|
(JNICALL *PopLocalFrame)
|
|
(JNIEnv*, jobject);
|
|
|
|
jobject
|
|
(JNICALL *NewGlobalRef)
|
|
(JNIEnv*, jobject);
|
|
|
|
void
|
|
(JNICALL *DeleteGlobalRef)
|
|
(JNIEnv*, jobject);
|
|
|
|
void
|
|
(JNICALL *DeleteLocalRef)
|
|
(JNIEnv*, jobject);
|
|
|
|
jboolean
|
|
(JNICALL *IsSameObject)
|
|
(JNIEnv*, jobject, jobject);
|
|
|
|
jobject
|
|
(JNICALL *NewLocalRef)
|
|
(JNIEnv*, jobject);
|
|
|
|
jint
|
|
(JNICALL *EnsureLocalCapacity)
|
|
(JNIEnv*, jint);
|
|
|
|
jobject
|
|
(JNICALL *AllocObject)
|
|
(JNIEnv*, jclass);
|
|
|
|
jobject
|
|
(JNICALL *NewObject)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jobject
|
|
(JNICALL *NewObjectV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jobject
|
|
(JNICALL *NewObjectA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jclass
|
|
(JNICALL *GetObjectClass)
|
|
(JNIEnv*, jobject);
|
|
|
|
jboolean
|
|
(JNICALL *IsInstanceOf)
|
|
(JNIEnv*, jobject, jclass);
|
|
|
|
jmethodID
|
|
(JNICALL *GetMethodID)
|
|
(JNIEnv*, jclass, const char*, const char*);
|
|
|
|
jobject
|
|
(JNICALL *CallObjectMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jobject
|
|
(JNICALL *CallObjectMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jobject
|
|
(JNICALL *CallObjectMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jboolean
|
|
(JNICALL *CallBooleanMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jboolean
|
|
(JNICALL *CallBooleanMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jboolean
|
|
(JNICALL *CallBooleanMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jbyte
|
|
(JNICALL *CallByteMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jbyte
|
|
(JNICALL *CallByteMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jbyte
|
|
(JNICALL *CallByteMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jchar
|
|
(JNICALL *CallCharMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jchar
|
|
(JNICALL *CallCharMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jchar
|
|
(JNICALL *CallCharMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jshort
|
|
(JNICALL *CallShortMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jshort
|
|
(JNICALL *CallShortMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jshort
|
|
(JNICALL *CallShortMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jint
|
|
(JNICALL *CallIntMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jint
|
|
(JNICALL *CallIntMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jint
|
|
(JNICALL *CallIntMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jlong
|
|
(JNICALL *CallLongMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jlong
|
|
(JNICALL *CallLongMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jlong
|
|
(JNICALL *CallLongMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jfloat
|
|
(JNICALL *CallFloatMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jfloat
|
|
(JNICALL *CallFloatMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jfloat
|
|
(JNICALL *CallFloatMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jdouble
|
|
(JNICALL *CallDoubleMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
jdouble
|
|
(JNICALL *CallDoubleMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
jdouble
|
|
(JNICALL *CallDoubleMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
void
|
|
(JNICALL *CallVoidMethod)
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
void
|
|
(JNICALL *CallVoidMethodV)
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
void
|
|
(JNICALL *CallVoidMethodA)
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
jobject
|
|
(JNICALL *CallNonvirtualObjectMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jobject
|
|
(JNICALL *CallNonvirtualObjectMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
jobject
|
|
(JNICALL *CallNonvirtualObjectMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
jboolean
|
|
(JNICALL *CallNonvirtualBooleanMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jboolean
|
|
(JNICALL *CallNonvirtualBooleanMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
jboolean
|
|
(JNICALL *CallNonvirtualBooleanMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
jbyte
|
|
(JNICALL *CallNonvirtualByteMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jbyte
|
|
(JNICALL *CallNonvirtualByteMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
jbyte
|
|
(JNICALL *CallNonvirtualByteMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
jchar
|
|
(JNICALL *CallNonvirtualCharMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jchar
|
|
(JNICALL *CallNonvirtualCharMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
jchar
|
|
(JNICALL *CallNonvirtualCharMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
jshort
|
|
(JNICALL *CallNonvirtualShortMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jshort
|
|
(JNICALL *CallNonvirtualShortMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
va_list);
|
|
|
|
jshort
|
|
(JNICALL *CallNonvirtualShortMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
const jvalue*);
|
|
|
|
jint
|
|
(JNICALL *CallNonvirtualIntMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jint
|
|
(JNICALL *CallNonvirtualIntMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
va_list);
|
|
|
|
jint
|
|
(JNICALL *CallNonvirtualIntMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
const jvalue*);
|
|
|
|
jlong
|
|
(JNICALL *CallNonvirtualLongMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jlong
|
|
(JNICALL *CallNonvirtualLongMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
va_list);
|
|
jlong
|
|
(JNICALL *CallNonvirtualLongMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
jfloat
|
|
(JNICALL *CallNonvirtualFloatMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jfloat
|
|
(JNICALL *CallNonvirtualFloatMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
jfloat
|
|
(JNICALL *CallNonvirtualFloatMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
jdouble
|
|
(JNICALL *CallNonvirtualDoubleMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
jdouble
|
|
(JNICALL *CallNonvirtualDoubleMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
jdouble
|
|
(JNICALL *CallNonvirtualDoubleMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
void
|
|
(JNICALL *CallNonvirtualVoidMethod)
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
void
|
|
(JNICALL *CallNonvirtualVoidMethodV)
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
void
|
|
(JNICALL *CallNonvirtualVoidMethodA)
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
jfieldID
|
|
(JNICALL *GetFieldID)
|
|
(JNIEnv*, jclass, const char*, const char*);
|
|
|
|
jobject
|
|
(JNICALL *GetObjectField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
jboolean
|
|
(JNICALL *GetBooleanField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
jbyte
|
|
(JNICALL *GetByteField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
jchar
|
|
(JNICALL *GetCharField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
jshort
|
|
(JNICALL *GetShortField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
jint
|
|
(JNICALL *GetIntField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
jlong
|
|
(JNICALL *GetLongField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
jfloat
|
|
(JNICALL *GetFloatField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
jdouble
|
|
(JNICALL *GetDoubleField)
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
void
|
|
(JNICALL *SetObjectField)
|
|
(JNIEnv*, jobject, jfieldID, jobject);
|
|
|
|
void
|
|
(JNICALL *SetBooleanField)
|
|
(JNIEnv*, jobject, jfieldID, jboolean);
|
|
|
|
void
|
|
(JNICALL *SetByteField)
|
|
(JNIEnv*, jobject, jfieldID, jbyte);
|
|
|
|
void
|
|
(JNICALL *SetCharField)
|
|
(JNIEnv*, jobject, jfieldID, jchar);
|
|
|
|
void
|
|
(JNICALL *SetShortField)
|
|
(JNIEnv*, jobject, jfieldID, jshort);
|
|
|
|
void
|
|
(JNICALL *SetIntField)
|
|
(JNIEnv*, jobject, jfieldID, jint);
|
|
|
|
void
|
|
(JNICALL *SetLongField)
|
|
(JNIEnv*, jobject, jfieldID, jlong);
|
|
|
|
void
|
|
(JNICALL *SetFloatField)
|
|
(JNIEnv*, jobject, jfieldID, jfloat);
|
|
|
|
void
|
|
(JNICALL *SetDoubleField)
|
|
(JNIEnv*, jobject, jfieldID, jdouble);
|
|
|
|
jmethodID
|
|
(JNICALL *GetStaticMethodID)
|
|
(JNIEnv*, jclass, const char*, const char*);
|
|
|
|
jobject
|
|
(JNICALL *CallStaticObjectMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jobject
|
|
(JNICALL *CallStaticObjectMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jobject
|
|
(JNICALL *CallStaticObjectMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jboolean
|
|
(JNICALL *CallStaticBooleanMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jboolean
|
|
(JNICALL *CallStaticBooleanMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jboolean
|
|
(JNICALL *CallStaticBooleanMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jbyte
|
|
(JNICALL *CallStaticByteMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jbyte
|
|
(JNICALL *CallStaticByteMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jbyte
|
|
(JNICALL *CallStaticByteMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jchar
|
|
(JNICALL *CallStaticCharMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jchar
|
|
(JNICALL *CallStaticCharMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jchar
|
|
(JNICALL *CallStaticCharMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jshort
|
|
(JNICALL *CallStaticShortMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jshort
|
|
(JNICALL *CallStaticShortMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jshort
|
|
(JNICALL *CallStaticShortMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jint
|
|
(JNICALL *CallStaticIntMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jint
|
|
(JNICALL *CallStaticIntMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jint
|
|
(JNICALL *CallStaticIntMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jlong
|
|
(JNICALL *CallStaticLongMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jlong
|
|
(JNICALL *CallStaticLongMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jlong
|
|
(JNICALL *CallStaticLongMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jfloat
|
|
(JNICALL *CallStaticFloatMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jfloat
|
|
(JNICALL *CallStaticFloatMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jfloat
|
|
(JNICALL *CallStaticFloatMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jdouble
|
|
(JNICALL *CallStaticDoubleMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
jdouble
|
|
(JNICALL *CallStaticDoubleMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
jdouble
|
|
(JNICALL *CallStaticDoubleMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
void
|
|
(JNICALL *CallStaticVoidMethod)
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
void
|
|
(JNICALL *CallStaticVoidMethodV)
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
void
|
|
(JNICALL *CallStaticVoidMethodA)
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
jfieldID
|
|
(JNICALL *GetStaticFieldID)
|
|
(JNIEnv*, jclass, const char*, const char*);
|
|
|
|
jobject
|
|
(JNICALL *GetStaticObjectField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
jboolean
|
|
(JNICALL *GetStaticBooleanField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
jbyte
|
|
(JNICALL *GetStaticByteField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
jchar
|
|
(JNICALL *GetStaticCharField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
jshort
|
|
(JNICALL *GetStaticShortField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
jint
|
|
(JNICALL *GetStaticIntField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
jlong
|
|
(JNICALL *GetStaticLongField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
jfloat
|
|
(JNICALL *GetStaticFloatField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
jdouble
|
|
(JNICALL *GetStaticDoubleField)
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
void
|
|
(JNICALL *SetStaticObjectField)
|
|
(JNIEnv*, jclass, jfieldID, jobject);
|
|
|
|
void
|
|
(JNICALL *SetStaticBooleanField)
|
|
(JNIEnv*, jclass, jfieldID, jboolean);
|
|
|
|
void
|
|
(JNICALL *SetStaticByteField)
|
|
(JNIEnv*, jclass, jfieldID, jbyte);
|
|
|
|
void
|
|
(JNICALL *SetStaticCharField)
|
|
(JNIEnv*, jclass, jfieldID, jchar);
|
|
|
|
void
|
|
(JNICALL *SetStaticShortField)
|
|
(JNIEnv*, jclass, jfieldID, jshort);
|
|
|
|
void
|
|
(JNICALL *SetStaticIntField)
|
|
(JNIEnv*, jclass, jfieldID, jint);
|
|
|
|
void
|
|
(JNICALL *SetStaticLongField)
|
|
(JNIEnv*, jclass, jfieldID, jlong);
|
|
|
|
void
|
|
(JNICALL *SetStaticFloatField)
|
|
(JNIEnv*, jclass, jfieldID, jfloat);
|
|
|
|
void
|
|
(JNICALL *SetStaticDoubleField)
|
|
(JNIEnv*, jclass, jfieldID, jdouble);
|
|
|
|
jstring
|
|
(JNICALL *NewString)
|
|
(JNIEnv*, const jchar*, jsize);
|
|
|
|
jsize
|
|
(JNICALL *GetStringLength)
|
|
(JNIEnv*, jstring);
|
|
|
|
const jchar*
|
|
(JNICALL *GetStringChars)
|
|
(JNIEnv*, jstring, jboolean*);
|
|
|
|
void
|
|
(JNICALL *ReleaseStringChars)
|
|
(JNIEnv*, jstring, const jchar*);
|
|
|
|
jstring
|
|
(JNICALL *NewStringUTF)
|
|
(JNIEnv*, const char*);
|
|
|
|
jsize
|
|
(JNICALL *GetStringUTFLength)
|
|
(JNIEnv*, jstring);
|
|
|
|
const char*
|
|
(JNICALL *GetStringUTFChars)
|
|
(JNIEnv*, jstring, jboolean*);
|
|
|
|
void
|
|
(JNICALL *ReleaseStringUTFChars)
|
|
(JNIEnv*, jstring, const char*);
|
|
|
|
jsize
|
|
(JNICALL *GetArrayLength)
|
|
(JNIEnv*, jarray);
|
|
|
|
jobjectArray
|
|
(JNICALL *NewObjectArray)
|
|
(JNIEnv*, jsize, jclass, jobject);
|
|
|
|
jobject
|
|
(JNICALL *GetObjectArrayElement)
|
|
(JNIEnv*, jobjectArray, jsize);
|
|
|
|
void
|
|
(JNICALL *SetObjectArrayElement)
|
|
(JNIEnv*, jobjectArray, jsize, jobject);
|
|
|
|
jbooleanArray
|
|
(JNICALL *NewBooleanArray)
|
|
(JNIEnv*, jsize);
|
|
|
|
jbyteArray
|
|
(JNICALL *NewByteArray)
|
|
(JNIEnv*, jsize);
|
|
|
|
jcharArray
|
|
(JNICALL *NewCharArray)
|
|
(JNIEnv*, jsize);
|
|
|
|
jshortArray
|
|
(JNICALL *NewShortArray)
|
|
(JNIEnv*, jsize);
|
|
|
|
jintArray
|
|
(JNICALL *NewIntArray)
|
|
(JNIEnv*, jsize);
|
|
|
|
jlongArray
|
|
(JNICALL *NewLongArray)
|
|
(JNIEnv*, jsize);
|
|
|
|
jfloatArray
|
|
(JNICALL *NewFloatArray)
|
|
(JNIEnv*, jsize);
|
|
|
|
jdoubleArray
|
|
(JNICALL *NewDoubleArray)
|
|
(JNIEnv*, jsize);
|
|
|
|
jboolean*
|
|
(JNICALL *GetBooleanArrayElements)
|
|
(JNIEnv*, jbooleanArray, jboolean*);
|
|
|
|
jbyte*
|
|
(JNICALL *GetByteArrayElements)
|
|
(JNIEnv*, jbyteArray, jboolean*);
|
|
|
|
jchar*
|
|
(JNICALL *GetCharArrayElements)
|
|
(JNIEnv*, jcharArray, jboolean*);
|
|
|
|
jshort*
|
|
(JNICALL *GetShortArrayElements)
|
|
(JNIEnv*, jshortArray, jboolean*);
|
|
|
|
jint*
|
|
(JNICALL *GetIntArrayElements)
|
|
(JNIEnv*, jintArray, jboolean*);
|
|
|
|
jlong*
|
|
(JNICALL *GetLongArrayElements)
|
|
(JNIEnv*, jlongArray, jboolean*);
|
|
|
|
jfloat*
|
|
(JNICALL *GetFloatArrayElements)
|
|
(JNIEnv*, jfloatArray, jboolean*);
|
|
|
|
jdouble*
|
|
(JNICALL *GetDoubleArrayElements)
|
|
(JNIEnv*, jdoubleArray, jboolean*);
|
|
|
|
void
|
|
(JNICALL *ReleaseBooleanArrayElements)
|
|
(JNIEnv*, jbooleanArray, jboolean*, jint);
|
|
|
|
void
|
|
(JNICALL *ReleaseByteArrayElements)
|
|
(JNIEnv*, jbyteArray, jbyte*, jint);
|
|
|
|
void
|
|
(JNICALL *ReleaseCharArrayElements)
|
|
(JNIEnv*, jcharArray, jchar*, jint);
|
|
|
|
void
|
|
(JNICALL *ReleaseShortArrayElements)
|
|
(JNIEnv*, jshortArray, jshort*, jint);
|
|
|
|
void
|
|
(JNICALL *ReleaseIntArrayElements)
|
|
(JNIEnv*, jintArray, jint*, jint);
|
|
|
|
void
|
|
(JNICALL *ReleaseLongArrayElements)
|
|
(JNIEnv*, jlongArray, jlong*, jint);
|
|
|
|
void
|
|
(JNICALL *ReleaseFloatArrayElements)
|
|
(JNIEnv*, jfloatArray, jfloat*, jint);
|
|
|
|
void
|
|
(JNICALL *ReleaseDoubleArrayElements)
|
|
(JNIEnv*, jdoubleArray, jdouble*, jint);
|
|
|
|
void
|
|
(JNICALL *GetBooleanArrayRegion)
|
|
(JNIEnv*, jbooleanArray, jsize, jsize, jboolean*);
|
|
|
|
void
|
|
(JNICALL *GetByteArrayRegion)
|
|
(JNIEnv*, jbyteArray, jsize, jsize, jbyte*);
|
|
|
|
void
|
|
(JNICALL *GetCharArrayRegion)
|
|
(JNIEnv*, jcharArray, jsize, jsize, jchar*);
|
|
|
|
void
|
|
(JNICALL *GetShortArrayRegion)
|
|
(JNIEnv*, jshortArray, jsize, jsize, jshort*);
|
|
|
|
void
|
|
(JNICALL *GetIntArrayRegion)
|
|
(JNIEnv*, jintArray, jsize, jsize, jint*);
|
|
|
|
void
|
|
(JNICALL *GetLongArrayRegion)
|
|
(JNIEnv*, jlongArray, jsize, jsize, jlong*);
|
|
|
|
void
|
|
(JNICALL *GetFloatArrayRegion)
|
|
(JNIEnv*, jfloatArray, jsize, jsize, jfloat*);
|
|
|
|
void
|
|
(JNICALL *GetDoubleArrayRegion)
|
|
(JNIEnv*, jdoubleArray, jsize, jsize, jdouble*);
|
|
|
|
void
|
|
(JNICALL *SetBooleanArrayRegion)
|
|
(JNIEnv*, jbooleanArray, jsize, jsize, const jboolean*);
|
|
|
|
void
|
|
(JNICALL *SetByteArrayRegion)
|
|
(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*);
|
|
|
|
void
|
|
(JNICALL *SetCharArrayRegion)
|
|
(JNIEnv*, jcharArray, jsize, jsize, const jchar*);
|
|
|
|
void
|
|
(JNICALL *SetShortArrayRegion)
|
|
(JNIEnv*, jshortArray, jsize, jsize, const jshort*);
|
|
|
|
void
|
|
(JNICALL *SetIntArrayRegion)
|
|
(JNIEnv*, jintArray, jsize, jsize, const jint*);
|
|
|
|
void
|
|
(JNICALL *SetLongArrayRegion)
|
|
(JNIEnv*, jlongArray, jsize, jsize, const jlong*);
|
|
|
|
void
|
|
(JNICALL *SetFloatArrayRegion)
|
|
(JNIEnv*, jfloatArray, jsize, jsize, const jfloat*);
|
|
|
|
void
|
|
(JNICALL *SetDoubleArrayRegion)
|
|
(JNIEnv*, jdoubleArray, jsize, jsize, const jdouble*);
|
|
|
|
jint
|
|
(JNICALL *RegisterNatives)
|
|
(JNIEnv*, jclass, const JNINativeMethod*, jint);
|
|
|
|
jint
|
|
(JNICALL *UnregisterNatives)
|
|
(JNIEnv*, jclass);
|
|
|
|
jint
|
|
(JNICALL *MonitorEnter)
|
|
(JNIEnv*, jobject);
|
|
|
|
jint
|
|
(JNICALL *MonitorExit)
|
|
(JNIEnv*, jobject);
|
|
|
|
jint
|
|
(JNICALL *GetJavaVM)
|
|
(JNIEnv*, JavaVM**);
|
|
|
|
void
|
|
(JNICALL *GetStringRegion)
|
|
(JNIEnv*, jstring, jsize, jsize, jchar*);
|
|
|
|
void
|
|
(JNICALL *GetStringUTFRegion)
|
|
(JNIEnv*, jstring, jsize, jsize, char*);
|
|
|
|
void*
|
|
(JNICALL *GetPrimitiveArrayCritical)
|
|
(JNIEnv*, jarray, jboolean*);
|
|
|
|
void
|
|
(JNICALL *ReleasePrimitiveArrayCritical)
|
|
(JNIEnv*, jarray, void*, jint);
|
|
|
|
const jchar*
|
|
(JNICALL *GetStringCritical)
|
|
(JNIEnv*, jstring, jboolean*);
|
|
|
|
void
|
|
(JNICALL *ReleaseStringCritical)
|
|
(JNIEnv*, jstring, const jchar*);
|
|
|
|
jweak
|
|
(JNICALL *NewWeakGlobalRef)
|
|
(JNIEnv*, jobject);
|
|
|
|
void
|
|
(JNICALL *DeleteWeakGlobalRef)
|
|
(JNIEnv*, jweak);
|
|
|
|
jboolean
|
|
(JNICALL *ExceptionCheck)
|
|
(JNIEnv*);
|
|
|
|
jobject
|
|
(JNICALL *NewDirectByteBuffer)
|
|
(JNIEnv*, void*, jlong);
|
|
|
|
void*
|
|
(JNICALL *GetDirectBufferAddress)
|
|
(JNIEnv* env, jobject);
|
|
|
|
jlong
|
|
(JNICALL *GetDirectBufferCapacity)
|
|
(JNIEnv*, jobject);
|
|
|
|
};
|
|
|
|
inline void
|
|
atomicOr(uint32_t* p, int v)
|
|
{
|
|
for (uint32_t old = *p;
|
|
not atomicCompareAndSwap32(p, old, old | v);
|
|
old = *p)
|
|
{ }
|
|
}
|
|
|
|
inline void
|
|
atomicAnd(uint32_t* p, int v)
|
|
{
|
|
for (uint32_t old = *p;
|
|
not atomicCompareAndSwap32(p, old, old & v);
|
|
old = *p)
|
|
{ }
|
|
}
|
|
|
|
inline int
|
|
strcmp(const int8_t* a, const int8_t* b)
|
|
{
|
|
return ::strcmp(reinterpret_cast<const char*>(a),
|
|
reinterpret_cast<const char*>(b));
|
|
}
|
|
|
|
void
|
|
noop();
|
|
|
|
class Reference {
|
|
public:
|
|
Reference(object target, Reference** handle, bool weak):
|
|
target(target),
|
|
next(*handle),
|
|
handle(handle),
|
|
count(0),
|
|
weak(weak)
|
|
{
|
|
if (next) {
|
|
next->handle = &next;
|
|
}
|
|
*handle = this;
|
|
}
|
|
|
|
object target;
|
|
Reference* next;
|
|
Reference** handle;
|
|
unsigned count;
|
|
bool weak;
|
|
};
|
|
|
|
class Classpath;
|
|
|
|
class Gc {
|
|
public:
|
|
enum Type {
|
|
#include "type-enums.cpp"
|
|
};
|
|
};
|
|
|
|
class GcObject {
|
|
public:
|
|
|
|
template <class T>
|
|
T* as(Thread* t);
|
|
|
|
template <class T>
|
|
bool isa(Thread* t);
|
|
|
|
protected:
|
|
template <class T>
|
|
T& field_at(size_t offset)
|
|
{
|
|
return *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(this) + offset);
|
|
}
|
|
};
|
|
|
|
class GcFinalizer;
|
|
class GcClassLoader;
|
|
class GcJreference;
|
|
class GcArray;
|
|
class GcThrowable;
|
|
|
|
class Machine {
|
|
public:
|
|
|
|
enum AllocationType {
|
|
MovableAllocation,
|
|
FixedAllocation,
|
|
ImmortalAllocation
|
|
};
|
|
|
|
enum Root {
|
|
BootLoader,
|
|
AppLoader,
|
|
BootstrapClassMap,
|
|
PackageMap,
|
|
FindLoadedClassMethod,
|
|
LoadClassMethod,
|
|
MonitorMap,
|
|
StringMap,
|
|
ByteArrayMap,
|
|
PoolMap,
|
|
ClassRuntimeDataTable,
|
|
MethodRuntimeDataTable,
|
|
JNIMethodTable,
|
|
JNIFieldTable,
|
|
ShutdownHooks,
|
|
FinalizerThread,
|
|
ObjectsToFinalize,
|
|
ObjectsToClean,
|
|
NullPointerException,
|
|
ArithmeticException,
|
|
ArrayIndexOutOfBoundsException,
|
|
OutOfMemoryError,
|
|
Shutdown,
|
|
VirtualFileFinders,
|
|
VirtualFiles,
|
|
ArrayInterfaceTable,
|
|
ThreadTerminated
|
|
};
|
|
|
|
static const unsigned RootCount = ThreadTerminated + 1;
|
|
|
|
Machine(System* system, Heap* heap, Finder* bootFinder, Finder* appFinder,
|
|
Processor* processor, Classpath* classpath, const char** properties,
|
|
unsigned propertyCount, const char** arguments,
|
|
unsigned argumentCount, unsigned stackSizeInBytes);
|
|
|
|
~Machine() {
|
|
dispose();
|
|
}
|
|
|
|
void dispose();
|
|
|
|
JavaVMVTable* vtable;
|
|
System* system;
|
|
Heap::Client* heapClient;
|
|
Heap* heap;
|
|
Finder* bootFinder;
|
|
Finder* appFinder;
|
|
Processor* processor;
|
|
Classpath* classpath;
|
|
Thread* rootThread;
|
|
Thread* exclusive;
|
|
Thread* finalizeThread;
|
|
Reference* jniReferences;
|
|
char** properties;
|
|
unsigned propertyCount;
|
|
const char** arguments;
|
|
unsigned argumentCount;
|
|
unsigned threadCount;
|
|
unsigned activeCount;
|
|
unsigned liveCount;
|
|
unsigned daemonCount;
|
|
unsigned fixedFootprint;
|
|
unsigned stackSizeInBytes;
|
|
System::Local* localThread;
|
|
System::Monitor* stateLock;
|
|
System::Monitor* heapLock;
|
|
System::Monitor* classLock;
|
|
System::Monitor* referenceLock;
|
|
System::Monitor* shutdownLock;
|
|
System::Library* libraries;
|
|
FILE* errorLog;
|
|
BootImage* bootimage;
|
|
GcArray* types;
|
|
GcArray* roots;
|
|
GcFinalizer* finalizers;
|
|
GcFinalizer* tenuredFinalizers;
|
|
GcFinalizer* finalizeQueue;
|
|
GcJreference* weakReferences;
|
|
GcJreference* tenuredWeakReferences;
|
|
bool unsafe;
|
|
bool collecting;
|
|
bool triedBuiltinOnLoad;
|
|
bool dumpedHeapOnOOM;
|
|
bool alive;
|
|
JavaVMVTable javaVMVTable;
|
|
JNIEnvVTable jniEnvVTable;
|
|
uintptr_t* heapPool[ThreadHeapPoolSize];
|
|
unsigned heapPoolIndex;
|
|
unsigned bootimageSize;
|
|
};
|
|
|
|
void
|
|
printTrace(Thread* t, GcThrowable* exception);
|
|
|
|
uint8_t&
|
|
threadInterrupted(Thread* t, object thread);
|
|
|
|
void
|
|
enterActiveState(Thread* t);
|
|
|
|
#ifdef VM_STRESS
|
|
|
|
inline void stress(Thread* t);
|
|
|
|
#else // not VM_STRESS
|
|
|
|
#define stress(t)
|
|
|
|
#endif // not VM_STRESS
|
|
|
|
uint64_t
|
|
runThread(Thread*, uintptr_t*);
|
|
|
|
uint64_t
|
|
run(Thread* t, uint64_t (*function)(Thread*, uintptr_t*),
|
|
uintptr_t* arguments);
|
|
|
|
void
|
|
checkDaemon(Thread* t);
|
|
|
|
object&
|
|
root(Thread* t, Machine::Root root);
|
|
|
|
extern "C" uint64_t
|
|
vmRun(uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments,
|
|
void* checkpoint);
|
|
|
|
extern "C" void
|
|
vmRun_returnAddress();
|
|
|
|
class GcThread;
|
|
class GcThrowable;
|
|
class GcString;
|
|
|
|
class Thread {
|
|
public:
|
|
enum State {
|
|
NoState,
|
|
ActiveState,
|
|
IdleState,
|
|
ZombieState,
|
|
JoinedState,
|
|
ExclusiveState,
|
|
ExitState
|
|
};
|
|
|
|
static const unsigned UseBackupHeapFlag = 1 << 0;
|
|
static const unsigned WaitingFlag = 1 << 1;
|
|
static const unsigned TracingFlag = 1 << 2;
|
|
static const unsigned DaemonFlag = 1 << 3;
|
|
static const unsigned StressFlag = 1 << 4;
|
|
static const unsigned ActiveFlag = 1 << 5;
|
|
static const unsigned SystemFlag = 1 << 6;
|
|
static const unsigned JoinFlag = 1 << 7;
|
|
|
|
class Protector {
|
|
public:
|
|
Protector(Thread* t): t(t), next(t->protector) {
|
|
t->protector = this;
|
|
}
|
|
|
|
~Protector() {
|
|
t->protector = next;
|
|
}
|
|
|
|
virtual void visit(Heap::Visitor* v) = 0;
|
|
|
|
Thread* t;
|
|
Protector* next;
|
|
};
|
|
|
|
class SingleProtector: public Protector {
|
|
public:
|
|
SingleProtector(Thread* t, void* p): Protector(t), p(p) { }
|
|
|
|
virtual void visit(Heap::Visitor* v) {
|
|
v->visit(p);
|
|
}
|
|
|
|
void* p;
|
|
};
|
|
|
|
class Resource {
|
|
public:
|
|
Resource(Thread* t, Resource* next): t(t), next(next) {
|
|
t->resource = this;
|
|
}
|
|
|
|
virtual void release() = 0;
|
|
|
|
Thread* t;
|
|
Resource* next;
|
|
};
|
|
|
|
class AutoResource: public Resource {
|
|
public:
|
|
AutoResource(Thread* t): Resource(t, t->resource) { }
|
|
|
|
~AutoResource() {
|
|
t->resource = next;
|
|
}
|
|
|
|
virtual void release() = 0;
|
|
|
|
};
|
|
|
|
class ClassInitStack: public AutoResource {
|
|
public:
|
|
ClassInitStack(Thread* t, GcClass* class_):
|
|
AutoResource(t),
|
|
next(t->classInitStack),
|
|
class_(class_),
|
|
protector(t, &(this->class_))
|
|
{
|
|
t->classInitStack = this;
|
|
}
|
|
|
|
~ClassInitStack() {
|
|
t->classInitStack = next;
|
|
}
|
|
|
|
virtual void release() {
|
|
this->ClassInitStack::~ClassInitStack();
|
|
}
|
|
|
|
ClassInitStack* next;
|
|
GcClass* class_;
|
|
SingleProtector protector;
|
|
};
|
|
|
|
class LibraryLoadStack: public AutoResource {
|
|
public:
|
|
LibraryLoadStack(Thread* t, GcClassLoader* classLoader)
|
|
: AutoResource(t),
|
|
next(t->libraryLoadStack),
|
|
classLoader(classLoader),
|
|
protector(t, &(this->classLoader))
|
|
{
|
|
t->libraryLoadStack = this;
|
|
}
|
|
|
|
~LibraryLoadStack() {
|
|
t->libraryLoadStack = next;
|
|
}
|
|
|
|
virtual void release() {
|
|
this->LibraryLoadStack::~LibraryLoadStack();
|
|
}
|
|
|
|
LibraryLoadStack* next;
|
|
GcClassLoader* classLoader;
|
|
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)
|
|
{ }
|
|
|
|
virtual void unwind() {
|
|
void* stack = this->stack;
|
|
this->stack = 0;
|
|
expect(t->m->system, stack);
|
|
vmJump(voidPointer(vmRun_returnAddress), 0, stack, t, 0, 0);
|
|
}
|
|
|
|
void* stack;
|
|
};
|
|
|
|
class Runnable: public System::Runnable {
|
|
public:
|
|
Runnable(Thread* t): t(t) { }
|
|
|
|
virtual void attach(System::Thread* st) {
|
|
t->systemThread = st;
|
|
}
|
|
|
|
virtual void run() {
|
|
enterActiveState(t);
|
|
|
|
vm::run(t, runThread, 0);
|
|
|
|
if (t->exception and reinterpret_cast<object>(t->exception) != root(t, Machine::Shutdown)) {
|
|
printTrace(t, t->exception);
|
|
}
|
|
|
|
t->exit();
|
|
}
|
|
|
|
virtual bool interrupted();
|
|
|
|
virtual void setInterrupted(bool v);
|
|
|
|
Thread* t;
|
|
};
|
|
|
|
Thread(Machine* m, GcThread* javaThread, Thread* parent);
|
|
|
|
void init();
|
|
void exit();
|
|
void dispose();
|
|
|
|
JNIEnvVTable* vtable;
|
|
Machine* m;
|
|
Thread* parent;
|
|
Thread* peer;
|
|
Thread* child;
|
|
Thread* waitNext;
|
|
State state;
|
|
unsigned criticalLevel;
|
|
System::Thread* systemThread;
|
|
System::Monitor* lock;
|
|
GcThread* javaThread;
|
|
GcThrowable* exception;
|
|
unsigned heapIndex;
|
|
unsigned heapOffset;
|
|
Protector* protector;
|
|
ClassInitStack* classInitStack;
|
|
LibraryLoadStack* libraryLoadStack;
|
|
Resource* resource;
|
|
Checkpoint* checkpoint;
|
|
Runnable runnable;
|
|
uintptr_t* defaultHeap;
|
|
uintptr_t* heap;
|
|
uintptr_t backupHeap[ThreadBackupHeapSizeInWords];
|
|
unsigned backupHeapIndex;
|
|
unsigned flags;
|
|
};
|
|
|
|
class Classpath {
|
|
public:
|
|
virtual object
|
|
makeJclass(Thread* t, GcClass* class_) = 0;
|
|
|
|
virtual GcString*
|
|
makeString(Thread* t, object array, int32_t offset, int32_t length) = 0;
|
|
|
|
virtual GcThread*
|
|
makeThread(Thread* t, Thread* parent) = 0;
|
|
|
|
virtual object
|
|
makeJMethod(Thread* t, GcMethod* vmMethod) = 0;
|
|
|
|
virtual GcMethod*
|
|
getVMMethod(Thread* t, object jmethod) = 0;
|
|
|
|
virtual object
|
|
makeJField(Thread* t, GcField* vmField) = 0;
|
|
|
|
virtual GcField*
|
|
getVMField(Thread* t, object jfield) = 0;
|
|
|
|
virtual void
|
|
clearInterrupted(Thread* t) = 0;
|
|
|
|
virtual void
|
|
runThread(Thread* t) = 0;
|
|
|
|
virtual void
|
|
resolveNative(Thread* t, GcMethod* method) = 0;
|
|
|
|
virtual void
|
|
interceptMethods(Thread* t) = 0;
|
|
|
|
virtual void
|
|
preBoot(Thread* t) = 0;
|
|
|
|
virtual bool mayInitClasses() = 0;
|
|
|
|
virtual void
|
|
boot(Thread* t) = 0;
|
|
|
|
virtual const char*
|
|
bootClasspath() = 0;
|
|
|
|
virtual object
|
|
makeDirectByteBuffer(Thread* t, void* p, jlong capacity) = 0;
|
|
|
|
virtual void*
|
|
getDirectBufferAddress(Thread* t, object buffer) = 0;
|
|
|
|
virtual int64_t
|
|
getDirectBufferCapacity(Thread* t, object buffer) = 0;
|
|
|
|
virtual bool
|
|
canTailCall(Thread* t, GcMethod* caller, GcByteArray* calleeClassName,
|
|
GcByteArray* calleeMethodName, GcByteArray* calleeMethodSpec) = 0;
|
|
|
|
virtual GcClassLoader* libraryClassLoader(Thread* t, GcMethod* caller) = 0;
|
|
|
|
virtual void
|
|
shutDown(Thread* t) = 0;
|
|
|
|
virtual void
|
|
dispose() = 0;
|
|
};
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
template <class T>
|
|
class ThreadRuntimeArray: public Thread::AutoResource {
|
|
public:
|
|
ThreadRuntimeArray(Thread* t, unsigned size):
|
|
AutoResource(t),
|
|
body(static_cast<T*>(t->m->heap->allocate(size * sizeof(T)))),
|
|
size(size)
|
|
{ }
|
|
|
|
~ThreadRuntimeArray() {
|
|
t->m->heap->free(body, size * sizeof(T));
|
|
}
|
|
|
|
virtual void release() {
|
|
ThreadRuntimeArray::~ThreadRuntimeArray();
|
|
}
|
|
|
|
T* body;
|
|
unsigned size;
|
|
};
|
|
|
|
# define THREAD_RUNTIME_ARRAY(thread, type, name, size) \
|
|
ThreadRuntimeArray<type> name(thread, size);
|
|
|
|
#else // not _MSC_VER
|
|
|
|
# define THREAD_RUNTIME_ARRAY(thread, type, name, size) \
|
|
type name##_body[size];
|
|
|
|
#endif // not _MSC_VER
|
|
|
|
Classpath*
|
|
makeClasspath(System* system, Allocator* allocator, const char* javaHome,
|
|
const char* embedPrefix);
|
|
|
|
typedef uint64_t (JNICALL *FastNativeFunction)(Thread*, GcMethod*, uintptr_t*);
|
|
|
|
inline GcClass*
|
|
objectClass(Thread*, object o)
|
|
{
|
|
return reinterpret_cast<GcClass*>(maskAlignedPointer(fieldAtOffset<object>(o, 0)));
|
|
}
|
|
inline GcClass*
|
|
objectClass(Thread*, GcObject* o)
|
|
{
|
|
return reinterpret_cast<GcClass*>(maskAlignedPointer(fieldAtOffset<object>(o, 0)));
|
|
}
|
|
|
|
inline unsigned
|
|
stackSizeInWords(Thread* t)
|
|
{
|
|
return t->m->stackSizeInBytes / BytesPerWord;
|
|
}
|
|
|
|
void
|
|
enter(Thread* t, Thread::State state);
|
|
|
|
inline void
|
|
enterActiveState(Thread* t)
|
|
{
|
|
enter(t, Thread::ActiveState);
|
|
}
|
|
|
|
class StateResource: public Thread::AutoResource {
|
|
public:
|
|
StateResource(Thread* t, Thread::State state):
|
|
AutoResource(t), oldState(t->state)
|
|
{
|
|
enter(t, state);
|
|
}
|
|
|
|
~StateResource() { enter(t, oldState); }
|
|
|
|
virtual void release() {
|
|
this->StateResource::~StateResource();
|
|
}
|
|
|
|
private:
|
|
Thread::State oldState;
|
|
};
|
|
|
|
inline void
|
|
dispose(Thread* t, Reference* r)
|
|
{
|
|
*(r->handle) = r->next;
|
|
if (r->next) {
|
|
r->next->handle = r->handle;
|
|
}
|
|
t->m->heap->free(r, sizeof(*r));
|
|
}
|
|
|
|
inline void
|
|
acquire(Thread*, Reference* r)
|
|
{
|
|
++ r->count;
|
|
}
|
|
|
|
inline void
|
|
release(Thread* t, Reference* r)
|
|
{
|
|
if ((-- r->count) == 0) {
|
|
dispose(t, r);
|
|
}
|
|
}
|
|
|
|
void
|
|
collect(Thread* t, Heap::CollectionType type, int pendingAllocation = 0);
|
|
|
|
void
|
|
shutDown(Thread* t);
|
|
|
|
#ifdef VM_STRESS
|
|
|
|
inline void
|
|
stress(Thread* t)
|
|
{
|
|
if ((not t->m->unsafe)
|
|
and (t->flags & (Thread::StressFlag | Thread::TracingFlag)) == 0
|
|
and t->state != Thread::NoState
|
|
and t->state != Thread::IdleState)
|
|
{
|
|
atomicOr(&(t->flags), Thread::StressFlag);
|
|
|
|
# ifdef VM_STRESS_MAJOR
|
|
collect(t, Heap::MajorCollection);
|
|
# else // not VM_STRESS_MAJOR
|
|
collect(t, Heap::MinorCollection);
|
|
# endif // not VM_STRESS_MAJOR
|
|
|
|
atomicAnd(&(t->flags), ~Thread::StressFlag);
|
|
}
|
|
}
|
|
|
|
#endif // not VM_STRESS
|
|
|
|
inline void
|
|
acquire(Thread* t, System::Monitor* m)
|
|
{
|
|
if (not m->tryAcquire(t->systemThread)) {
|
|
ENTER(t, Thread::IdleState);
|
|
m->acquire(t->systemThread);
|
|
}
|
|
|
|
stress(t);
|
|
}
|
|
|
|
inline void
|
|
release(Thread* t, System::Monitor* m)
|
|
{
|
|
m->release(t->systemThread);
|
|
}
|
|
|
|
class MonitorResource: public Thread::AutoResource {
|
|
public:
|
|
MonitorResource(Thread* t, System::Monitor* m):
|
|
AutoResource(t), m(m)
|
|
{
|
|
acquire(t, m);
|
|
}
|
|
|
|
~MonitorResource() {
|
|
vm::release(t, m);
|
|
}
|
|
|
|
virtual void release() {
|
|
this->MonitorResource::~MonitorResource();
|
|
}
|
|
|
|
private:
|
|
System::Monitor* m;
|
|
};
|
|
|
|
class RawMonitorResource: public Thread::Resource {
|
|
public:
|
|
RawMonitorResource(Thread* t, System::Monitor* m):
|
|
Resource(t, t->resource), m(m)
|
|
{
|
|
m->acquire(t->systemThread);
|
|
}
|
|
|
|
~RawMonitorResource() {
|
|
t->resource = next;
|
|
vm::release(t, m);
|
|
}
|
|
|
|
virtual void release() {
|
|
this->RawMonitorResource::~RawMonitorResource();
|
|
}
|
|
|
|
private:
|
|
System::Monitor* m;
|
|
};
|
|
|
|
inline Aborter* getAborter(Thread* t) {
|
|
return t->m->system;
|
|
}
|
|
|
|
inline bool
|
|
ensure(Thread* t, unsigned sizeInBytes)
|
|
{
|
|
if (t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord)
|
|
> ThreadHeapSizeInWords)
|
|
{
|
|
if (sizeInBytes <= ThreadBackupHeapSizeInBytes) {
|
|
expect(t, (t->flags & Thread::UseBackupHeapFlag) == 0);
|
|
|
|
atomicOr(&(t->flags), Thread::UseBackupHeapFlag);
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
object
|
|
allocate2(Thread* t, unsigned sizeInBytes, bool objectMask);
|
|
|
|
object
|
|
allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type,
|
|
unsigned sizeInBytes, bool objectMask);
|
|
|
|
inline object
|
|
allocateSmall(Thread* t, unsigned sizeInBytes)
|
|
{
|
|
assertT(t, t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord)
|
|
<= ThreadHeapSizeInWords);
|
|
|
|
object o = reinterpret_cast<object>(t->heap + t->heapIndex);
|
|
t->heapIndex += ceilingDivide(sizeInBytes, BytesPerWord);
|
|
return o;
|
|
}
|
|
|
|
inline object
|
|
allocate(Thread* t, unsigned sizeInBytes, bool objectMask)
|
|
{
|
|
stress(t);
|
|
|
|
if (UNLIKELY(t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord)
|
|
> ThreadHeapSizeInWords
|
|
or t->m->exclusive))
|
|
{
|
|
return allocate2(t, sizeInBytes, objectMask);
|
|
} else {
|
|
assertT(t, t->criticalLevel == 0);
|
|
return allocateSmall(t, sizeInBytes);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
mark(Thread* t, object o, unsigned offset, unsigned count)
|
|
{
|
|
t->m->heap->mark(o, offset / BytesPerWord, count);
|
|
}
|
|
|
|
inline void
|
|
mark(Thread* t, object o, unsigned offset)
|
|
{
|
|
t->m->heap->mark(o, offset / BytesPerWord, 1);
|
|
}
|
|
|
|
inline void
|
|
set(Thread* t, object target, unsigned offset, object value)
|
|
{
|
|
fieldAtOffset<object>(target, offset) = value;
|
|
mark(t, target, offset);
|
|
}
|
|
|
|
inline void
|
|
set(Thread* t, GcObject* target, unsigned offset, GcObject* value)
|
|
{
|
|
fieldAtOffset<GcObject*>(target, offset) = value;
|
|
mark(t, reinterpret_cast<object>(target), offset);
|
|
}
|
|
|
|
inline void
|
|
setObject(Thread* t, GcObject* target, unsigned offset, GcObject* value)
|
|
{
|
|
set(t, target, offset, value);
|
|
}
|
|
|
|
inline void
|
|
setObjectClass(Thread*, object o, GcClass* c)
|
|
{
|
|
fieldAtOffset<object>(o, 0)
|
|
= reinterpret_cast<object>
|
|
(reinterpret_cast<intptr_alias_t>(c)
|
|
| (reinterpret_cast<intptr_alias_t>
|
|
(fieldAtOffset<object>(o, 0)) & (~PointerMask)));
|
|
}
|
|
|
|
inline const char*
|
|
findProperty(Machine* m, const char* name)
|
|
{
|
|
for (unsigned i = 0; i < m->propertyCount; ++i) {
|
|
const char* p = m->properties[i];
|
|
const char* n = name;
|
|
while (*p and *p != '=' and *n and *p == *n) {
|
|
++ p;
|
|
++ n;
|
|
}
|
|
if (*p == '=' and *n == 0) {
|
|
return p + 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
inline const char*
|
|
findProperty(Thread* t, const char* name)
|
|
{
|
|
return findProperty(t->m, name);
|
|
}
|
|
|
|
object&
|
|
arrayBodyUnsafe(Thread*, GcArray*, unsigned);
|
|
|
|
bool
|
|
instanceOf(Thread* t, GcClass* class_, object o);
|
|
|
|
template <class T>
|
|
T* GcObject::as(Thread* t UNUSED)
|
|
{
|
|
if(this == 0) {
|
|
return 0;
|
|
}
|
|
assertT(t,
|
|
t->m->unsafe
|
|
|| instanceOf(t, reinterpret_cast<GcClass*>(arrayBodyUnsafe(t, t->m->types, T::Type)), reinterpret_cast<object>(this)));
|
|
return static_cast<T*>(this);
|
|
}
|
|
|
|
template <class T>
|
|
bool GcObject::isa(Thread* t)
|
|
{
|
|
return instanceOf(t, reinterpret_cast<GcClass*>(arrayBodyUnsafe(t, t->m->types, T::Type)), reinterpret_cast<object>(this));
|
|
}
|
|
|
|
template <class T>
|
|
T* cast(Thread* t UNUSED, object o)
|
|
{
|
|
if(o == 0) {
|
|
return 0;
|
|
}
|
|
assertT(t,
|
|
t->m->unsafe || instanceOf(t,
|
|
reinterpret_cast<GcClass*>(arrayBodyUnsafe(
|
|
t, t->m->types, T::Type)),
|
|
o));
|
|
return reinterpret_cast<T*>(o);
|
|
}
|
|
|
|
#include "type-declarations.cpp"
|
|
|
|
|
|
inline object&
|
|
arrayBodyUnsafe(Thread*, GcArray* a, unsigned index)
|
|
{
|
|
return a->body()[index];
|
|
}
|
|
|
|
inline bool Thread::Runnable::interrupted() {
|
|
return t->javaThread and t->javaThread->interrupted();
|
|
}
|
|
|
|
inline void Thread::Runnable::setInterrupted(bool v) {
|
|
t->javaThread->interrupted() = v;
|
|
}
|
|
|
|
inline uint64_t
|
|
runRaw(Thread* t,
|
|
uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments)
|
|
{
|
|
Thread::RunCheckpoint checkpoint(t);
|
|
return vmRun(function, arguments, &checkpoint);
|
|
}
|
|
|
|
inline uint64_t
|
|
run(Thread* t, uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments)
|
|
{
|
|
ENTER(t, Thread::ActiveState);
|
|
return runRaw(t, function, arguments);
|
|
}
|
|
|
|
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)
|
|
{
|
|
p->flags |= Thread::JoinFlag;
|
|
return t->m->system->success(t->m->system->start(&(p->runnable)));
|
|
}
|
|
|
|
inline void
|
|
addThread(Thread* t, Thread* p)
|
|
{
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
assertT(t, p->state == Thread::NoState);
|
|
expect(t, t->state == Thread::ActiveState || t->state == Thread::ExclusiveState || t->state == Thread::NoState);
|
|
|
|
p->state = Thread::IdleState;
|
|
++ t->m->threadCount;
|
|
++ t->m->liveCount;
|
|
|
|
p->peer = p->parent->child;
|
|
p->parent->child = p;
|
|
|
|
if (p->javaThread) {
|
|
p->javaThread->peer() = reinterpret_cast<jlong>(p);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
removeThread(Thread* t, Thread* p)
|
|
{
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
assertT(t, p->state == Thread::IdleState);
|
|
|
|
-- t->m->liveCount;
|
|
-- t->m->threadCount;
|
|
|
|
t->m->stateLock->notifyAll(t->systemThread);
|
|
|
|
p->parent->child = p->peer;
|
|
|
|
if (p->javaThread) {
|
|
p->javaThread->peer() = 0;
|
|
}
|
|
|
|
p->dispose();
|
|
}
|
|
|
|
inline Thread*
|
|
startThread(Thread* t, GcThread* javaThread)
|
|
{
|
|
{ PROTECT(t, javaThread);
|
|
|
|
stress(t);
|
|
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
if (t->m->threadCount > t->m->liveCount + ZombieCollectionThreshold) {
|
|
collect(t, Heap::MinorCollection);
|
|
}
|
|
}
|
|
|
|
Thread* p = t->m->processor->makeThread(t->m, javaThread, t);
|
|
|
|
addThread(t, p);
|
|
|
|
if (startThread(t, p)) {
|
|
return p;
|
|
} else {
|
|
removeThread(t, p);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
registerDaemon(Thread* t)
|
|
{
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
atomicOr(&(t->flags), Thread::DaemonFlag);
|
|
|
|
++ t->m->daemonCount;
|
|
|
|
t->m->stateLock->notifyAll(t->systemThread);
|
|
}
|
|
|
|
inline void
|
|
checkDaemon(Thread* t)
|
|
{
|
|
if (t->javaThread->daemon()) {
|
|
registerDaemon(t);
|
|
}
|
|
}
|
|
|
|
inline uint64_t
|
|
initAttachedThread(Thread* t, uintptr_t* arguments)
|
|
{
|
|
bool daemon = arguments[0];
|
|
|
|
t->javaThread = t->m->classpath->makeThread(t, t->m->rootThread);
|
|
|
|
t->javaThread->peer() = reinterpret_cast<jlong>(t);
|
|
|
|
if (daemon) {
|
|
t->javaThread->daemon() = true;
|
|
|
|
registerDaemon(t);
|
|
}
|
|
|
|
t->m->localThread->set(t);
|
|
|
|
return 1;
|
|
}
|
|
|
|
inline Thread*
|
|
attachThread(Machine* m, bool daemon)
|
|
{
|
|
Thread* t = m->processor->makeThread(m, 0, m->rootThread);
|
|
m->system->attach(&(t->runnable));
|
|
|
|
addThread(t, t);
|
|
|
|
enter(t, Thread::ActiveState);
|
|
|
|
uintptr_t arguments[] = { daemon };
|
|
|
|
if (run(t, initAttachedThread, arguments)) {
|
|
enter(t, Thread::IdleState);
|
|
return t;
|
|
} else {
|
|
t->exit();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
inline object&
|
|
root(Thread* t, Machine::Root root)
|
|
{
|
|
return t->m->roots->body()[root];
|
|
}
|
|
|
|
inline void
|
|
setRoot(Thread* t, Machine::Root root, object value)
|
|
{
|
|
set(t, reinterpret_cast<object>(t->m->roots), ArrayBody + (root * BytesPerWord), value);
|
|
}
|
|
|
|
inline GcClass*
|
|
type(Thread* t, Gc::Type type)
|
|
{
|
|
return cast<GcClass>(t, t->m->types->body()[type]);
|
|
}
|
|
|
|
inline void
|
|
setType(Thread* t, Gc::Type type, GcClass* value)
|
|
{
|
|
set(t, t->m->types, ArrayBody + (type * BytesPerWord), value);
|
|
}
|
|
|
|
inline bool
|
|
objectFixed(Thread*, object o)
|
|
{
|
|
return (alias(o, 0) & (~PointerMask)) == FixedMark;
|
|
}
|
|
|
|
inline bool
|
|
objectExtended(Thread*, object o)
|
|
{
|
|
return (alias(o, 0) & (~PointerMask)) == ExtendedMark;
|
|
}
|
|
|
|
inline bool
|
|
hashTaken(Thread*, object o)
|
|
{
|
|
return (alias(o, 0) & (~PointerMask)) == HashTakenMark;
|
|
}
|
|
|
|
inline unsigned
|
|
baseSize(Thread* t UNUSED, object o, GcClass* class_)
|
|
{
|
|
assertT(t, class_->fixedSize() >= BytesPerWord);
|
|
|
|
return ceilingDivide(class_->fixedSize(), BytesPerWord)
|
|
+ ceilingDivide(class_->arrayElementSize()
|
|
* fieldAtOffset<uintptr_t>(o, class_->fixedSize() - BytesPerWord),
|
|
BytesPerWord);
|
|
}
|
|
|
|
object
|
|
makeTrace(Thread* t, Processor::StackWalker* walker);
|
|
|
|
object
|
|
makeTrace(Thread* t, Thread* target);
|
|
|
|
inline object
|
|
makeTrace(Thread* t)
|
|
{
|
|
return makeTrace(t, t);
|
|
}
|
|
|
|
inline object
|
|
makeNew(Thread* t, GcClass* class_)
|
|
{
|
|
assertT(t, t->state == Thread::NoState or t->state == Thread::ActiveState);
|
|
|
|
PROTECT(t, class_);
|
|
unsigned sizeInBytes = pad(class_->fixedSize());
|
|
assertT(t, sizeInBytes);
|
|
object instance = allocate(t, sizeInBytes, class_->objectMask());
|
|
setObjectClass(t, instance, class_);
|
|
|
|
return instance;
|
|
}
|
|
|
|
object
|
|
makeNewGeneral(Thread* t, GcClass* class_);
|
|
|
|
inline object
|
|
make(Thread* t, GcClass* class_)
|
|
{
|
|
if (UNLIKELY(class_->vmFlags()
|
|
& (WeakReferenceFlag | HasFinalizerFlag)))
|
|
{
|
|
return makeNewGeneral(t, class_);
|
|
} else {
|
|
return makeNew(t, class_);
|
|
}
|
|
}
|
|
|
|
GcByteArray*
|
|
makeByteArrayV(Thread* t, const char* format, va_list a, int size);
|
|
|
|
GcByteArray*
|
|
makeByteArray(Thread* t, const char* format, ...);
|
|
|
|
GcString*
|
|
makeString(Thread* t, const char* format, ...);
|
|
|
|
#ifndef HAVE_StringOffset
|
|
|
|
inline uint32_t GcString::length(Thread* t)
|
|
{
|
|
return cast<GcCharArray>(t, this->data())->length();
|
|
}
|
|
|
|
inline uint32_t GcString::offset(Thread*)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
# ifndef HAVE_StringHash32
|
|
|
|
inline GcString*
|
|
makeString(Thread* t, object data, int32_t hash, int32_t)
|
|
{
|
|
return makeString(t, data, hash);
|
|
}
|
|
|
|
# endif // not HAVE_StringHash32
|
|
|
|
inline GcString*
|
|
makeString(Thread* t, object data, unsigned offset, unsigned length, unsigned)
|
|
{
|
|
if (offset == 0 and length == charArrayLength(t, data)) {
|
|
return makeString(t, data, 0, 0);
|
|
} else {
|
|
PROTECT(t, data);
|
|
|
|
object array = reinterpret_cast<object>(makeCharArray(t, length));
|
|
|
|
memcpy(&charArrayBody(t, array, 0), &charArrayBody(t, data, offset),
|
|
length * 2);
|
|
|
|
return makeString(t, array, 0, 0);
|
|
}
|
|
}
|
|
|
|
#endif // not HAVE_StringOffset
|
|
|
|
int
|
|
stringUTFLength(Thread* t, GcString* string, unsigned start, unsigned length);
|
|
|
|
inline int
|
|
stringUTFLength(Thread* t, GcString* string)
|
|
{
|
|
return stringUTFLength(t, string, 0, string->length(t));
|
|
}
|
|
|
|
void
|
|
stringChars(Thread* t, GcString* string, unsigned start, unsigned length,
|
|
char* chars);
|
|
|
|
inline void
|
|
stringChars(Thread* t, GcString* string, char* chars)
|
|
{
|
|
stringChars(t, string, 0, string->length(t), chars);
|
|
}
|
|
|
|
void
|
|
stringChars(Thread* t, GcString* string, unsigned start, unsigned length,
|
|
uint16_t* chars);
|
|
|
|
inline void
|
|
stringChars(Thread* t, GcString* string, uint16_t* chars)
|
|
{
|
|
stringChars(t, string, 0, string->length(t), chars);
|
|
}
|
|
|
|
void
|
|
stringUTFChars(Thread* t, GcString* string, unsigned start, unsigned length,
|
|
char* chars, unsigned charsLength);
|
|
|
|
inline void
|
|
stringUTFChars(Thread* t, GcString* string, char* chars, unsigned charsLength)
|
|
{
|
|
stringUTFChars(t, string, 0, string->length(t), chars, charsLength);
|
|
}
|
|
|
|
bool
|
|
isAssignableFrom(Thread* t, GcClass* a, GcClass* b);
|
|
|
|
GcMethod*
|
|
classInitializer(Thread* t, GcClass* class_);
|
|
|
|
object
|
|
frameMethod(Thread* t, int frame);
|
|
|
|
inline uintptr_t&
|
|
extendedWord(Thread* t UNUSED, object o, unsigned baseSize)
|
|
{
|
|
assertT(t, objectExtended(t, o));
|
|
return fieldAtOffset<uintptr_t>(o, baseSize * BytesPerWord);
|
|
}
|
|
|
|
inline unsigned
|
|
extendedSize(Thread* t, object o, unsigned baseSize)
|
|
{
|
|
return baseSize + objectExtended(t, o);
|
|
}
|
|
|
|
inline void
|
|
markHashTaken(Thread* t, object o)
|
|
{
|
|
assertT(t, not objectExtended(t, o));
|
|
assertT(t, not objectFixed(t, o));
|
|
|
|
ACQUIRE_RAW(t, t->m->heapLock);
|
|
|
|
alias(o, 0) |= HashTakenMark;
|
|
t->m->heap->pad(o);
|
|
}
|
|
|
|
inline uint32_t
|
|
takeHash(Thread*, object o)
|
|
{
|
|
// some broken code implicitly relies on System.identityHashCode
|
|
// always returning a non-negative number (e.g. old versions of
|
|
// com/sun/xml/bind/v2/util/CollisionCheckStack.hash), hence the "&
|
|
// 0x7FFFFFFF":
|
|
return (reinterpret_cast<uintptr_t>(o) / BytesPerWord) & 0x7FFFFFFF;
|
|
}
|
|
|
|
inline uint32_t
|
|
objectHash(Thread* t, object o)
|
|
{
|
|
if (objectExtended(t, o)) {
|
|
return extendedWord(t, o, baseSize(t, o, objectClass(t, o)));
|
|
} else {
|
|
if (not objectFixed(t, o)) {
|
|
markHashTaken(t, o);
|
|
}
|
|
return takeHash(t, o);
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
objectEqual(Thread*, object a, object b)
|
|
{
|
|
return a == b;
|
|
}
|
|
|
|
inline uint32_t
|
|
byteArrayHash(Thread* t UNUSED, object ao)
|
|
{
|
|
GcByteArray* a = cast<GcByteArray>(t, ao);
|
|
return hash(a->body());
|
|
}
|
|
|
|
inline uint32_t
|
|
charArrayHash(Thread* t UNUSED, object ao)
|
|
{
|
|
GcByteArray* a = cast<GcByteArray>(t, ao);
|
|
return hash(a->body());
|
|
}
|
|
|
|
inline bool
|
|
byteArrayEqual(Thread* t UNUSED, object ao, object bo)
|
|
{
|
|
GcByteArray* a = cast<GcByteArray>(t, ao);
|
|
GcByteArray* b = cast<GcByteArray>(t, bo);
|
|
return a == b or
|
|
((a->length() == b->length()) and
|
|
memcmp(a->body().begin(), b->body().begin(),
|
|
a->length()) == 0);
|
|
}
|
|
|
|
inline uint32_t
|
|
stringHash(Thread* t, object so)
|
|
{
|
|
GcString* s = cast<GcString>(t, so);
|
|
if (s->hashCode() == 0 and s->length(t)) {
|
|
object data = reinterpret_cast<object>(s->data());
|
|
if (objectClass(t, data) == type(t, GcByteArray::Type)) {
|
|
s->hashCode() = hash
|
|
(cast<GcByteArray>(t, data)->body().subslice(s->offset(t), s->length(t)));
|
|
} else {
|
|
s->hashCode() = hash
|
|
(cast<GcCharArray>(t, data)->body().subslice(s->offset(t), s->length(t)));
|
|
}
|
|
}
|
|
return s->hashCode();
|
|
}
|
|
|
|
inline uint16_t
|
|
stringCharAt(Thread* t, GcString* s, int i)
|
|
{
|
|
object data = reinterpret_cast<object>(s->data());
|
|
if (objectClass(t, data) == type(t, GcByteArray::Type)) {
|
|
return cast<GcByteArray>(t, data)->body()[s->offset(t) + i];
|
|
} else {
|
|
return cast<GcCharArray>(t, data)->body()[s->offset(t) + i];
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
stringEqual(Thread* t, object ao, object bo)
|
|
{
|
|
GcString* a = cast<GcString>(t, ao);
|
|
GcString* b = cast<GcString>(t, bo);
|
|
if (a == b) {
|
|
return true;
|
|
} else if (a->length(t) == b->length(t)) {
|
|
for (unsigned i = 0; i < a->length(t); ++i) {
|
|
if (stringCharAt(t, a, i) != stringCharAt(t, b, i)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline uint32_t
|
|
methodHash(Thread* t, object mo)
|
|
{
|
|
GcMethod* method = cast<GcMethod>(t, mo);
|
|
return byteArrayHash(t, reinterpret_cast<object>(method->name()))
|
|
^ byteArrayHash(t, reinterpret_cast<object>(method->spec()));
|
|
}
|
|
|
|
inline bool
|
|
methodEqual(Thread* t, object ao, object bo)
|
|
{
|
|
GcMethod* a = cast<GcMethod>(t, ao);
|
|
GcMethod* b = cast<GcMethod>(t, bo);
|
|
return a == b or
|
|
(byteArrayEqual(t, reinterpret_cast<object>(a->name()), reinterpret_cast<object>(b->name())) and
|
|
byteArrayEqual(t, reinterpret_cast<object>(a->spec()), reinterpret_cast<object>(b->spec())));
|
|
}
|
|
|
|
class MethodSpecIterator {
|
|
public:
|
|
MethodSpecIterator(Thread* t, const char* s):
|
|
t(t), s(s + 1)
|
|
{ }
|
|
|
|
const char* next() {
|
|
assertT(t, *s != ')');
|
|
|
|
const char* p = s;
|
|
|
|
switch (*s) {
|
|
case 'L':
|
|
while (*s and *s != ';') ++ s;
|
|
++ s;
|
|
break;
|
|
|
|
case '[':
|
|
while (*s == '[') ++ s;
|
|
switch (*s) {
|
|
case 'L':
|
|
while (*s and *s != ';') ++ s;
|
|
++ s;
|
|
break;
|
|
|
|
default:
|
|
++ s;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
++ s;
|
|
break;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
bool hasNext() {
|
|
return *s != ')';
|
|
}
|
|
|
|
const char* returnSpec() {
|
|
assertT(t, *s == ')');
|
|
return s + 1;
|
|
}
|
|
|
|
Thread* t;
|
|
const char* s;
|
|
};
|
|
|
|
unsigned
|
|
fieldCode(Thread* t, unsigned javaCode);
|
|
|
|
unsigned
|
|
fieldType(Thread* t, unsigned code);
|
|
|
|
unsigned
|
|
primitiveSize(Thread* t, unsigned code);
|
|
|
|
inline unsigned
|
|
fieldSize(Thread* t, unsigned code)
|
|
{
|
|
if (code == ObjectField) {
|
|
return BytesPerWord;
|
|
} else {
|
|
return primitiveSize(t, code);
|
|
}
|
|
}
|
|
|
|
inline unsigned
|
|
fieldSize(Thread* t, GcField* field)
|
|
{
|
|
return fieldSize(t, field->code());
|
|
}
|
|
|
|
inline void
|
|
scanMethodSpec(Thread* t, const char* s, bool static_,
|
|
unsigned* parameterCount, unsigned* parameterFootprint,
|
|
unsigned* returnCode)
|
|
{
|
|
unsigned count = 0;
|
|
unsigned footprint = 0;
|
|
MethodSpecIterator it(t, s);
|
|
while (it.hasNext()) {
|
|
++ count;
|
|
switch (*it.next()) {
|
|
case 'J':
|
|
case 'D':
|
|
footprint += 2;
|
|
break;
|
|
|
|
default:
|
|
++ footprint;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (not static_) {
|
|
++ footprint;
|
|
}
|
|
|
|
*parameterCount = count;
|
|
*parameterFootprint = footprint;
|
|
*returnCode = fieldCode(t, *it.returnSpec());
|
|
}
|
|
|
|
GcClass*
|
|
findLoadedClass(Thread* t, GcClassLoader* loader, GcByteArray* spec);
|
|
|
|
inline bool
|
|
emptyMethod(Thread* t UNUSED, GcMethod* method)
|
|
{
|
|
return ((method->flags() & ACC_NATIVE) == 0)
|
|
and (method->code()->length() == 1)
|
|
and (method->code()->body()[0] == return_);
|
|
}
|
|
|
|
object
|
|
parseUtf8(Thread* t, const char* data, unsigned length);
|
|
|
|
object
|
|
parseUtf8(Thread* t, GcByteArray* array);
|
|
|
|
GcClass*
|
|
parseClass(Thread* t, GcClassLoader* loader, const uint8_t* data, unsigned length,
|
|
Gc::Type throwType = GcNoClassDefFoundError::Type);
|
|
|
|
GcClass*
|
|
resolveClass(Thread* t, GcClassLoader* loader, GcByteArray* name, bool throw_ = true,
|
|
Gc::Type throwType = GcNoClassDefFoundError::Type);
|
|
|
|
inline GcClass*
|
|
resolveClass(Thread* t, GcClassLoader* loader, const char* name, bool throw_ = true,
|
|
Gc::Type throwType = GcNoClassDefFoundError::Type)
|
|
{
|
|
PROTECT(t, loader);
|
|
GcByteArray* n = makeByteArray(t, "%s", name);
|
|
return resolveClass(t, loader, n, throw_, throwType);
|
|
}
|
|
|
|
GcClass*
|
|
resolveSystemClass
|
|
(Thread* t, GcClassLoader* loader, GcByteArray* name, bool throw_ = true,
|
|
Gc::Type throwType = GcNoClassDefFoundError::Type);
|
|
|
|
inline GcClass*
|
|
resolveSystemClass(Thread* t, GcClassLoader* loader, const char* name)
|
|
{
|
|
return resolveSystemClass(t, loader, makeByteArray(t, "%s", name));
|
|
}
|
|
|
|
void
|
|
linkClass(Thread* t, GcClassLoader* loader, GcClass* class_);
|
|
|
|
GcMethod*
|
|
resolveMethod(Thread* t, GcClass* class_, const char* methodName,
|
|
const char* methodSpec);
|
|
|
|
inline GcMethod*
|
|
resolveMethod(Thread* t, GcClassLoader* loader, const char* className,
|
|
const char* methodName, const char* methodSpec)
|
|
{
|
|
return resolveMethod
|
|
(t, resolveClass(t, loader, className), methodName, methodSpec);
|
|
}
|
|
|
|
GcField*
|
|
resolveField(Thread* t, GcClass* class_, const char* fieldName,
|
|
const char* fieldSpec);
|
|
|
|
inline GcField*
|
|
resolveField(Thread* t, GcClassLoader* loader, const char* className,
|
|
const char* fieldName, const char* fieldSpec)
|
|
{
|
|
return resolveField
|
|
(t, resolveClass(t, loader, className), fieldName, fieldSpec);
|
|
}
|
|
|
|
bool
|
|
classNeedsInit(Thread* t, GcClass* c);
|
|
|
|
bool
|
|
preInitClass(Thread* t, GcClass* c);
|
|
|
|
void
|
|
postInitClass(Thread* t, GcClass* c);
|
|
|
|
void
|
|
initClass(Thread* t, GcClass* c);
|
|
|
|
GcClass*
|
|
resolveObjectArrayClass(Thread* t, GcClassLoader* loader, GcClass* elementClass);
|
|
|
|
object
|
|
makeObjectArray(Thread* t, GcClass* elementClass, unsigned count);
|
|
|
|
inline object
|
|
makeObjectArray(Thread* t, unsigned count)
|
|
{
|
|
return makeObjectArray(t, type(t, GcJobject::Type), count);
|
|
}
|
|
|
|
object
|
|
findFieldInClass(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec);
|
|
|
|
inline object
|
|
findFieldInClass2(Thread* t, GcClass* class_, const char* name, const char* spec)
|
|
{
|
|
PROTECT(t, class_);
|
|
GcByteArray* n = makeByteArray(t, "%s", name);
|
|
PROTECT(t, n);
|
|
GcByteArray* s = makeByteArray(t, "%s", spec);
|
|
return findFieldInClass(t, class_, n, s);
|
|
}
|
|
|
|
object
|
|
findMethodInClass(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec);
|
|
|
|
inline GcThrowable*
|
|
makeThrowable
|
|
(Thread* t, Gc::Type type, object message = 0, object trace = 0,
|
|
GcThrowable* cause = 0)
|
|
{
|
|
PROTECT(t, message);
|
|
PROTECT(t, trace);
|
|
PROTECT(t, cause);
|
|
|
|
if (trace == 0) {
|
|
trace = makeTrace(t);
|
|
}
|
|
|
|
GcThrowable* result = cast<GcThrowable>(t, make(t, vm::type(t, type)));
|
|
|
|
set(t, reinterpret_cast<object>(result), ThrowableMessage, message);
|
|
set(t, reinterpret_cast<object>(result), ThrowableTrace, trace);
|
|
set(t, result, ThrowableCause, cause);
|
|
|
|
return result;
|
|
}
|
|
|
|
inline GcThrowable*
|
|
makeThrowableV(Thread* t, Gc::Type type, const char* format, va_list a,
|
|
int size)
|
|
{
|
|
GcByteArray* s = makeByteArrayV(t, format, a, size);
|
|
|
|
if (s) {
|
|
GcString* message = t->m->classpath->makeString
|
|
(t, reinterpret_cast<object>(s), 0, s->length() - 1);
|
|
|
|
return makeThrowable(t, type, reinterpret_cast<object>(message));
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
inline GcThrowable*
|
|
makeThrowable(Thread* t, Gc::Type type, const char* format, ...)
|
|
{
|
|
int size = 256;
|
|
while (true) {
|
|
va_list a;
|
|
va_start(a, format);
|
|
GcThrowable* r = makeThrowableV(t, type, format, a, size);
|
|
va_end(a);
|
|
|
|
if (r) {
|
|
return r;
|
|
} else {
|
|
size *= 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
popResources(Thread* t);
|
|
|
|
} // namespace vm
|
|
|
|
AVIAN_EXPORT void
|
|
vmPrintTrace(vm::Thread* t);
|
|
|
|
AVIAN_EXPORT void
|
|
vmfPrintTrace(vm::Thread* t, FILE* out);
|
|
|
|
namespace vm {
|
|
|
|
void
|
|
dumpHeap(Thread* t, FILE* out);
|
|
|
|
inline void NO_RETURN
|
|
throw_(Thread* t, GcThrowable* e)
|
|
{
|
|
assertT(t, t->exception == 0);
|
|
assertT(t, e);
|
|
|
|
expect(t, not t->checkpoint->noThrow);
|
|
|
|
t->exception = e;
|
|
|
|
if (objectClass(t, e) == type(t, GcOutOfMemoryError::Type)) {
|
|
#ifdef AVIAN_HEAPDUMP
|
|
if (not t->m->dumpedHeapOnOOM) {
|
|
t->m->dumpedHeapOnOOM = true;
|
|
const char* path = findProperty(t, "avian.heap.dump");
|
|
if (path) {
|
|
FILE* out = vm::fopen(path, "wb");
|
|
if (out) {
|
|
dumpHeap(t, out);
|
|
fclose(out);
|
|
}
|
|
}
|
|
}
|
|
#endif//AVIAN_HEAPDUMP
|
|
|
|
if (AbortOnOutOfMemoryError) {
|
|
fprintf(stderr, "OutOfMemoryError\n");
|
|
vmPrintTrace(t);
|
|
abort();
|
|
}
|
|
}
|
|
|
|
// printTrace(t, e);
|
|
|
|
popResources(t);
|
|
|
|
t->checkpoint->unwind();
|
|
|
|
abort(t);
|
|
}
|
|
|
|
inline void NO_RETURN
|
|
throwNew
|
|
(Thread* t, Gc::Type type, object message = 0, object trace = 0,
|
|
GcThrowable* cause = 0)
|
|
{
|
|
throw_(t, makeThrowable(t, type, message, trace, cause));
|
|
}
|
|
|
|
inline void NO_RETURN
|
|
throwNew(Thread* t, Gc::Type type, const char* format, ...)
|
|
{
|
|
int size = 256;
|
|
while (true) {
|
|
va_list a;
|
|
va_start(a, format);
|
|
GcThrowable* r = makeThrowableV(t, type, format, a, size);
|
|
va_end(a);
|
|
|
|
if (r) {
|
|
throw_(t, r);
|
|
} else {
|
|
size *= 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
object
|
|
findInHierarchyOrNull(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec,
|
|
object (*find)(Thread*, GcClass*, GcByteArray*, GcByteArray*));
|
|
|
|
inline object
|
|
findInHierarchy(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec,
|
|
object (*find)(Thread*, GcClass*, GcByteArray*, GcByteArray*),
|
|
Gc::Type errorType, bool throw_ = true)
|
|
{
|
|
object o = findInHierarchyOrNull(t, class_, name, spec, find);
|
|
|
|
if (throw_ and o == 0) {
|
|
throwNew(t, errorType, "%s %s not found in %s",
|
|
name->body().begin(),
|
|
spec->body().begin(),
|
|
class_->name()->body().begin());
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
inline GcMethod*
|
|
findMethod(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec)
|
|
{
|
|
return cast<GcMethod>(t, findInHierarchy
|
|
(t, class_, name, spec, findMethodInClass, GcNoSuchMethodError::Type));
|
|
}
|
|
|
|
inline GcMethod*
|
|
findMethodOrNull(Thread* t, GcClass* class_, const char* name, const char* spec)
|
|
{
|
|
PROTECT(t, class_);
|
|
GcByteArray* n = makeByteArray(t, "%s", name);
|
|
PROTECT(t, n);
|
|
GcByteArray* s = makeByteArray(t, "%s", spec);
|
|
return cast<GcMethod>(t, findInHierarchyOrNull(t, class_, n, s, findMethodInClass));
|
|
}
|
|
|
|
inline GcMethod*
|
|
findVirtualMethod(Thread* t, GcMethod* method, GcClass* class_)
|
|
{
|
|
return cast<GcMethod>(t, cast<GcArray>(t, class_->virtualTable())->body()[method->offset()]);
|
|
}
|
|
|
|
inline GcMethod*
|
|
findInterfaceMethod(Thread* t, GcMethod* method, GcClass* class_)
|
|
{
|
|
assertT(t, (class_->vmFlags() & BootstrapFlag) == 0);
|
|
|
|
object interface = reinterpret_cast<object>(method->class_());
|
|
GcArray* itable = cast<GcArray>(t, class_->interfaceTable());
|
|
for (unsigned i = 0; i < itable->length(); i += 2) {
|
|
if (itable->body()[i] == interface) {
|
|
return cast<GcMethod>(t, cast<GcArray>(t, itable->body()[i + 1])->body()[method->offset()]);
|
|
}
|
|
}
|
|
abort(t);
|
|
}
|
|
|
|
inline unsigned
|
|
objectArrayLength(Thread* t UNUSED, object array)
|
|
{
|
|
assertT(t, objectClass(t, array)->fixedSize() == BytesPerWord * 2);
|
|
assertT(t, objectClass(t, array)->arrayElementSize() == BytesPerWord);
|
|
return fieldAtOffset<uintptr_t>(array, BytesPerWord);
|
|
}
|
|
|
|
inline object&
|
|
objectArrayBody(Thread* t UNUSED, object array, unsigned index)
|
|
{
|
|
assertT(t, objectClass(t, array)->fixedSize() == BytesPerWord * 2);
|
|
assertT(t, objectClass(t, array)->arrayElementSize() == BytesPerWord);
|
|
assertT(t, objectClass(t, array)->objectMask()
|
|
== cast<GcClass>(t, t->m->types->body()[GcArray::Type])->objectMask());
|
|
return fieldAtOffset<object>(array, ArrayBody + (index * BytesPerWord));
|
|
}
|
|
|
|
unsigned
|
|
parameterFootprint(Thread* t, const char* s, bool static_);
|
|
|
|
void
|
|
addFinalizer(Thread* t, object target, void (*finalize)(Thread*, object));
|
|
|
|
inline bool
|
|
acquireSystem(Thread* t, Thread* target)
|
|
{
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
if (t->state != Thread::JoinedState) {
|
|
atomicOr(&(target->flags), Thread::SystemFlag);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
releaseSystem(Thread* t, Thread* target)
|
|
{
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
assertT(t, t->state != Thread::JoinedState);
|
|
|
|
atomicAnd(&(target->flags), ~Thread::SystemFlag);
|
|
}
|
|
|
|
inline bool
|
|
atomicCompareAndSwapObject(Thread* t, object target, unsigned offset,
|
|
object old, object new_)
|
|
{
|
|
if (atomicCompareAndSwap(&fieldAtOffset<uintptr_t>(target, offset),
|
|
reinterpret_cast<uintptr_t>(old),
|
|
reinterpret_cast<uintptr_t>(new_)))
|
|
{
|
|
mark(t, target, offset);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// The following two methods (monitorAtomicAppendAcquire and
|
|
// monitorAtomicPollAcquire) use the Michael and Scott Non-Blocking
|
|
// Queue Algorithm: http://www.cs.rochester.edu/u/michael/PODC96.html
|
|
|
|
inline void
|
|
monitorAtomicAppendAcquire(Thread* t, GcMonitor* monitor, GcMonitorNode* node)
|
|
{
|
|
if (node == 0) {
|
|
PROTECT(t, monitor);
|
|
|
|
node = makeMonitorNode(t, t, 0);
|
|
}
|
|
|
|
while (true) {
|
|
GcMonitorNode* tail = cast<GcMonitorNode>(t, monitor->acquireTail());
|
|
|
|
loadMemoryBarrier();
|
|
|
|
object next = tail->next();
|
|
|
|
loadMemoryBarrier();
|
|
|
|
if (tail == cast<GcMonitorNode>(t, monitor->acquireTail())) {
|
|
if (next) {
|
|
atomicCompareAndSwapObject
|
|
(t, reinterpret_cast<object>(monitor), MonitorAcquireTail, reinterpret_cast<object>(tail), next);
|
|
} else if (atomicCompareAndSwapObject
|
|
(t, reinterpret_cast<object>(tail), MonitorNodeNext, 0, reinterpret_cast<object>(node)))
|
|
{
|
|
atomicCompareAndSwapObject
|
|
(t, reinterpret_cast<object>(monitor), MonitorAcquireTail, reinterpret_cast<object>(tail), reinterpret_cast<object>(node));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline Thread*
|
|
monitorAtomicPollAcquire(Thread* t, GcMonitor* monitor, bool remove)
|
|
{
|
|
while (true) {
|
|
GcMonitorNode* head = cast<GcMonitorNode>(t, monitor->acquireHead());
|
|
|
|
loadMemoryBarrier();
|
|
|
|
GcMonitorNode* tail = cast<GcMonitorNode>(t, monitor->acquireTail());
|
|
|
|
loadMemoryBarrier();
|
|
|
|
GcMonitorNode* next = cast<GcMonitorNode>(t, head->next());
|
|
|
|
loadMemoryBarrier();
|
|
|
|
if (head == cast<GcMonitorNode>(t, monitor->acquireHead())) {
|
|
if (head == tail) {
|
|
if (next) {
|
|
atomicCompareAndSwapObject
|
|
(t, reinterpret_cast<object>(monitor), MonitorAcquireTail, reinterpret_cast<object>(tail), reinterpret_cast<object>(next));
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else {
|
|
Thread* value = static_cast<Thread*>(next->value());
|
|
if ((not remove)
|
|
or atomicCompareAndSwapObject
|
|
(t, reinterpret_cast<object>(monitor), MonitorAcquireHead, reinterpret_cast<object>(head), reinterpret_cast<object>(next)))
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
monitorTryAcquire(Thread* t, GcMonitor* monitor)
|
|
{
|
|
if (monitor->owner() == t
|
|
or (monitorAtomicPollAcquire(t, monitor, false) == 0
|
|
and atomicCompareAndSwap
|
|
(reinterpret_cast<uintptr_t*>(&monitor->owner()), 0,
|
|
reinterpret_cast<uintptr_t>(t))))
|
|
{
|
|
++ monitor->depth();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
monitorAcquire(Thread* t, GcMonitor* monitor, GcMonitorNode* node = 0)
|
|
{
|
|
if (not monitorTryAcquire(t, monitor)) {
|
|
PROTECT(t, monitor);
|
|
PROTECT(t, node);
|
|
|
|
ACQUIRE(t, t->lock);
|
|
|
|
monitorAtomicAppendAcquire(t, monitor, node);
|
|
|
|
// note that we don't try to acquire the lock until we're first in
|
|
// line, both because it's fair and because we don't support
|
|
// removing elements from arbitrary positions in the queue
|
|
|
|
while (not (t == monitorAtomicPollAcquire(t, monitor, false)
|
|
and atomicCompareAndSwap
|
|
(reinterpret_cast<uintptr_t*>(&monitor->owner()), 0,
|
|
reinterpret_cast<uintptr_t>(t))))
|
|
{
|
|
ENTER(t, Thread::IdleState);
|
|
|
|
t->lock->wait(t->systemThread, 0);
|
|
}
|
|
|
|
expect(t, t == monitorAtomicPollAcquire(t, monitor, true));
|
|
|
|
++ monitor->depth();
|
|
}
|
|
|
|
assertT(t, monitor->owner() == t);
|
|
}
|
|
|
|
inline void
|
|
monitorRelease(Thread* t, GcMonitor* monitor)
|
|
{
|
|
expect(t, monitor->owner() == t);
|
|
|
|
if (-- monitor->depth() == 0) {
|
|
monitor->owner() = 0;
|
|
|
|
storeLoadMemoryBarrier();
|
|
|
|
Thread* next = monitorAtomicPollAcquire(t, monitor, false);
|
|
|
|
if (next and acquireSystem(t, next)) {
|
|
ACQUIRE(t, next->lock);
|
|
|
|
next->lock->notify(t->systemThread);
|
|
|
|
releaseSystem(t, next);
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void
|
|
monitorAppendWait(Thread* t, GcMonitor* monitor)
|
|
{
|
|
assertT(t, monitor->owner() == t);
|
|
|
|
expect(t, (t->flags & Thread::WaitingFlag) == 0);
|
|
expect(t, t->waitNext == 0);
|
|
|
|
atomicOr(&(t->flags), Thread::WaitingFlag);
|
|
|
|
if (monitor->waitTail()) {
|
|
static_cast<Thread*>(monitor->waitTail())->waitNext = t;
|
|
} else {
|
|
monitor->waitHead() = t;
|
|
}
|
|
|
|
monitor->waitTail() = t;
|
|
}
|
|
|
|
inline void
|
|
monitorRemoveWait(Thread* t, GcMonitor* monitor)
|
|
{
|
|
assertT(t, monitor->owner() == t);
|
|
|
|
Thread* previous = 0;
|
|
for (Thread* current = static_cast<Thread*>(monitor->waitHead());
|
|
current; current = current->waitNext)
|
|
{
|
|
if (t == current) {
|
|
if (t == monitor->waitHead()) {
|
|
monitor->waitHead() = t->waitNext;
|
|
} else {
|
|
previous->waitNext = t->waitNext;
|
|
}
|
|
|
|
if (t == monitor->waitTail()) {
|
|
assertT(t, t->waitNext == 0);
|
|
monitor->waitTail() = previous;
|
|
}
|
|
|
|
t->waitNext = 0;
|
|
atomicAnd(&(t->flags), ~Thread::WaitingFlag);
|
|
|
|
return;
|
|
} else {
|
|
previous = current;
|
|
}
|
|
}
|
|
|
|
abort(t);
|
|
}
|
|
|
|
inline bool
|
|
monitorFindWait(Thread* t, GcMonitor* monitor)
|
|
{
|
|
assertT(t, monitor->owner() == t);
|
|
|
|
for (Thread* current = static_cast<Thread*>(monitor->waitHead());
|
|
current; current = current->waitNext)
|
|
{
|
|
if (t == current) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
inline bool
|
|
monitorWait(Thread* t, GcMonitor* monitor, int64_t time)
|
|
{
|
|
expect(t, monitor->owner() == t);
|
|
|
|
bool interrupted;
|
|
unsigned depth;
|
|
|
|
PROTECT(t, monitor);
|
|
|
|
// pre-allocate monitor node so we don't get an OutOfMemoryError
|
|
// when we try to re-acquire the monitor below
|
|
GcMonitorNode* monitorNode = makeMonitorNode(t, t, 0);
|
|
PROTECT(t, monitorNode);
|
|
|
|
{ ACQUIRE(t, t->lock);
|
|
|
|
monitorAppendWait(t, monitor);
|
|
|
|
depth = monitor->depth();
|
|
monitor->depth() = 1;
|
|
|
|
monitorRelease(t, monitor);
|
|
|
|
ENTER(t, Thread::IdleState);
|
|
|
|
interrupted = t->lock->waitAndClearInterrupted(t->systemThread, time);
|
|
}
|
|
|
|
monitorAcquire(t, monitor, monitorNode);
|
|
|
|
monitor->depth() = depth;
|
|
|
|
if (t->flags & Thread::WaitingFlag) {
|
|
monitorRemoveWait(t, monitor);
|
|
} else {
|
|
expect(t, not monitorFindWait(t, monitor));
|
|
}
|
|
|
|
assertT(t, monitor->owner() == t);
|
|
|
|
return interrupted;
|
|
}
|
|
|
|
inline Thread*
|
|
monitorPollWait(Thread* t UNUSED, GcMonitor* monitor)
|
|
{
|
|
assertT(t, monitor->owner() == t);
|
|
|
|
Thread* next = static_cast<Thread*>(monitor->waitHead());
|
|
|
|
if (next) {
|
|
monitor->waitHead() = next->waitNext;
|
|
atomicAnd(&(next->flags), ~Thread::WaitingFlag);
|
|
next->waitNext = 0;
|
|
if (next == monitor->waitTail()) {
|
|
monitor->waitTail() = 0;
|
|
}
|
|
} else {
|
|
assertT(t, monitor->waitTail() == 0);
|
|
}
|
|
|
|
return next;
|
|
}
|
|
|
|
inline bool
|
|
monitorNotify(Thread* t, GcMonitor* monitor)
|
|
{
|
|
expect(t, monitor->owner() == t);
|
|
|
|
Thread* next = monitorPollWait(t, monitor);
|
|
|
|
if (next) {
|
|
ACQUIRE(t, next->lock);
|
|
|
|
next->lock->notify(t->systemThread);
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
monitorNotifyAll(Thread* t, GcMonitor* monitor)
|
|
{
|
|
PROTECT(t, monitor);
|
|
|
|
while (monitorNotify(t, monitor)) { }
|
|
}
|
|
|
|
class ObjectMonitorResource {
|
|
public:
|
|
ObjectMonitorResource(Thread* t, GcMonitor* o): o(o), protector(t, &(this->o)) {
|
|
monitorAcquire(protector.t, o);
|
|
}
|
|
|
|
~ObjectMonitorResource() {
|
|
monitorRelease(protector.t, o);
|
|
}
|
|
|
|
private:
|
|
GcMonitor* o;
|
|
Thread::SingleProtector protector;
|
|
};
|
|
|
|
GcMonitor*
|
|
objectMonitor(Thread* t, object o, bool createNew);
|
|
|
|
inline void
|
|
acquire(Thread* t, object o)
|
|
{
|
|
unsigned hash;
|
|
if (DebugMonitors) {
|
|
hash = objectHash(t, o);
|
|
}
|
|
|
|
GcMonitor* m = objectMonitor(t, o, true);
|
|
|
|
if (DebugMonitors) {
|
|
fprintf(stderr, "thread %p acquires %p for %x\n", t, m, hash);
|
|
}
|
|
|
|
monitorAcquire(t, m);
|
|
}
|
|
|
|
inline void
|
|
release(Thread* t, object o)
|
|
{
|
|
unsigned hash;
|
|
if (DebugMonitors) {
|
|
hash = objectHash(t, o);
|
|
}
|
|
|
|
GcMonitor* m = objectMonitor(t, o, false);
|
|
|
|
if (DebugMonitors) {
|
|
fprintf(stderr, "thread %p releases %p for %x\n", t, m, hash);
|
|
}
|
|
|
|
monitorRelease(t, m);
|
|
}
|
|
|
|
inline void
|
|
wait(Thread* t, object o, int64_t milliseconds)
|
|
{
|
|
unsigned hash;
|
|
if (DebugMonitors) {
|
|
hash = objectHash(t, o);
|
|
}
|
|
|
|
GcMonitor* m = objectMonitor(t, o, false);
|
|
|
|
if (DebugMonitors) {
|
|
fprintf(stderr, "thread %p waits %d millis on %p for %x\n",
|
|
t, static_cast<int>(milliseconds), m, hash);
|
|
}
|
|
|
|
if (m and m->owner() == t) {
|
|
PROTECT(t, m);
|
|
|
|
bool interrupted = monitorWait(t, m, milliseconds);
|
|
|
|
if (interrupted) {
|
|
if (t->m->alive or (t->flags & Thread::DaemonFlag) == 0) {
|
|
t->m->classpath->clearInterrupted(t);
|
|
throwNew(t, GcInterruptedException::Type);
|
|
} else {
|
|
throw_(t, cast<GcThrowable>(t, root(t, Machine::Shutdown)));
|
|
}
|
|
}
|
|
} else {
|
|
throwNew(t, GcIllegalMonitorStateException::Type);
|
|
}
|
|
|
|
if (DebugMonitors) {
|
|
fprintf(stderr, "thread %p wakes up on %p for %x\n",
|
|
t, m, hash);
|
|
}
|
|
|
|
stress(t);
|
|
}
|
|
|
|
inline void
|
|
notify(Thread* t, object o)
|
|
{
|
|
unsigned hash;
|
|
if (DebugMonitors) {
|
|
hash = objectHash(t, o);
|
|
}
|
|
|
|
GcMonitor* m = objectMonitor(t, o, false);
|
|
|
|
if (DebugMonitors) {
|
|
fprintf(stderr, "thread %p notifies on %p for %x\n",
|
|
t, m, hash);
|
|
}
|
|
|
|
if (m and m->owner() == t) {
|
|
monitorNotify(t, m);
|
|
} else {
|
|
throwNew(t, GcIllegalMonitorStateException::Type);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
notifyAll(Thread* t, object o)
|
|
{
|
|
GcMonitor* m = objectMonitor(t, o, false);
|
|
|
|
if (DebugMonitors) {
|
|
fprintf(stderr, "thread %p notifies all on %p for %x\n",
|
|
t, m, objectHash(t, o));
|
|
}
|
|
|
|
if (m and m->owner() == t) {
|
|
monitorNotifyAll(t, m);
|
|
} else {
|
|
throwNew(t, GcIllegalMonitorStateException::Type);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
interrupt(Thread* t, Thread* target)
|
|
{
|
|
if (acquireSystem(t, target)) {
|
|
target->systemThread->interrupt();
|
|
releaseSystem(t, target);
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
getAndClearInterrupted(Thread* t, Thread* target)
|
|
{
|
|
if (acquireSystem(t, target)) {
|
|
bool result = target->systemThread->getAndClearInterrupted();
|
|
releaseSystem(t, target);
|
|
return result;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline bool
|
|
exceptionMatch(Thread* t, GcClass* type, GcThrowable* exception)
|
|
{
|
|
return type == 0
|
|
or (reinterpret_cast<object>(exception) != root(t, Machine::Shutdown)
|
|
and instanceOf(t, type, reinterpret_cast<object>(t->exception)));
|
|
}
|
|
|
|
object
|
|
intern(Thread* t, object s);
|
|
|
|
void
|
|
walk(Thread* t, Heap::Walker* w, object o, unsigned start);
|
|
|
|
int
|
|
walkNext(Thread* t, object o, int previous);
|
|
|
|
void
|
|
visitRoots(Machine* m, Heap::Visitor* v);
|
|
|
|
inline jobject
|
|
makeLocalReference(Thread* t, object o)
|
|
{
|
|
return t->m->processor->makeLocalReference(t, o);
|
|
}
|
|
|
|
inline void
|
|
disposeLocalReference(Thread* t, jobject r)
|
|
{
|
|
t->m->processor->disposeLocalReference(t, r);
|
|
}
|
|
|
|
inline bool
|
|
methodVirtual(Thread* t UNUSED, GcMethod* method)
|
|
{
|
|
return (method->flags() & (ACC_STATIC | ACC_PRIVATE)) == 0
|
|
and method->name()->body()[0] != '<';
|
|
}
|
|
|
|
inline unsigned
|
|
singletonMaskSize(unsigned count, unsigned bitsPerWord)
|
|
{
|
|
if (count) {
|
|
return ceilingDivide(count + 2, bitsPerWord);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
inline unsigned
|
|
singletonMaskSize(unsigned count)
|
|
{
|
|
return singletonMaskSize(count, BitsPerWord);
|
|
}
|
|
|
|
inline unsigned
|
|
singletonMaskSize(Thread* t UNUSED, GcSingleton* singleton)
|
|
{
|
|
unsigned length = singleton->length();
|
|
if (length) {
|
|
return ceilingDivide(length + 2, BitsPerWord + 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
inline unsigned
|
|
singletonCount(Thread* t, GcSingleton* singleton)
|
|
{
|
|
return singleton->length() - singletonMaskSize(t, singleton);
|
|
}
|
|
|
|
inline uint32_t*
|
|
singletonMask(Thread* t UNUSED, GcSingleton* singleton)
|
|
{
|
|
assertT(t, singleton->length());
|
|
return reinterpret_cast<uint32_t*>
|
|
(&singleton->body()[singletonCount(t, singleton)]);
|
|
}
|
|
|
|
inline void
|
|
singletonMarkObject(uint32_t* mask, unsigned index)
|
|
{
|
|
mask[(index + 2) / 32]
|
|
|= (static_cast<uint32_t>(1) << ((index + 2) % 32));
|
|
}
|
|
|
|
inline void
|
|
singletonMarkObject(Thread* t, GcSingleton* singleton, unsigned index)
|
|
{
|
|
singletonMarkObject(singletonMask(t, singleton), index);
|
|
}
|
|
|
|
inline bool
|
|
singletonIsObject(Thread* t, GcSingleton* singleton, unsigned index)
|
|
{
|
|
assertT(t, index < singletonCount(t, singleton));
|
|
|
|
return (singletonMask(t, singleton)[(index + 2) / 32]
|
|
& (static_cast<uint32_t>(1) << ((index + 2) % 32))) != 0;
|
|
}
|
|
|
|
inline object&
|
|
singletonObject(Thread* t UNUSED, GcSingleton* singleton, unsigned index)
|
|
{
|
|
assertT(t, singletonIsObject(t, singleton, index));
|
|
return reinterpret_cast<object&>(singleton->body()[index]);
|
|
}
|
|
|
|
inline uintptr_t&
|
|
singletonValue(Thread* t UNUSED, GcSingleton* singleton, unsigned index)
|
|
{
|
|
assertT(t, not singletonIsObject(t, singleton, index));
|
|
return singleton->body()[index];
|
|
}
|
|
|
|
inline GcSingleton*
|
|
makeSingletonOfSize(Thread* t, unsigned count)
|
|
{
|
|
GcSingleton* o = makeSingleton(t, count + singletonMaskSize(count));
|
|
assertT(t, o->length() == count + singletonMaskSize(t, o));
|
|
if (count) {
|
|
singletonMask(t, o)[0] = 1;
|
|
}
|
|
return o;
|
|
}
|
|
|
|
inline void
|
|
singletonSetBit(Thread* t, GcSingleton* singleton, unsigned start, unsigned index)
|
|
{
|
|
singletonValue(t, singleton, start + (index / BitsPerWord))
|
|
|= static_cast<uintptr_t>(1) << (index % BitsPerWord);
|
|
}
|
|
|
|
inline bool
|
|
singletonBit(Thread* t, GcSingleton* singleton, unsigned start, unsigned index)
|
|
{
|
|
return (singletonValue(t, singleton, start + (index / BitsPerWord))
|
|
& (static_cast<uintptr_t>(1) << (index % BitsPerWord))) != 0;
|
|
}
|
|
|
|
inline unsigned
|
|
poolMaskSize(unsigned count, unsigned bitsPerWord)
|
|
{
|
|
return ceilingDivide(count, bitsPerWord);
|
|
}
|
|
|
|
inline unsigned
|
|
poolMaskSize(unsigned count)
|
|
{
|
|
return poolMaskSize(count, BitsPerWord);
|
|
}
|
|
|
|
inline unsigned
|
|
poolMaskSize(Thread* t, GcSingleton* pool)
|
|
{
|
|
return ceilingDivide(singletonCount(t, pool), BitsPerWord + 1);
|
|
}
|
|
|
|
inline unsigned
|
|
poolSize(Thread* t, GcSingleton* pool)
|
|
{
|
|
return singletonCount(t, pool) - poolMaskSize(t, pool);
|
|
}
|
|
|
|
inline GcClass*
|
|
resolveClassInObject(Thread* t, GcClassLoader* loader, object container,
|
|
unsigned classOffset, bool throw_ = true)
|
|
{
|
|
object o = fieldAtOffset<object>(container, classOffset);
|
|
|
|
loadMemoryBarrier();
|
|
|
|
if (objectClass(t, o) == type(t, GcByteArray::Type)) {
|
|
GcByteArray* name = cast<GcByteArray>(t, o);
|
|
PROTECT(t, container);
|
|
|
|
GcClass* c = resolveClass(t, loader, name, throw_);
|
|
|
|
if (c) {
|
|
storeStoreMemoryBarrier();
|
|
|
|
set(t, container, classOffset, reinterpret_cast<object>(c));
|
|
}
|
|
|
|
return c;
|
|
}
|
|
return cast<GcClass>(t, o);
|
|
}
|
|
|
|
inline GcClass*
|
|
resolveClassInPool(Thread* t, GcClassLoader* loader, GcMethod* method, unsigned index,
|
|
bool throw_ = true)
|
|
{
|
|
object o = singletonObject(t, method->code()->pool(), index);
|
|
|
|
loadMemoryBarrier();
|
|
|
|
if (objectClass(t, o) == type(t, GcReference::Type)) {
|
|
PROTECT(t, method);
|
|
|
|
GcClass* c = resolveClass(t, loader, cast<GcReference>(t, o)->name(), throw_);
|
|
|
|
if (c) {
|
|
storeStoreMemoryBarrier();
|
|
|
|
set(t, method->code()->pool(),
|
|
SingletonBody + (index * BytesPerWord), c);
|
|
}
|
|
return c;
|
|
}
|
|
return cast<GcClass>(t, o);
|
|
}
|
|
|
|
inline GcClass*
|
|
resolveClassInPool(Thread* t, GcMethod* method, unsigned index,
|
|
bool throw_ = true)
|
|
{
|
|
return resolveClassInPool(t, method->class_()->loader(),
|
|
method, index, throw_);
|
|
}
|
|
|
|
inline object
|
|
resolve(Thread* t, GcClassLoader* loader, GcMethod* method, unsigned index,
|
|
object (*find)(vm::Thread*, GcClass*, GcByteArray*, GcByteArray*),
|
|
Gc::Type errorType, bool throw_ = true)
|
|
{
|
|
object o = singletonObject(t, method->code()->pool(), index);
|
|
|
|
loadMemoryBarrier();
|
|
|
|
if (objectClass(t, o) == type(t, GcReference::Type)) {
|
|
PROTECT(t, method);
|
|
|
|
GcReference* reference = cast<GcReference>(t, o);
|
|
PROTECT(t, reference);
|
|
|
|
GcClass* class_ = resolveClassInObject(t, loader, o, ReferenceClass, throw_);
|
|
|
|
if (class_) {
|
|
o = findInHierarchy
|
|
(t, class_, reference->name(), reference->spec(),
|
|
find, errorType, throw_);
|
|
|
|
if (o) {
|
|
storeStoreMemoryBarrier();
|
|
|
|
set(t, reinterpret_cast<object>(method->code()->pool()),
|
|
SingletonBody + (index * BytesPerWord), o);
|
|
}
|
|
} else {
|
|
o = 0;
|
|
}
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
inline GcField*
|
|
resolveField(Thread* t, GcClassLoader* loader, GcMethod* method, unsigned index,
|
|
bool throw_ = true)
|
|
{
|
|
return cast<GcField>(t, resolve(t, loader, method, index, findFieldInClass,
|
|
GcNoSuchFieldError::Type, throw_));
|
|
}
|
|
|
|
inline GcField*
|
|
resolveField(Thread* t, GcMethod* method, unsigned index, bool throw_ = true)
|
|
{
|
|
return resolveField
|
|
(t, method->class_()->loader(), method, index, throw_);
|
|
}
|
|
|
|
inline void
|
|
acquireFieldForRead(Thread* t, GcField* field)
|
|
{
|
|
if (UNLIKELY((field->flags() & ACC_VOLATILE)
|
|
and BytesPerWord == 4
|
|
and (field->code() == DoubleField
|
|
or field->code() == LongField)))
|
|
{
|
|
acquire(t, reinterpret_cast<object>(field));
|
|
}
|
|
}
|
|
|
|
inline void
|
|
releaseFieldForRead(Thread* t, GcField* field)
|
|
{
|
|
if (UNLIKELY(field->flags() & ACC_VOLATILE)) {
|
|
if (BytesPerWord == 4
|
|
and (field->code() == DoubleField
|
|
or field->code() == LongField))
|
|
{
|
|
release(t, reinterpret_cast<object>(field));
|
|
} else {
|
|
loadMemoryBarrier();
|
|
}
|
|
}
|
|
}
|
|
|
|
class FieldReadResource {
|
|
public:
|
|
FieldReadResource(Thread* t, GcField* o): o(o), protector(t, &(this->o)) {
|
|
acquireFieldForRead(protector.t, o);
|
|
}
|
|
|
|
~FieldReadResource() {
|
|
releaseFieldForRead(protector.t, o);
|
|
}
|
|
|
|
private:
|
|
GcField* o;
|
|
Thread::SingleProtector protector;
|
|
};
|
|
|
|
inline void
|
|
acquireFieldForWrite(Thread* t, GcField* field)
|
|
{
|
|
if (UNLIKELY(field->flags() & ACC_VOLATILE)) {
|
|
if (BytesPerWord == 4
|
|
and (field->code() == DoubleField
|
|
or field->code() == LongField))
|
|
{
|
|
acquire(t, reinterpret_cast<object>(field));
|
|
} else {
|
|
storeStoreMemoryBarrier();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void
|
|
releaseFieldForWrite(Thread* t, GcField* field)
|
|
{
|
|
if (UNLIKELY(field->flags() & ACC_VOLATILE)) {
|
|
if (BytesPerWord == 4
|
|
and (field->code() == DoubleField
|
|
or field->code() == LongField))
|
|
{
|
|
release(t, reinterpret_cast<object>(field));
|
|
} else {
|
|
storeLoadMemoryBarrier();
|
|
}
|
|
}
|
|
}
|
|
|
|
class FieldWriteResource {
|
|
public:
|
|
FieldWriteResource(Thread* t, GcField* o): o(o), protector(t, &(this->o)) {
|
|
acquireFieldForWrite(protector.t, o);
|
|
}
|
|
|
|
~FieldWriteResource() {
|
|
releaseFieldForWrite(protector.t, o);
|
|
}
|
|
|
|
private:
|
|
GcField* o;
|
|
Thread::SingleProtector protector;
|
|
};
|
|
|
|
inline GcMethod*
|
|
resolveMethod(Thread* t, GcClassLoader* loader, GcMethod* method, unsigned index,
|
|
bool throw_ = true)
|
|
{
|
|
return cast<GcMethod>(t, resolve(t, loader, method, index, findMethodInClass,
|
|
GcNoSuchMethodError::Type, throw_));
|
|
}
|
|
|
|
inline GcMethod*
|
|
resolveMethod(Thread* t, GcMethod* method, unsigned index, bool throw_ = true)
|
|
{
|
|
return resolveMethod
|
|
(t, method->class_()->loader(), method, index, throw_);
|
|
}
|
|
|
|
GcVector*
|
|
vectorAppend(Thread*, GcVector*, object);
|
|
|
|
inline object
|
|
getClassRuntimeDataIfExists(Thread* t, GcClass* c)
|
|
{
|
|
if (c->runtimeDataIndex()) {
|
|
return cast<GcVector>(t, root(t, Machine::ClassRuntimeDataTable))->body()[c->runtimeDataIndex() - 1];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
inline GcClassRuntimeData*
|
|
getClassRuntimeData(Thread* t, GcClass* c)
|
|
{
|
|
if (c->runtimeDataIndex() == 0) {
|
|
PROTECT(t, c);
|
|
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
if (c->runtimeDataIndex() == 0) {
|
|
object runtimeData = reinterpret_cast<object>(makeClassRuntimeData(t, 0, 0, 0, 0));
|
|
|
|
setRoot(t, Machine::ClassRuntimeDataTable, reinterpret_cast<object>(vectorAppend
|
|
(t, cast<GcVector>(t, root(t, Machine::ClassRuntimeDataTable)), runtimeData)));
|
|
|
|
c->runtimeDataIndex() = cast<GcVector>(t, root(t, Machine::ClassRuntimeDataTable))->size();
|
|
}
|
|
}
|
|
|
|
return cast<GcClassRuntimeData>(
|
|
t,
|
|
cast<GcVector>(t, root(t, Machine::ClassRuntimeDataTable))
|
|
->body()[c->runtimeDataIndex() - 1]);
|
|
}
|
|
|
|
inline GcMethodRuntimeData*
|
|
getMethodRuntimeData(Thread* t, GcMethod* method)
|
|
{
|
|
int index = method->runtimeDataIndex();
|
|
|
|
loadMemoryBarrier();
|
|
|
|
if (index == 0) {
|
|
PROTECT(t, method);
|
|
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
if (method->runtimeDataIndex() == 0) {
|
|
object runtimeData = reinterpret_cast<object>(makeMethodRuntimeData(t, 0));
|
|
|
|
setRoot(t, Machine::MethodRuntimeDataTable, reinterpret_cast<object>(vectorAppend
|
|
(t, cast<GcVector>(t, root(t, Machine::MethodRuntimeDataTable)), runtimeData)));
|
|
|
|
storeStoreMemoryBarrier();
|
|
|
|
method->runtimeDataIndex() = cast<GcVector>(t, root(t, Machine::MethodRuntimeDataTable))->size();
|
|
}
|
|
}
|
|
|
|
return cast<GcMethodRuntimeData>(
|
|
t,
|
|
cast<GcVector>(t, root(t, Machine::MethodRuntimeDataTable))
|
|
->body()[method->runtimeDataIndex() - 1]);
|
|
}
|
|
|
|
inline GcJclass*
|
|
getJClass(Thread* t, GcClass* c)
|
|
{
|
|
PROTECT(t, c);
|
|
|
|
GcJclass* jclass = cast<GcJclass>(t, getClassRuntimeData(t, c)->jclass());
|
|
|
|
loadMemoryBarrier();
|
|
|
|
if (jclass == 0) {
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
jclass = cast<GcJclass>(t, getClassRuntimeData(t, c)->jclass());
|
|
if (jclass == 0) {
|
|
jclass = cast<GcJclass>(t, t->m->classpath->makeJclass(t, c));
|
|
|
|
storeStoreMemoryBarrier();
|
|
|
|
set(t, getClassRuntimeData(t, c), ClassRuntimeDataJclass, jclass);
|
|
}
|
|
}
|
|
|
|
return jclass;
|
|
}
|
|
|
|
inline GcClass*
|
|
primitiveClass(Thread* t, char name)
|
|
{
|
|
switch (name) {
|
|
case 'B': return type(t, GcJbyte::Type);
|
|
case 'C': return type(t, GcJchar::Type);
|
|
case 'D': return type(t, GcJdouble::Type);
|
|
case 'F': return type(t, GcJfloat::Type);
|
|
case 'I': return type(t, GcJint::Type);
|
|
case 'J': return type(t, GcJlong::Type);
|
|
case 'S': return type(t, GcJshort::Type);
|
|
case 'V': return type(t, GcJvoid::Type);
|
|
case 'Z': return type(t, GcJboolean::Type);
|
|
default: throwNew(t, GcIllegalArgumentException::Type);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
registerNative(Thread* t, GcMethod* method, void* function)
|
|
{
|
|
PROTECT(t, method);
|
|
|
|
expect(t, method->flags() & ACC_NATIVE);
|
|
|
|
GcNative* native = makeNative(t, function, false);
|
|
PROTECT(t, native);
|
|
|
|
GcMethodRuntimeData* runtimeData = getMethodRuntimeData(t, method);
|
|
|
|
// ensure other threads only see the methodRuntimeDataNative field
|
|
// populated once the object it points to has been populated:
|
|
storeStoreMemoryBarrier();
|
|
|
|
set(t, runtimeData, MethodRuntimeDataNative, native);
|
|
}
|
|
|
|
inline void
|
|
unregisterNatives(Thread* t, GcClass* c)
|
|
{
|
|
GcArray* table = cast<GcArray>(t, c->methodTable());
|
|
if (table) {
|
|
for (unsigned i = 0; i < table->length(); ++i) {
|
|
GcMethod* method = cast<GcMethod>(t, table->body()[i]);
|
|
if (method->flags() & ACC_NATIVE) {
|
|
set(t, getMethodRuntimeData(t, method), MethodRuntimeDataNative, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
populateMultiArray(Thread* t, object array, int32_t* counts,
|
|
unsigned index, unsigned dimensions);
|
|
|
|
GcMethod*
|
|
getCaller(Thread* t, unsigned target, bool skipMethodInvoke = false);
|
|
|
|
object
|
|
defineClass(Thread* t, GcClassLoader* loader, const uint8_t* buffer, unsigned length);
|
|
|
|
inline GcMethod*
|
|
methodClone(Thread* t, GcMethod* method)
|
|
{
|
|
return makeMethod
|
|
(t, method->vmFlags(),
|
|
method->returnCode(),
|
|
method->parameterCount(),
|
|
method->parameterFootprint(),
|
|
method->flags(),
|
|
method->offset(),
|
|
method->nativeID(),
|
|
method->runtimeDataIndex(),
|
|
method->name(),
|
|
method->spec(),
|
|
method->addendum(),
|
|
method->class_(),
|
|
method->code());
|
|
}
|
|
|
|
inline uint64_t
|
|
exceptionHandler(uint64_t start, uint64_t end, uint64_t ip, uint64_t catchType)
|
|
{
|
|
return (start << 48) | (end << 32) | (ip << 16) | catchType;
|
|
}
|
|
|
|
inline unsigned
|
|
exceptionHandlerStart(uint64_t eh)
|
|
{
|
|
return eh >> 48;
|
|
}
|
|
|
|
inline unsigned
|
|
exceptionHandlerEnd(uint64_t eh)
|
|
{
|
|
return (eh >> 32) & 0xFFFF;
|
|
}
|
|
|
|
inline unsigned
|
|
exceptionHandlerIp(uint64_t eh)
|
|
{
|
|
return (eh >> 16) & 0xFFFF;
|
|
}
|
|
|
|
inline unsigned
|
|
exceptionHandlerCatchType(uint64_t eh)
|
|
{
|
|
return eh & 0xFFFF;
|
|
}
|
|
|
|
inline uint64_t
|
|
lineNumber(uint64_t ip, uint64_t line)
|
|
{
|
|
return (ip << 32) | line;
|
|
}
|
|
|
|
inline unsigned
|
|
lineNumberIp(uint64_t ln)
|
|
{
|
|
return ln >> 32;
|
|
}
|
|
|
|
inline unsigned
|
|
lineNumberLine(uint64_t ln)
|
|
{
|
|
return ln & 0xFFFFFFFF;
|
|
}
|
|
|
|
object
|
|
interruptLock(Thread* t, GcThread* thread);
|
|
|
|
void
|
|
clearInterrupted(Thread* t);
|
|
|
|
void
|
|
threadInterrupt(Thread* t, GcThread* thread);
|
|
|
|
bool
|
|
threadIsInterrupted(Thread* t, GcThread* thread, bool clear);
|
|
|
|
inline FILE*
|
|
errorLog(Thread* t)
|
|
{
|
|
if (t->m->errorLog == 0) {
|
|
const char* path = findProperty(t, "avian.error.log");
|
|
if (path) {
|
|
t->m->errorLog = vm::fopen(path, "wb");
|
|
} else {
|
|
t->m->errorLog = stderr;
|
|
}
|
|
}
|
|
|
|
return t->m->errorLog;
|
|
}
|
|
|
|
} // namespace vm
|
|
|
|
AVIAN_EXPORT void*
|
|
vmAddressFromLine(vm::Thread* t, vm::object m, unsigned line);
|
|
|
|
#endif//MACHINE_H
|