blob: 89887b0fa6f2dfbf2da769d7326b63b8f8763bac [file] [log] [blame]
//===---------- Jnjvm.cpp - Java virtual machine description --------------===//
//
// The VMKit project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#define JNJVM_LOAD 0
#include <cfloat>
#include <climits>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include "debug.h"
#include "mvm/Threads/Thread.h"
#include "MvmGC.h"
#include "ClasspathReflect.h"
#include "JavaArray.h"
#include "JavaClass.h"
#include "JavaCompiler.h"
#include "JavaConstantPool.h"
#include "JavaString.h"
#include "JavaThread.h"
#include "JavaTypes.h"
#include "JavaUpcalls.h"
#include "Jnjvm.h"
#include "LinkJavaRuntime.h"
#include "LockedMap.h"
#include "Reader.h"
#include "Zip.h"
using namespace j3;
const char* Jnjvm::dirSeparator = "/";
const char* Jnjvm::envSeparator = ":";
const unsigned int Jnjvm::Magic = 0xcafebabe;
#ifdef ISOLATE
Jnjvm* Jnjvm::RunningIsolates[NR_ISOLATES];
mvm::LockNormal Jnjvm::IsolateLock;
#endif
/// initialiseClass - Java class initialisation. Java specification §2.17.5.
void UserClass::initialiseClass(Jnjvm* vm) {
// Primitives are initialized at boot time, arrays are initialized directly.
// Assumes that the Class object has already been verified and prepared and
// that the Class object contains state that can indicate one of four
// situations:
//
// * This Class object is verified and prepared but not initialized.
// * This Class object is being initialized by some particular thread T.
// * This Class object is fully initialized and ready for use.
// * This Class object is in an erroneous state, perhaps because the
// verification step failed or because initialization was attempted and
// failed.
assert((isResolved() || getOwnerClass() || isReady() ||
isErroneous()) && "Class in wrong state");
if (getInitializationState() != ready) {
// 1. Synchronize on the Class object that represents the class or
// interface to be initialized. This involves waiting until the
// current thread can obtain the lock for that object
// (Java specification §8.13).
acquire();
JavaThread* self = JavaThread::get();
if (getInitializationState() == inClinit) {
// 2. If initialization by some other thread is in progress for the
// class or interface, then wait on this Class object (which
// temporarily releases the lock). When the current thread awakens
// from the wait, repeat this step.
if (getOwnerClass() != self) {
while (getOwnerClass()) {
waitClass();
}
} else {
// 3. If initialization is in progress for the class or interface by
// the current thread, then this must be a recursive request for
// initialization. Release the lock on the Class object and complete
// normally.
release();
return;
}
}
// 4. If the class or interface has already been initialized, then no
// further action is required. Release the lock on the Class object
// and complete normally.
if (getInitializationState() == ready) {
release();
return;
}
// 5. If the Class object is in an erroneous state, then initialization is
// not possible. Release the lock on the Class object and throw a
// NoClassDefFoundError.
if (isErroneous()) {
release();
vm->noClassDefFoundError(name);
}
// 6. Otherwise, record the fact that initialization of the Class object is
// now in progress by the current thread and release the lock on the
// Class object.
setOwnerClass(self);
bool vmjced = (getInitializationState() == vmjc);
setInitializationState(inClinit);
UserClass* cl = (UserClass*)this;
#if defined(ISOLATE) || defined(ISOLATE_SHARING)
// Isolate environments allocate the static instance on their own, not when
// the class is being resolved.
void* val = cl->allocateStaticInstance(vm);
#else
// Single environment allocates the static instance during resolution, so
// that compiled code can access it directly (with an initialization
// check just before the access)
void* val = cl->getStaticInstance();
if (!val) {
val = cl->allocateStaticInstance(vm);
}
#endif
release();
// 7. Next, if the Class object represents a class rather than an interface,
// and the direct superclass of this class has not yet been initialized,
// then recursively perform this entire procedure for the uninitialized
// superclass. If the initialization of the direct superclass completes
// abruptly because of a thrown exception, then lock this Class object,
// label it erroneous, notify all waiting threads, release the lock,
// and complete abruptly, throwing the same exception that resulted from
// the initializing the superclass.
UserClass* super = getSuper();
if (super) {
try {
super->initialiseClass(vm);
} catch(...) {
acquire();
setErroneous();
setOwnerClass(0);
broadcastClass();
release();
self->throwPendingException();
}
}
JavaObject* exc = 0;
JavaObject* obj = 0;
llvm_gcroot(exc, 0);
llvm_gcroot(obj, 0);
#ifdef SERVICE
if (classLoader == classLoader->bootstrapLoader ||
classLoader->getIsolate() == vm) {
#endif
// 8. Next, execute either the class variable initializers and static
// initializers of the class or the field initializers of the interface,
// in textual order, as though they were a single block, except that
// final static variables and fields of interfaces whose values are
// compile-time constants are initialized first.
PRINT_DEBUG(JNJVM_LOAD, 0, COLOR_NORMAL, "; ", 0);
PRINT_DEBUG(JNJVM_LOAD, 0, LIGHT_GREEN, "clinit ", 0);
PRINT_DEBUG(JNJVM_LOAD, 0, COLOR_NORMAL, "%s\n", mvm::PrintString(this).cString());
if (!vmjced) {
JavaField* fields = cl->getStaticFields();
for (uint32 i = 0; i < cl->nbStaticFields; ++i) {
fields[i].initField(val, vm);
}
}
JavaMethod* meth = lookupMethodDontThrow(vm->bootstrapLoader->clinitName,
vm->bootstrapLoader->clinitType,
true, false, 0);
if (meth) {
try{
meth->invokeIntStatic(vm, cl);
} catch(...) {
exc = self->getJavaException();
assert(exc && "no exception?");
self->clearException();
}
}
#ifdef SERVICE
}
#endif
// 9. If the execution of the initializers completes normally, then lock
// this Class object, label it fully initialized, notify all waiting
// threads, release the lock, and complete this procedure normally.
if (!exc) {
acquire();
setInitializationState(ready);
setOwnerClass(0);
broadcastClass();
release();
return;
}
// 10. Otherwise, the initializers must have completed abruptly by
// throwing some exception E. If the class of E is not Error or one
// of its subclasses, then create a new instance of the class
// ExceptionInInitializerError, with E as the argument, and use this
// object in place of E in the following step. But if a new instance of
// ExceptionInInitializerError cannot be created because an
// OutOfMemoryError occurs, then instead use an OutOfMemoryError object
// in place of E in the following step.
if (exc->getClass()->isAssignableFrom(vm->upcalls->newException)) {
Classpath* upcalls = classLoader->bootstrapLoader->upcalls;
UserClass* clExcp = upcalls->ExceptionInInitializerError;
Jnjvm* vm = self->getJVM();
obj = clExcp->doNew(vm);
if (!obj) {
fprintf(stderr, "implement me");
abort();
}
JavaMethod* init = upcalls->ErrorWithExcpExceptionInInitializerError;
init->invokeIntSpecial(vm, clExcp, obj, &exc);
exc = obj;
}
// 11. Lock the Class object, label it erroneous, notify all waiting
// threads, release the lock, and complete this procedure abruptly
// with reason E or its replacement as determined in the previous step.
acquire();
setErroneous();
setOwnerClass(0);
broadcastClass();
release();
self->throwException(exc);
}
}
void Jnjvm::errorWithExcp(UserClass* cl, JavaMethod* init,
const JavaObject* excp) {
JavaObject* obj = cl->doNew(this);
llvm_gcroot(obj, 0);
init->invokeIntSpecial(this, cl, obj, &excp);
JavaThread::get()->throwException(obj);
}
JavaObject* Jnjvm::CreateError(UserClass* cl, JavaMethod* init,
const char* asciiz) {
JavaObject* obj = cl->doNew(this);
JavaString* str = 0;
llvm_gcroot(obj, 0);
llvm_gcroot(str, 0);
if (asciiz) str = asciizToStr(asciiz);
init->invokeIntSpecial(this, cl, obj, &str);
return obj;
}
JavaObject* Jnjvm::CreateError(UserClass* cl, JavaMethod* init,
JavaString* str) {
JavaObject* obj = 0;
llvm_gcroot(str, 0);
llvm_gcroot(obj, 0);
obj = cl->doNew(this);
init->invokeIntSpecial(this, cl, obj, &str);
return obj;
}
void Jnjvm::error(UserClass* cl, JavaMethod* init, JavaString* str) {
JavaObject* obj = 0;
llvm_gcroot(obj, 0);
llvm_gcroot(str, 0);
obj = CreateError(cl, init, str);
JavaThread::get()->throwException(obj);
}
void Jnjvm::arrayStoreException() {
error(upcalls->ArrayStoreException,
upcalls->InitArrayStoreException, (JavaString*)0);
}
void Jnjvm::indexOutOfBounds(const JavaObject* obj, sint32 entry) {
JavaString* str = (JavaString*)
upcalls->IntToString->invokeJavaObjectStatic(this, upcalls->intClass,
entry, 10);
llvm_gcroot(str, 0);
error(upcalls->ArrayIndexOutOfBoundsException,
upcalls->InitArrayIndexOutOfBoundsException, str);
}
void Jnjvm::negativeArraySizeException(sint32 size) {
JavaString* str = (JavaString*)
upcalls->IntToString->invokeJavaObjectStatic(this, upcalls->intClass,
size, 10);
llvm_gcroot(str, 0);
error(upcalls->NegativeArraySizeException,
upcalls->InitNegativeArraySizeException, str);
}
void Jnjvm::nullPointerException() {
error(upcalls->NullPointerException,
upcalls->InitNullPointerException, (JavaString*)0);
}
JavaObject* Jnjvm::CreateIndexOutOfBoundsException(sint32 entry) {
JavaString* str = (JavaString*)
upcalls->IntToString->invokeJavaObjectStatic(this, upcalls->intClass,
entry, 10);
llvm_gcroot(str, 0);
return CreateError(upcalls->ArrayIndexOutOfBoundsException,
upcalls->InitArrayIndexOutOfBoundsException, str);
}
JavaObject* Jnjvm::CreateNegativeArraySizeException() {
return CreateError(upcalls->NegativeArraySizeException,
upcalls->InitNegativeArraySizeException,
(JavaString*)0);
}
JavaObject* Jnjvm::CreateUnsatisfiedLinkError(JavaMethod* meth) {
JavaString* str = constructString(meth->toString());
llvm_gcroot(str, 0);
return CreateError(upcalls->UnsatisfiedLinkError,
upcalls->InitUnsatisfiedLinkError,
str);
}
JavaObject* Jnjvm::CreateArithmeticException() {
JavaString* str = asciizToStr("/ by zero");
llvm_gcroot(str, 0);
return CreateError(upcalls->ArithmeticException,
upcalls->InitArithmeticException, str);
}
JavaObject* Jnjvm::CreateNullPointerException() {
return CreateError(upcalls->NullPointerException,
upcalls->InitNullPointerException,
(JavaString*)0);
}
JavaObject* Jnjvm::CreateOutOfMemoryError() {
JavaString* str = asciizToStr("Java heap space");
llvm_gcroot(str, 0);
return CreateError(upcalls->OutOfMemoryError,
upcalls->InitOutOfMemoryError, str);
}
JavaObject* Jnjvm::CreateStackOverflowError() {
// Don't call init, or else we'll get a new stack overflow error.
JavaObject* obj = upcalls->StackOverflowError->doNew(this);
llvm_gcroot(obj, 0);
((JavaObjectThrowable*)obj)->fillInStackTrace();
return obj;
}
JavaObject* Jnjvm::CreateArrayStoreException(JavaVirtualTable* VT) {
JavaString* str = VT ? JavaString::internalToJava(VT->cl->name, this) : NULL;
llvm_gcroot(str, 0);
return CreateError(upcalls->ArrayStoreException,
upcalls->InitArrayStoreException, str);
}
JavaObject* Jnjvm::CreateClassCastException(JavaObject* obj,
UserCommonClass* cl) {
llvm_gcroot(obj, 0);
return CreateError(upcalls->ClassCastException,
upcalls->InitClassCastException,
(JavaString*)0);
}
JavaObject* Jnjvm::CreateLinkageError(const char* msg) {
JavaString* str = asciizToStr(msg);
llvm_gcroot(str, 0);
return CreateError(upcalls->LinkageError,
upcalls->InitLinkageError, str);
}
void Jnjvm::illegalAccessException(const char* msg) {
JavaString* str = asciizToStr(msg);
llvm_gcroot(str, 0);
error(upcalls->IllegalAccessException,
upcalls->InitIllegalAccessException, str);
}
void Jnjvm::illegalMonitorStateException(const JavaObject* obj) {
llvm_gcroot(obj, 0);
error(upcalls->IllegalMonitorStateException,
upcalls->InitIllegalMonitorStateException,
(JavaString*)0);
}
void Jnjvm::interruptedException(const JavaObject* obj) {
llvm_gcroot(obj, 0);
error(upcalls->InterruptedException,
upcalls->InitInterruptedException,
(JavaString*)0);
}
void Jnjvm::initializerError(const JavaObject* excp) {
llvm_gcroot(excp, 0);
errorWithExcp(upcalls->ExceptionInInitializerError,
upcalls->ErrorWithExcpExceptionInInitializerError,
excp);
}
void Jnjvm::invocationTargetException(const JavaObject* excp) {
llvm_gcroot(excp, 0);
errorWithExcp(upcalls->InvocationTargetException,
upcalls->ErrorWithExcpInvocationTargetException,
excp);
}
void Jnjvm::outOfMemoryError() {
JavaString* str = asciizToStr("Java heap space");
llvm_gcroot(str, 0);
error(upcalls->OutOfMemoryError,
upcalls->InitOutOfMemoryError, str);
}
void Jnjvm::illegalArgumentException(const char* msg) {
JavaString* str = asciizToStr(msg);
llvm_gcroot(str, 0);
error(upcalls->IllegalArgumentException,
upcalls->InitIllegalArgumentException, str);
}
void Jnjvm::classCastException(JavaObject* obj, UserCommonClass* cl) {
llvm_gcroot(obj, 0);
error(upcalls->ClassCastException,
upcalls->InitClassCastException,
(JavaString*)0);
}
void Jnjvm::noClassDefFoundError(JavaObject* obj) {
llvm_gcroot(obj, 0);
errorWithExcp(upcalls->NoClassDefFoundError,
upcalls->ErrorWithExcpNoClassDefFoundError,
obj);
}
void Jnjvm::instantiationException(UserCommonClass* cl) {
JavaString* str = internalUTF8ToStr(cl->name);
llvm_gcroot(str, 0);
error(upcalls->InstantiationException, upcalls->InitInstantiationException,
str);
}
void Jnjvm::instantiationError(UserCommonClass* cl) {
JavaString* str = internalUTF8ToStr(cl->name);
llvm_gcroot(str, 0);
error(upcalls->InstantiationError, upcalls->InitInstantiationError, str);
}
static JavaString* CreateNoSuchMsg(CommonClass* cl, const UTF8* name,
Jnjvm* vm) {
ArrayUInt16* msg = (ArrayUInt16*)
vm->upcalls->ArrayOfChar->doNew(19 + cl->name->size + name->size, vm);
JavaString* str = 0;
llvm_gcroot(msg, 0);
llvm_gcroot(str, 0);
uint32 i = 0;
msg->elements[i++] = 'u';
msg->elements[i++] = 'n';
msg->elements[i++] = 'a';
msg->elements[i++] = 'b';
msg->elements[i++] = 'l';
msg->elements[i++] = 'e';
msg->elements[i++] = ' ';
msg->elements[i++] = 't';
msg->elements[i++] = 'o';
msg->elements[i++] = ' ';
msg->elements[i++] = 'f';
msg->elements[i++] = 'i';
msg->elements[i++] = 'n';
msg->elements[i++] = 'd';
msg->elements[i++] = ' ';
for (sint32 j = 0; j < name->size; ++j)
msg->elements[i++] = name->elements[j];
msg->elements[i++] = ' ';
msg->elements[i++] = 'i';
msg->elements[i++] = 'n';
msg->elements[i++] = ' ';
for (sint32 j = 0; j < cl->name->size; ++j) {
if (cl->name->elements[j] == '/') msg->elements[i++] = '.';
else msg->elements[i++] = cl->name->elements[j];
}
str = vm->constructString(msg);
return str;
}
void Jnjvm::noSuchFieldError(CommonClass* cl, const UTF8* name) {
JavaString* str = CreateNoSuchMsg(cl, name, this);
llvm_gcroot(str, 0);
error(upcalls->NoSuchFieldError,
upcalls->InitNoSuchFieldError, str);
}
void Jnjvm::noSuchMethodError(CommonClass* cl, const UTF8* name) {
JavaString* str = CreateNoSuchMsg(cl, name, this);
llvm_gcroot(str, 0);
error(upcalls->NoSuchMethodError,
upcalls->InitNoSuchMethodError, str);
}
void Jnjvm::abstractMethodError(CommonClass* cl, const UTF8* name) {
JavaString* str = CreateNoSuchMsg(cl, name, this);
llvm_gcroot(str, 0);
error(upcalls->AbstractMethodError,
upcalls->InitAbstractMethodError, str);
}
static JavaString* CreateUnableToLoad(const UTF8* name, Jnjvm* vm) {
ArrayUInt16* msg = (ArrayUInt16*)
vm->upcalls->ArrayOfChar->doNew(15 + name->size, vm);
JavaString* str = 0;
llvm_gcroot(msg, 0);
llvm_gcroot(str, 0);
uint32 i = 0;
msg->elements[i++] = 'u';
msg->elements[i++] = 'n';
msg->elements[i++] = 'a';
msg->elements[i++] = 'b';
msg->elements[i++] = 'l';
msg->elements[i++] = 'e';
msg->elements[i++] = ' ';
msg->elements[i++] = 't';
msg->elements[i++] = 'o';
msg->elements[i++] = ' ';
msg->elements[i++] = 'l';
msg->elements[i++] = 'o';
msg->elements[i++] = 'a';
msg->elements[i++] = 'd';
msg->elements[i++] = ' ';
for (sint32 j = 0; j < name->size; ++j) {
if (name->elements[j] == '/') msg->elements[i++] = '.';
else msg->elements[i++] = name->elements[j];
}
str = vm->constructString(msg);
return str;
}
static JavaString* CreateUnableToLoad(JavaString* name, Jnjvm* vm) {
JavaString* str = 0;
ArrayUInt16* msg = (ArrayUInt16*)
vm->upcalls->ArrayOfChar->doNew(15 + name->count, vm);
llvm_gcroot(msg, 0);
llvm_gcroot(str, 0);
uint32 i = 0;
msg->elements[i++] = 'u';
msg->elements[i++] = 'n';
msg->elements[i++] = 'a';
msg->elements[i++] = 'b';
msg->elements[i++] = 'l';
msg->elements[i++] = 'e';
msg->elements[i++] = ' ';
msg->elements[i++] = 't';
msg->elements[i++] = 'o';
msg->elements[i++] = ' ';
msg->elements[i++] = 'l';
msg->elements[i++] = 'o';
msg->elements[i++] = 'a';
msg->elements[i++] = 'd';
msg->elements[i++] = ' ';
for (sint32 j = name->offset; j < name->offset + name->count; ++j) {
if (name->value->elements[j] == '/') msg->elements[i++] = '.';
else msg->elements[i++] = name->value->elements[j];
}
str = vm->constructString(msg);
return str;
}
void Jnjvm::noClassDefFoundError(const UTF8* name) {
JavaString* str = CreateUnableToLoad(name, this);
llvm_gcroot(str, 0);
error(upcalls->NoClassDefFoundError,
upcalls->InitNoClassDefFoundError, str);
}
void Jnjvm::classNotFoundException(JavaString* name) {
JavaString* str = CreateUnableToLoad(name, this);
llvm_gcroot(str, 0);
error(upcalls->ClassNotFoundException,
upcalls->InitClassNotFoundException, str);
}
void Jnjvm::classFormatError(UserClass* cl, const UTF8* name) {
uint32 size = 35 + name->size + cl->name->size;
ArrayUInt16* msg = (ArrayUInt16*)upcalls->ArrayOfChar->doNew(size, this);
JavaString* str = 0;
llvm_gcroot(msg, 0);
llvm_gcroot(str, 0);
uint32 i = 0;
msg->elements[i++] = 't';
msg->elements[i++] = 'r';
msg->elements[i++] = 'y';
msg->elements[i++] = ' ';
msg->elements[i++] = 't';
msg->elements[i++] = 'o';
msg->elements[i++] = ' ';
msg->elements[i++] = 'l';
msg->elements[i++] = 'o';
msg->elements[i++] = 'a';
msg->elements[i++] = 'd';
msg->elements[i++] = ' ';
for (sint32 j = 0; j < cl->name->size; ++j) {
if (cl->name->elements[j] == '/') msg->elements[i++] = '.';
else msg->elements[i++] = cl->name->elements[j];
}
msg->elements[i++] = ' ';
msg->elements[i++] = 'a';
msg->elements[i++] = 'n';
msg->elements[i++] = 'd';
msg->elements[i++] = ' ';
msg->elements[i++] = 'f';
msg->elements[i++] = 'o';
msg->elements[i++] = 'u';
msg->elements[i++] = 'n';
msg->elements[i++] = 'd';
msg->elements[i++] = ' ';
msg->elements[i++] = 'c';
msg->elements[i++] = 'l';
msg->elements[i++] = 'a';
msg->elements[i++] = 's';
msg->elements[i++] = 's';
msg->elements[i++] = ' ';
msg->elements[i++] = 'n';
msg->elements[i++] = 'a';
msg->elements[i++] = 'm';
msg->elements[i++] = 'e';
msg->elements[i++] = 'd';
msg->elements[i++] = ' ';
for (sint32 j = 0; j < name->size; ++j) {
if (name->elements[j] == '/') msg->elements[i++] = '.';
else msg->elements[i++] = name->elements[j];
}
assert(i == size && "Array overflow");
str = constructString(msg);
error(upcalls->ClassFormatError, upcalls->InitClassFormatError, str);
}
void Jnjvm::classFormatError(const char* msg) {
JavaString* str = asciizToStr(msg);
llvm_gcroot(str, 0);
error(upcalls->ClassFormatError, upcalls->InitClassFormatError, str);
}
JavaString* Jnjvm::internalUTF8ToStr(const UTF8* utf8) {
uint32 size = utf8->size;
ArrayUInt16* tmp = (ArrayUInt16*)upcalls->ArrayOfChar->doNew(size, this);
llvm_gcroot(tmp, 0);
uint16* buf = tmp->elements;
for (uint32 i = 0; i < size; i++) {
buf[i] = utf8->elements[i];
}
return hashStr.lookupOrCreate((const ArrayUInt16*&)tmp, this,
JavaString::stringDup);
}
JavaString* Jnjvm::constructString(const ArrayUInt16* array) {
llvm_gcroot(array, 0);
JavaString* res = hashStr.lookupOrCreate(array, this, JavaString::stringDup);
return res;
}
JavaString* Jnjvm::asciizToStr(const char* asciiz) {
assert(asciiz && "No asciiz given");
ArrayUInt16* var = 0;
llvm_gcroot(var, 0);
var = asciizToArray(asciiz);
return constructString(var);
}
void Jnjvm::addProperty(char* key, char* value) {
postProperties.push_back(std::make_pair(key, value));
}
// Mimic what's happening in Classpath when creating a java.lang.Class object.
JavaObject* UserCommonClass::getClassDelegatee(Jnjvm* vm, JavaObject* pd) {
JavaObjectClass* delegatee = 0;
JavaObjectClass* base = 0;
llvm_gcroot(pd, 0);
llvm_gcroot(delegatee, 0);
llvm_gcroot(base, 0);
if (!getDelegatee()) {
UserClass* cl = vm->upcalls->newClass;
delegatee = (JavaObjectClass*)cl->doNew(vm);
delegatee->vmdata = this;
if (!pd && isArray()) {
base = (JavaObjectClass*)
asArrayClass()->baseClass()->getClassDelegatee(vm, pd);
delegatee->pd = base->pd;
} else {
delegatee->pd = pd;
}
setDelegatee(delegatee);
}
return getDelegatee();
}
JavaObject* const* UserCommonClass::getClassDelegateePtr(Jnjvm* vm, JavaObject* pd) {
llvm_gcroot(pd, 0);
// Make sure it's created.
getClassDelegatee(vm, pd);
return getDelegateePtr();
}
//===----------------------------------------------------------------------===//
// The command line parsing tool does not manipulate any GC-allocated objects.
// The ArrayUInt8 is allocated with malloc and free'd after parsing.
//===----------------------------------------------------------------------===//
#define PATH_MANIFEST "META-INF/MANIFEST.MF"
#define MAIN_CLASS "Main-Class: "
#define MAIN_LOWER_CLASS "Main-class: "
#define PREMAIN_CLASS "Premain-Class: "
#define BOOT_CLASS_PATH "Boot-Class-Path: "
#define CAN_REDEFINE_CLASS_PATH "Can-Redefine-Classes: "
#define LENGTH_MAIN_CLASS 12
#define LENGTH_PREMAIN_CLASS 15
#define LENGTH_BOOT_CLASS_PATH 17
extern "C" struct JNINativeInterface JNI_JNIEnvTable;
extern "C" const struct JNIInvokeInterface JNI_JavaVMTable;
void ClArgumentsInfo::javaAgent(char* cur) {
assert(0 && "implement me");
}
extern "C" int sys_strnstr(const char *haystack, const char *needle) {
const char* res = strstr(haystack, needle);
if (res) return res - haystack;
else return -1;
}
static char* findInformation(Jnjvm* vm, ArrayUInt8* manifest, const char* entry,
uint32 len) {
uint8* ptr = manifest->elements;
sint32 index = sys_strnstr((char*)ptr, entry);
if (index != -1) {
index += len;
sint32 end = sys_strnstr((char*)&(ptr[index]), "\n");
if (end == -1) end = manifest->size;
else end += index;
sint32 length = end - index - 1;
char* name = (char*)vm->allocator.Allocate(length + 1, "class name");
memcpy(name, &(ptr[index]), length);
name[length] = 0;
return name;
} else {
return 0;
}
}
void ClArgumentsInfo::extractClassFromJar(Jnjvm* vm, int argc, char** argv,
int i) {
jarFile = argv[i];
vm->setClasspath(jarFile);
ArrayUInt8* bytes = Reader::openFile(vm->bootstrapLoader, jarFile, true);
if (!bytes) {
printf("Unable to access jarfile %s\n", jarFile);
return;
}
ZipArchive archive(bytes, vm->allocator);
if (archive.getOfscd() != -1) {
ZipFile* file = archive.getFile(PATH_MANIFEST);
if (file) {
UserClassArray* array = vm->bootstrapLoader->upcalls->ArrayOfByte;
ArrayUInt8* res = (ArrayUInt8*)array->doNew(file->ucsize, vm->allocator,
true);
int ok = archive.readFile(res, file);
if (ok) {
char* mainClass = findInformation(vm, res, MAIN_CLASS,
LENGTH_MAIN_CLASS);
if (!mainClass)
mainClass = findInformation(vm, res, MAIN_LOWER_CLASS,
LENGTH_MAIN_CLASS);
if (mainClass) {
className = mainClass;
} else {
printf("No Main-Class: in Manifest of archive %s.\n", jarFile);
}
} else {
printf("Can't extract Manifest file from archive %s\n", jarFile);
}
free(res);
} else {
printf("Can't find Manifest file in archive %s\n", jarFile);
}
} else {
printf("Can't find archive %s\n", jarFile);
}
free(bytes);
}
void ClArgumentsInfo::nyi() {
fprintf(stdout, "Not yet implemented\n");
}
void ClArgumentsInfo::printVersion() {
fprintf(stdout, "JnJVM for Java 1.1 -- 1.5\n");
}
void ClArgumentsInfo::printInformation() {
fprintf(stdout,
"Usage: java [-options] class [args...] (to execute a class)\n"
"or java [-options] -jar jarfile [args...]\n"
"(to execute a jar file) where options include:\n"
"-client to select the \"client\" VM\n"
"-server to select the \"server\" VM\n"
"-hotspot is a synonym for the \"client\" VM [deprecated]\n"
" The default VM is client.\n"
"\n"
"-cp <class search path of directories and zip/jar files>\n"
"-classpath <class search path of directories and zip/jar files>\n"
" A : separated list of directories, JAR archives,\n"
" and ZIP archives to search for class files.\n"
"-D<name>=<value>\n"
" set a system property\n"
"-verbose[:class|gc|jni]\n"
" enable verbose output\n"
"-version print product version and exit\n"
"-version:<value>\n"
" require the specified version to run\n"
"-showversion print product version and continue\n"
"-jre-restrict-search | -jre-no-restrict-search\n"
" include/exclude user private JREs in the version search\n"
"-? -help print this help message\n"
"-X print help on non-standard options\n"
"-ea[:<packagename>...|:<classname>]\n"
"-enableassertions[:<packagename>...|:<classname>]\n"
" enable assertions\n"
"-da[:<packagename>...|:<classname>]\n"
"-disableassertions[:<packagename>...|:<classname>]\n"
" disable assertions\n"
"-esa | -enablesystemassertions\n"
" enable system assertions\n"
"-dsa | -disablesystemassertions\n"
" disable system assertions\n"
"-agentlib:<libname>[=<options>]\n"
" load native agent library <libname>, e.g. -agentlib:hprof\n"
" see also, -agentlib:jdwp=help and -agentlib:hprof=help\n"
"-agentpath:<pathname>[=<options>]\n"
" load native agent library by full pathname\n"
"-javaagent:<jarpath>[=<options>]\n"
" load Java programming language agent, see java.lang.instrument\n");
}
void ClArgumentsInfo::readArgs(Jnjvm* vm) {
className = 0;
appArgumentsPos = 0;
sint32 i = 1;
if (i == argc) printInformation();
while (i < argc) {
char* cur = argv[i];
if (!(strcmp(cur, "-client"))) {
nyi();
} else if (!(strcmp(cur, "-server"))) {
nyi();
} else if (!(strcmp(cur, "-classpath"))) {
++i;
if (i == argc) printInformation();
else vm->setClasspath(argv[i]);
} else if (!(strcmp(cur, "-cp"))) {
++i;
if (i == argc) printInformation();
else vm->setClasspath(argv[i]);
} else if (!(strcmp(cur, "-debug"))) {
nyi();
} else if (!(strncmp(cur, "-D", 2))) {
uint32 len = strlen(cur);
if (len == 2) {
printInformation();
} else {
char* key = &cur[2];
char* value = strchr(key, '=');
if (!value) {
printInformation();
return;
} else {
value[0] = 0;
vm->addProperty(key, &value[1]);
}
}
} else if (!(strncmp(cur, "-Xbootclasspath:", 16))) {
uint32 len = strlen(cur);
if (len == 16) {
printInformation();
} else {
char* path = &cur[16];
vm->bootstrapLoader->analyseClasspathEnv(path);
}
} else if (!(strcmp(cur, "-enableassertions"))) {
nyi();
} else if (!(strcmp(cur, "-ea"))) {
nyi();
} else if (!(strcmp(cur, "-disableassertions"))) {
nyi();
} else if (!(strcmp(cur, "-da"))) {
nyi();
} else if (!(strcmp(cur, "-enablesystemassertions"))) {
nyi();
} else if (!(strcmp(cur, "-esa"))) {
nyi();
} else if (!(strcmp(cur, "-disablesystemassertions"))) {
nyi();
} else if (!(strcmp(cur, "-dsa"))) {
nyi();
} else if (!(strcmp(cur, "-jar"))) {
++i;
if (i == argc) {
printInformation();
} else {
extractClassFromJar(vm, argc, argv, i);
appArgumentsPos = i;
return;
}
} else if (!(strcmp(cur, "-jre-restrict-research"))) {
nyi();
} else if (!(strcmp(cur, "-jre-no-restrict-research"))) {
nyi();
} else if (!(strcmp(cur, "-noclassgc"))) {
nyi();
} else if (!(strcmp(cur, "-ms"))) {
nyi();
} else if (!(strcmp(cur, "-mx"))) {
nyi();
} else if (!(strcmp(cur, "-ss"))) {
nyi();
} else if (!(strcmp(cur, "-verbose"))) {
nyi();
} else if (!(strcmp(cur, "-verbose:class"))) {
nyi();
} else if (!(strcmp(cur, "-verbosegc"))) {
nyi();
} else if (!(strcmp(cur, "-verbose:gc"))) {
nyi();
} else if (!(strcmp(cur, "-verbose:jni"))) {
nyi();
} else if (!(strcmp(cur, "-version"))) {
printVersion();
} else if (!(strcmp(cur, "-showversion"))) {
nyi();
} else if (!(strcmp(cur, "-?"))) {
printInformation();
} else if (!(strcmp(cur, "-help"))) {
printInformation();
} else if (!(strcmp(cur, "-X"))) {
nyi();
} else if (!(strcmp(cur, "-agentlib"))) {
nyi();
} else if (!(strcmp(cur, "-agentpath"))) {
nyi();
} else if (cur[0] == '-') {
} else if (!(strcmp(cur, "-javaagent"))) {
javaAgent(cur);
} else {
className = cur;
appArgumentsPos = i;
return;
}
++i;
}
}
JnjvmClassLoader* Jnjvm::loadAppClassLoader() {
JavaObject* loader = 0;
llvm_gcroot(loader, 0);
if (appClassLoader == 0) {
UserClass* cl = upcalls->newClassLoader;
loader = upcalls->getSystemClassLoader->invokeJavaObjectStatic(this, cl);
appClassLoader = JnjvmClassLoader::getJnjvmLoaderFromJavaObject(loader,
this);
if (argumentsInfo.jarFile)
appClassLoader->loadLibFromJar(this, argumentsInfo.jarFile,
argumentsInfo.className);
else if (argumentsInfo.className)
appClassLoader->loadLibFromFile(this, argumentsInfo.className);
}
return appClassLoader;
}
void Jnjvm::loadBootstrap() {
JnjvmClassLoader* loader = bootstrapLoader;
// First create system threads.
finalizerThread = new JavaThread(0, 0, this);
finalizerThread->start((void (*)(mvm::Thread*))finalizerStart);
enqueueThread = new JavaThread(0, 0, this);
enqueueThread->start((void (*)(mvm::Thread*))enqueueStart);
// Initialise the bootstrap class loader if it's not
// done already.
if (!bootstrapLoader->upcalls->newString)
bootstrapLoader->upcalls->initialiseClasspath(bootstrapLoader);
#define LOAD_CLASS(cl) \
cl->resolveClass(); \
cl->initialiseClass(this);
// If a string belongs to the vm hashmap, we must remove it when
// it's destroyed. So we define a new VT for strings that will be
// placed in the hashmap. This VT will have its destructor set so
// that the string is removed when deallocated.
upcalls->newString->resolveClass();
if (!JavaString::internStringVT) {
JavaVirtualTable* stringVT = upcalls->newString->getVirtualVT();
uint32 size = upcalls->newString->virtualTableSize * sizeof(uintptr_t);
JavaString::internStringVT =
(JavaVirtualTable*)bootstrapLoader->allocator.Allocate(size, "String VT");
memcpy(JavaString::internStringVT, stringVT, size);
JavaString::internStringVT->destructor =
(uintptr_t)JavaString::stringDestructor;
// Tell the finalizer that this is a native destructor.
JavaString::internStringVT->operatorDelete =
(uintptr_t)JavaString::stringDestructor;
}
upcalls->newString->initialiseClass(this);
#ifdef SERVICE
if (!IsolateID)
#endif
// The initialization code of the classes initialized below may require
// to get the Java thread, so we create the Java thread object first.
upcalls->InitializeThreading(this);
LOAD_CLASS(upcalls->newClass);
LOAD_CLASS(upcalls->newConstructor);
LOAD_CLASS(upcalls->newField);
LOAD_CLASS(upcalls->newMethod);
LOAD_CLASS(upcalls->newVMThread);
LOAD_CLASS(upcalls->newStackTraceElement);
LOAD_CLASS(upcalls->newVMThrowable);
LOAD_CLASS(upcalls->boolClass);
LOAD_CLASS(upcalls->byteClass);
LOAD_CLASS(upcalls->charClass);
LOAD_CLASS(upcalls->shortClass);
LOAD_CLASS(upcalls->intClass);
LOAD_CLASS(upcalls->longClass);
LOAD_CLASS(upcalls->floatClass);
LOAD_CLASS(upcalls->doubleClass);
LOAD_CLASS(upcalls->InvocationTargetException);
LOAD_CLASS(upcalls->ArrayStoreException);
LOAD_CLASS(upcalls->ClassCastException);
LOAD_CLASS(upcalls->IllegalMonitorStateException);
LOAD_CLASS(upcalls->IllegalArgumentException);
LOAD_CLASS(upcalls->InterruptedException);
LOAD_CLASS(upcalls->IndexOutOfBoundsException);
LOAD_CLASS(upcalls->ArrayIndexOutOfBoundsException);
LOAD_CLASS(upcalls->NegativeArraySizeException);
LOAD_CLASS(upcalls->NullPointerException);
LOAD_CLASS(upcalls->SecurityException);
LOAD_CLASS(upcalls->ClassFormatError);
LOAD_CLASS(upcalls->ClassCircularityError);
LOAD_CLASS(upcalls->NoClassDefFoundError);
LOAD_CLASS(upcalls->UnsupportedClassVersionError);
LOAD_CLASS(upcalls->NoSuchFieldError);
LOAD_CLASS(upcalls->NoSuchMethodError);
LOAD_CLASS(upcalls->InstantiationError);
LOAD_CLASS(upcalls->IllegalAccessError);
LOAD_CLASS(upcalls->IllegalAccessException);
LOAD_CLASS(upcalls->VerifyError);
LOAD_CLASS(upcalls->ExceptionInInitializerError);
LOAD_CLASS(upcalls->LinkageError);
LOAD_CLASS(upcalls->AbstractMethodError);
LOAD_CLASS(upcalls->UnsatisfiedLinkError);
LOAD_CLASS(upcalls->InternalError);
LOAD_CLASS(upcalls->OutOfMemoryError);
LOAD_CLASS(upcalls->StackOverflowError);
LOAD_CLASS(upcalls->UnknownError);
LOAD_CLASS(upcalls->ClassNotFoundException);
LOAD_CLASS(upcalls->ArithmeticException);
LOAD_CLASS(upcalls->InstantiationException);
LOAD_CLASS(upcalls->SystemClass);
#undef LOAD_CLASS
loadAppClassLoader();
JavaObject* obj = JavaThread::get()->currentThread();
JavaObject* javaLoader = appClassLoader->getJavaClassLoader();
#ifdef SERVICE
if (!IsolateID)
#endif
upcalls->setContextClassLoader->invokeIntSpecial(this, upcalls->newThread,
obj, &javaLoader);
// load and initialise math since it is responsible for dlopen'ing
// libjavalang.so and we are optimizing some math operations
UserCommonClass* math =
loader->loadName(loader->asciizConstructUTF8("java/lang/Math"), true, true);
math->asClass()->initialiseClass(this);
}
void Jnjvm::executeClass(const char* className, ArrayObject* args) {
JavaObject* exc = 0;
JavaObject* obj = 0;
JavaObject* group = 0;
llvm_gcroot(args, 0);
llvm_gcroot(exc, 0);
llvm_gcroot(obj, 0);
llvm_gcroot(group, 0);
try {
// First try to see if we are a self-contained executable.
UserClass* cl = appClassLoader->loadClassFromSelf(this, className);
// If not, load the class.
if (!cl) {
const UTF8* name = appClassLoader->asciizConstructUTF8(className);
cl = (UserClass*)appClassLoader->loadName(name, true, true);
}
cl->initialiseClass(this);
const UTF8* funcSign =
appClassLoader->asciizConstructUTF8("([Ljava/lang/String;)V");
const UTF8* funcName = appClassLoader->asciizConstructUTF8("main");
JavaMethod* method = cl->lookupMethod(funcName, funcSign, true, true, 0);
method->invokeIntStatic(this, method->classDef, &args);
}catch(...) {
}
exc = JavaThread::get()->pendingException;
if (exc) {
JavaThread* th = JavaThread::get();
th->clearException();
obj = th->currentThread();
group = upcalls->group->getObjectField(obj);
try{
upcalls->uncaughtException->invokeIntSpecial(this, upcalls->threadGroup,
group, &obj, &exc);
}catch(...) {
fprintf(stderr, "Exception in thread \"main\": "
"Can not print stack trace.\n");
}
}
}
void Jnjvm::executePremain(const char* className, JavaString* args,
JavaObject* instrumenter) {
llvm_gcroot(instrumenter, 0);
try {
const UTF8* name = appClassLoader->asciizConstructUTF8(className);
UserClass* cl = (UserClass*)appClassLoader->loadName(name, true, true);
cl->initialiseClass(this);
const UTF8* funcSign = appClassLoader->asciizConstructUTF8(
"(Ljava/lang/String;Ljava/lang/instrument/Instrumentation;)V");
const UTF8* funcName = appClassLoader->asciizConstructUTF8("premain");
JavaMethod* method = cl->lookupMethod(funcName, funcSign, true, true, 0);
method->invokeIntStatic(this, method->classDef, &args, &instrumenter);
} catch(...) {
JavaThread::get()->clearException();
}
}
void Jnjvm::waitForExit() {
threadSystem.nonDaemonLock.lock();
while (threadSystem.nonDaemonThreads) {
threadSystem.nonDaemonVar.wait(&threadSystem.nonDaemonLock);
}
threadSystem.nonDaemonLock.unlock();
return;
}
void Jnjvm::mainJavaStart(JavaThread* thread) {
JavaString* str = 0;
JavaObject* instrumenter = 0;
ArrayObject* args = 0;
llvm_gcroot(str, 0);
llvm_gcroot(instrumenter, 0);
llvm_gcroot(args, 0);
Jnjvm* vm = thread->getJVM();
vm->mainThread = thread;
vm->loadBootstrap();
#ifdef SERVICE
thread->ServiceException = vm->upcalls->newThrowable->doNew(vm);
#endif
ClArgumentsInfo& info = vm->argumentsInfo;
if (info.agents.size()) {
assert(0 && "implement me");
instrumenter = 0;//createInstrumenter();
for (std::vector< std::pair<char*, char*> >::iterator i =
info.agents.begin(),
e = info.agents.end(); i!= e; ++i) {
str = vm->asciizToStr(i->second);
vm->executePremain(i->first, str, instrumenter);
}
}
UserClassArray* array = vm->bootstrapLoader->upcalls->ArrayOfString;
args = (ArrayObject*)array->doNew(info.argc - 2, vm);
for (int i = 2; i < info.argc; ++i) {
args->elements[i - 2] = (JavaObject*)vm->asciizToStr(info.argv[i]);
}
vm->executeClass(info.className, args);
vm->threadSystem.nonDaemonLock.lock();
--(vm->threadSystem.nonDaemonThreads);
if (vm->threadSystem.nonDaemonThreads == 0)
vm->threadSystem.nonDaemonVar.signal();
vm->threadSystem.nonDaemonLock.unlock();
}
#ifdef SERVICE
#include <signal.h>
extern void terminationHandler(int);
static void serviceCPUMonitor(mvm::Thread* th) {
while (true) {
sleep(1);
for(JavaThread* cur = (Java*)th->next(); cur != th;
cur = (JavaThread*)cur->next()) {
JavaThread* th = (JavaThread*)cur;
if (!(th->StateWaiting)) {
mvm::VirtualMachine* executingVM = cur->MyVM;
assert(executingVM && "Thread with no VM!");
++executingVM->executionTime;
}
}
}
}
#endif
void Jnjvm::runApplication(int argc, char** argv) {
argumentsInfo.argc = argc;
argumentsInfo.argv = argv;
argumentsInfo.readArgs(this);
if (argumentsInfo.className) {
int pos = argumentsInfo.appArgumentsPos;
argumentsInfo.argv = argumentsInfo.argv + pos - 1;
argumentsInfo.argc = argumentsInfo.argc - pos + 1;
#ifdef SERVICE
struct sigaction sa;
sigset_t mask;
sigfillset(&mask);
sigaction(SIGUSR1, 0, &sa);
sa.sa_mask = mask;
sa.sa_handler = terminationHandler;
sa.sa_flags |= SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
mvm::Thread* th = new JavaThread(0, 0, this);
th->start(serviceCPUMonitor);
#endif
mainThread = new JavaThread(0, 0, this);
mainThread->start((void (*)(mvm::Thread*))mainJavaStart);
} else {
threadSystem.nonDaemonThreads = 0;
}
}
Jnjvm::Jnjvm(mvm::BumpPtrAllocator& Alloc, JnjvmBootstrapLoader* loader) :
VirtualMachine(Alloc), lockSystem(this) {
classpath = getenv("CLASSPATH");
if (!classpath) classpath = ".";
appClassLoader = 0;
jniEnv = &JNI_JNIEnvTable;
javavmEnv = &JNI_JavaVMTable;
bootstrapLoader = loader;
upcalls = bootstrapLoader->upcalls;
throwable = upcalls->newThrowable;
StringList* end = loader->strings;
while (end) {
for (uint32 i = 0; i < end->length; ++i) {
JavaString* obj = end->strings[i];
hashStr.insert(obj);
}
end = end->prev;
}
bootstrapLoader->insertAllMethodsInVM(this);
#ifdef ISOLATE
IsolateLock.lock();
for (uint32 i = 0; i < NR_ISOLATES; ++i) {
if (RunningIsolates[i] == 0) {
RunningIsolates[i] = this;
IsolateID = i;
break;
}
}
IsolateLock.unlock();
#endif
scanner = loader->getCompiler()->createStackScanner();
}
Jnjvm::~Jnjvm() {
#ifdef ISOLATE
RunningIsolates[IsolateID] = 0;
#endif
}
const UTF8* Jnjvm::asciizToInternalUTF8(const char* asciiz) {
uint32 size = strlen(asciiz);
UTF8* tmp = (UTF8*)upcalls->ArrayOfChar->doNew(size, this);
uint16* buf = tmp->elements;
for (uint32 i = 0; i < size; i++) {
if (asciiz[i] == '.') buf[i] = '/';
else buf[i] = asciiz[i];
}
return (const UTF8*)tmp;
}
ArrayUInt16* Jnjvm::asciizToArray(const char* asciiz) {
uint32 size = strlen(asciiz);
ArrayUInt16* tmp = (ArrayUInt16*)upcalls->ArrayOfChar->doNew(size, this);
uint16* buf = tmp->elements;
for (uint32 i = 0; i < size; i++) {
buf[i] = asciiz[i];
}
return tmp;
}
void Jnjvm::internalRemoveMethods(JnjvmClassLoader* loader, mvm::FunctionMap& Map) {
// Loop over all methods in the map to find which ones belong
// to this class loader.
Map.FunctionMapLock.acquire();
std::map<void*, mvm::MethodInfo*>::iterator temp;
for (std::map<void*, mvm::MethodInfo*>::iterator i = Map.Functions.begin(),
e = Map.Functions.end(); i != e;) {
mvm::MethodInfo* MI = i->second;
if (MI->MethodType == 1) {
JavaMethod* meth = (JavaMethod*)i->second->getMetaInfo();
if (meth->classDef->classLoader == loader) {
temp = i;
++i;
Map.Functions.erase(temp);
} else {
++i;
}
} else {
++i;
}
}
Map.FunctionMapLock.release();
}
void Jnjvm::removeMethodsInFunctionMaps(JnjvmClassLoader* loader) {
internalRemoveMethods(loader, RuntimeFunctions);
internalRemoveMethods(loader, StaticFunctions);
}
/// JavaStaticCompiler - Compiler for AOT-compiled programs that
/// do not use the JIT.
class JavaStaticCompiler : public JavaCompiler {
public:
#ifdef WITH_LLVM_GCC
virtual mvm::StackScanner* createStackScanner() {
return new mvm::PreciseStackScanner();
}
#endif
};
// Helper function to run Jnjvm without JIT.
extern "C" int StartJnjvmWithoutJIT(int argc, char** argv, char* mainClass) {
mvm::Collector::initialise();
char** newArgv = new char*[argc + 1];
memcpy(newArgv + 1, argv, argc * sizeof(char*));
newArgv[0] = newArgv[1];
newArgv[1] = mainClass;
JavaCompiler* Comp = new JavaStaticCompiler();
JnjvmClassLoader* JCL = mvm::VirtualMachine::initialiseJVM(Comp);
mvm::VirtualMachine* vm = mvm::VirtualMachine::createJVM(JCL);
vm->runApplication(argc + 1, newArgv);
vm->waitForExit();
delete[] newArgv;
return 0;
}