| //===---------- 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 1 |
| |
| #include <cfloat> |
| #include <climits> |
| #include <cstdarg> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <string> |
| #include "debug.h" |
| |
| #include "vmkit/Thread.h" |
| #include "VmkitGC.h" |
| |
| #include "ClasspathReflect.h" |
| #include "JavaArray.h" |
| #include "JavaClass.h" |
| #include "j3/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 "JavaReferenceQueue.h" |
| #include "VMStaticInstance.h" |
| #include "Zip.h" |
| |
| using namespace j3; |
| |
| const char* Jnjvm::dirSeparator = "/"; |
| const char* Jnjvm::envSeparator = ":"; |
| const unsigned int Jnjvm::Magic = 0xcafebabe; |
| |
| /** |
| * In JVM specification, the virtual machine should execute some code when |
| * the application finish. |
| * See Runtime.addShutdownHook |
| * In GNUClasspath the default behavior when the program call System.exit |
| * is to execute such a code. |
| * Hence, the only mission of this thread is to call System.exit when |
| * the user press Ctrl_C |
| */ |
| void threadToDetectCtrl_C(vmkit::Thread* th) { |
| while (!vmkit::finishForCtrl_C) { |
| vmkit::lockForCtrl_C.lock(); |
| vmkit::condForCtrl_C.wait(&vmkit::lockForCtrl_C); |
| vmkit::lockForCtrl_C.unlock(th); |
| } |
| JavaThread* kk = (JavaThread*)th; |
| UserClass* cl = kk->getJVM()->upcalls->SystemClass; |
| kk->getJVM() -> upcalls->SystemExit->invokeIntStatic(kk->getJVM(), cl, 0); |
| } |
| |
| |
| /// initialiseClass - Java class initialization. Java specification §2.17.5. |
| |
| void UserClass::initialiseClass(Jnjvm* vm) { |
| JavaObject* exc = NULL; |
| JavaObject* obj = NULL; |
| llvm_gcroot(exc, 0); |
| llvm_gcroot(obj, 0); |
| |
| // 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); |
| setInitializationState(inClinit); |
| UserClass* cl = (UserClass*)this; |
| |
| // Single environment allocates the static instance during resolution, so |
| // that compiled code can access it directly (with an initialization |
| // check just before the access) |
| if (!cl->getStaticInstance()) cl->allocateStaticInstance(vm); |
| |
| 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(); |
| } END_CATCH; |
| if (self->pendingException != NULL) { |
| self->throwPendingException(); |
| return; |
| } |
| } |
| |
| // 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, "; "); |
| PRINT_DEBUG(JNJVM_LOAD, 0, LIGHT_GREEN, "clinit "); |
| PRINT_DEBUG(JNJVM_LOAD, 0, COLOR_NORMAL, "%s\n", UTF8Buffer(this->name).cString()); |
| |
| JavaField* fields = cl->getStaticFields(); |
| for (uint32 i = 0; i < cl->nbStaticFields; ++i) { |
| fields[i].InitStaticField(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(); |
| } END_CATCH; |
| } |
| |
| // 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 (JavaObject::getClass(exc)->isSubclassOf(vm->upcalls->newException)) { |
| Classpath* upcalls = classLoader->bootstrapLoader->upcalls; |
| UserClass* clExcp = upcalls->ExceptionInInitializerError; |
| Jnjvm* vm = self->getJVM(); |
| obj = clExcp->doNew(vm); |
| if (obj == NULL) { |
| 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); |
| return; |
| } |
| } |
| |
| void Jnjvm::errorWithExcp(UserClass* cl, JavaMethod* init, |
| const JavaObject* excp) { |
| JavaObject* obj = NULL; |
| llvm_gcroot(obj, 0); |
| llvm_gcroot(excp, 0); |
| |
| obj = cl->doNew(this); |
| init->invokeIntSpecial(this, cl, obj, &excp); |
| JavaThread::get()->throwException(obj); |
| } |
| |
| JavaObject* Jnjvm::CreateError(UserClass* cl, JavaMethod* init, |
| const char* asciiz) { |
| JavaObject* obj = NULL; |
| JavaString* str = NULL; |
| llvm_gcroot(obj, 0); |
| llvm_gcroot(str, 0); |
| obj = cl->doNew(this); |
| |
| if (asciiz) str = asciizToStr(asciiz); |
| |
| init->invokeIntSpecial(this, cl, obj, &str); |
| return obj; |
| } |
| |
| JavaObject* Jnjvm::CreateError(UserClass* cl, JavaMethod* init, |
| JavaString* str) { |
| JavaObject* obj = NULL; |
| 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 = NULL; |
| llvm_gcroot(obj, 0); |
| llvm_gcroot(str, 0); |
| str = (JavaString*)upcalls->IntToString->invokeJavaObjectStatic( |
| this, upcalls->intClass, entry, 10); |
| error(upcalls->ArrayIndexOutOfBoundsException, |
| upcalls->InitArrayIndexOutOfBoundsException, str); |
| } |
| |
| void Jnjvm::negativeArraySizeException(sint32 size) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = (JavaString*) |
| upcalls->IntToString->invokeJavaObjectStatic(this, upcalls->intClass, |
| size, 10); |
| error(upcalls->NegativeArraySizeException, |
| upcalls->InitNegativeArraySizeException, str); |
| } |
| |
| void Jnjvm::nullPointerException() { |
| error(upcalls->NullPointerException, |
| upcalls->InitNullPointerException, (JavaString*)0); |
| } |
| |
| void Jnjvm::cloneNotSupportedException() { |
| error(upcalls->CloneNotSupportedException, |
| upcalls->InitCloneNotSupportedException, (JavaString*)0); |
| } |
| |
| JavaObject* Jnjvm::CreateIndexOutOfBoundsException(sint32 entry) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = (JavaString*) |
| upcalls->IntToString->invokeJavaObjectStatic(this, upcalls->intClass, |
| entry, 10); |
| 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 = NULL; |
| llvm_gcroot(str, 0); |
| str = constructString(meth->toString()); |
| return CreateError(upcalls->UnsatisfiedLinkError, |
| upcalls->InitUnsatisfiedLinkError, |
| str); |
| } |
| |
| JavaObject* Jnjvm::CreateArithmeticException() { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = asciizToStr("/ by zero"); |
| return CreateError(upcalls->ArithmeticException, |
| upcalls->InitArithmeticException, str); |
| } |
| |
| JavaObject* Jnjvm::CreateNullPointerException() { |
| return CreateError(upcalls->NullPointerException, |
| upcalls->InitNullPointerException, |
| (JavaString*)0); |
| } |
| |
| JavaObject* Jnjvm::CreateOutOfMemoryError() { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = asciizToStr("Java heap space"); |
| return CreateError(upcalls->OutOfMemoryError, |
| upcalls->InitOutOfMemoryError, str); |
| } |
| |
| JavaObject* Jnjvm::CreateStackOverflowError() { |
| // Don't call init, or else we'll get a new stack overflow error. |
| JavaObjectThrowable* obj = NULL; |
| llvm_gcroot(obj, 0); |
| obj = (JavaObjectThrowable*)upcalls->StackOverflowError->doNew(this); |
| JavaObjectThrowable::fillInStackTrace(obj); |
| return obj; |
| } |
| |
| void Jnjvm::stackOverflowError() { |
| JavaThread::get()->throwException(CreateStackOverflowError()); |
| UNREACHABLE(); |
| } |
| |
| JavaObject* Jnjvm::CreateArrayStoreException(JavaVirtualTable* VT) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| if (VT != NULL) str = JavaString::internalToJava(VT->cl->name, this); |
| 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 = NULL; |
| llvm_gcroot(str, 0); |
| str = asciizToStr(msg); |
| return CreateError(upcalls->LinkageError, |
| upcalls->InitLinkageError, str); |
| } |
| |
| void Jnjvm::illegalAccessException(const char* msg) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = asciizToStr(msg); |
| 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 = NULL; |
| llvm_gcroot(str, 0); |
| str = asciizToStr("Java heap space"); |
| error(upcalls->OutOfMemoryError, |
| upcalls->InitOutOfMemoryError, str); |
| } |
| |
| void Jnjvm::illegalArgumentException(const char* msg) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = asciizToStr(msg); |
| 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 = NULL; |
| llvm_gcroot(str, 0); |
| str = internalUTF8ToStr(cl->name); |
| error(upcalls->InstantiationException, upcalls->InitInstantiationException, |
| str); |
| } |
| |
| void Jnjvm::instantiationError(UserCommonClass* cl) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = internalUTF8ToStr(cl->name); |
| error(upcalls->InstantiationError, upcalls->InitInstantiationError, str); |
| } |
| |
| |
| JavaString* CreateNoSuchMsg(CommonClass* cl, const UTF8* name, |
| Jnjvm* vm) { |
| ArrayUInt16* msg = NULL; |
| JavaString* str = NULL; |
| llvm_gcroot(msg, 0); |
| llvm_gcroot(str, 0); |
| msg = (ArrayUInt16*) |
| vm->upcalls->ArrayOfChar->doNew(19 + cl->name->size + name->size, vm); |
| |
| uint32 i = 0; |
| |
| |
| ArrayUInt16::setElement(msg, 'u', i); i++; |
| ArrayUInt16::setElement(msg, 'n', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 'b', i); i++; |
| ArrayUInt16::setElement(msg, 'l', i); i++; |
| ArrayUInt16::setElement(msg, 'e', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 't', i); i++; |
| ArrayUInt16::setElement(msg, 'o', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'f', i); i++; |
| ArrayUInt16::setElement(msg, 'i', i); i++; |
| ArrayUInt16::setElement(msg, 'n', i); i++; |
| ArrayUInt16::setElement(msg, 'd', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| |
| for (sint32 j = 0; j < name->size; ++j) { |
| ArrayUInt16::setElement(msg, name->elements[j], i); |
| i++; |
| } |
| |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'i', i); i++; |
| ArrayUInt16::setElement(msg, 'n', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| |
| for (sint32 j = 0; j < cl->name->size; ++j) { |
| if (cl->name->elements[j] == '/') { |
| ArrayUInt16::setElement(msg, '.', i); |
| i++; |
| } else { |
| ArrayUInt16::setElement(msg, cl->name->elements[j], i); |
| i++; |
| } |
| } |
| |
| str = vm->constructString(msg); |
| |
| return str; |
| } |
| |
| void Jnjvm::noSuchFieldError(CommonClass* cl, const UTF8* name) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = CreateNoSuchMsg(cl, name, this); |
| error(upcalls->NoSuchFieldError, |
| upcalls->InitNoSuchFieldError, str); |
| } |
| |
| void Jnjvm::noSuchMethodError(CommonClass* cl, const UTF8* name) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = CreateNoSuchMsg(cl, name, this); |
| error(upcalls->NoSuchMethodError, |
| upcalls->InitNoSuchMethodError, str); |
| } |
| |
| void Jnjvm::abstractMethodError(CommonClass* cl, const UTF8* name) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = CreateNoSuchMsg(cl, name, this); |
| error(upcalls->AbstractMethodError, |
| upcalls->InitAbstractMethodError, str); |
| } |
| |
| JavaString* CreateUnableToLoad(const UTF8* name, Jnjvm* vm) { |
| ArrayUInt16* msg = NULL; |
| JavaString* str = NULL; |
| llvm_gcroot(msg, 0); |
| llvm_gcroot(str, 0); |
| |
| msg = (ArrayUInt16*)vm->upcalls->ArrayOfChar->doNew(15 + name->size, vm); |
| uint32 i = 0; |
| |
| |
| ArrayUInt16::setElement(msg, 'u', i); i++; |
| ArrayUInt16::setElement(msg, 'n', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 'b', i); i++; |
| ArrayUInt16::setElement(msg, 'l', i); i++; |
| ArrayUInt16::setElement(msg, 'e', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 't', i); i++; |
| ArrayUInt16::setElement(msg, 'o', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'l', i); i++; |
| ArrayUInt16::setElement(msg, 'o', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 'd', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| |
| for (sint32 j = 0; j < name->size; ++j) { |
| if (name->elements[j] == '/') { |
| ArrayUInt16::setElement(msg, '.', i); i++; |
| } else { |
| ArrayUInt16::setElement(msg, name->elements[j], i); i++; |
| } |
| } |
| |
| str = vm->constructString(msg); |
| |
| return str; |
| } |
| |
| JavaString* CreateUnableToLoad(JavaString* name, Jnjvm* vm) { |
| JavaString* str = NULL; |
| ArrayUInt16* msg = NULL; |
| llvm_gcroot(msg, 0); |
| llvm_gcroot(str, 0); |
| |
| msg = (ArrayUInt16*)vm->upcalls->ArrayOfChar->doNew(15 + name->count, vm); |
| uint32 i = 0; |
| |
| ArrayUInt16::setElement(msg, 'u', i); i++; |
| ArrayUInt16::setElement(msg, 'n', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 'b', i); i++; |
| ArrayUInt16::setElement(msg, 'l', i); i++; |
| ArrayUInt16::setElement(msg, 'e', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 't', i); i++; |
| ArrayUInt16::setElement(msg, 'o', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'l', i); i++; |
| ArrayUInt16::setElement(msg, 'o', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 'd', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| |
| for (sint32 j = name->offset; j < name->offset + name->count; ++j) { |
| if (ArrayUInt16::getElement(JavaString::getValue(name), j) == '/') { |
| ArrayUInt16::setElement(msg, '.', i); i++; |
| } else { |
| ArrayUInt16::setElement(msg, ArrayUInt16::getElement(JavaString::getValue(name), j), i); i++; |
| } |
| } |
| |
| str = vm->constructString(msg); |
| |
| return str; |
| } |
| |
| |
| |
| void Jnjvm::noClassDefFoundError(const UTF8* name) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = CreateUnableToLoad(name, this); |
| error(upcalls->NoClassDefFoundError, |
| upcalls->InitNoClassDefFoundError, str); |
| } |
| |
| void Jnjvm::classNotFoundException(JavaString* name) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = CreateUnableToLoad(name, this); |
| error(upcalls->ClassNotFoundException, |
| upcalls->InitClassNotFoundException, str); |
| } |
| |
| void Jnjvm::noClassDefFoundError(UserClass* cl, const UTF8* name) { |
| ArrayUInt16* msg = NULL; |
| JavaString* str = NULL; |
| llvm_gcroot(msg, 0); |
| llvm_gcroot(str, 0); |
| |
| uint32 size = 35 + name->size + cl->name->size; |
| msg = (ArrayUInt16*)upcalls->ArrayOfChar->doNew(size, this); |
| uint32 i = 0; |
| |
| |
| ArrayUInt16::setElement(msg, 't', i); i++; |
| ArrayUInt16::setElement(msg, 'r', i); i++; |
| ArrayUInt16::setElement(msg, 'y', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 't', i); i++; |
| ArrayUInt16::setElement(msg, 'o', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'l', i); i++; |
| ArrayUInt16::setElement(msg, 'o', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 'd', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| |
| for (sint32 j = 0; j < cl->name->size; ++j) { |
| if (cl->name->elements[j] == '/') { |
| ArrayUInt16::setElement(msg, '.', i); i++; |
| } else { |
| ArrayUInt16::setElement(msg, cl->name->elements[j], i); i++; |
| } |
| } |
| |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 'n', i); i++; |
| ArrayUInt16::setElement(msg, 'd', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'f', i); i++; |
| ArrayUInt16::setElement(msg, 'o', i); i++; |
| ArrayUInt16::setElement(msg, 'u', i); i++; |
| ArrayUInt16::setElement(msg, 'n', i); i++; |
| ArrayUInt16::setElement(msg, 'd', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'c', i); i++; |
| ArrayUInt16::setElement(msg, 'l', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 's', i); i++; |
| ArrayUInt16::setElement(msg, 's', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| ArrayUInt16::setElement(msg, 'n', i); i++; |
| ArrayUInt16::setElement(msg, 'a', i); i++; |
| ArrayUInt16::setElement(msg, 'm', i); i++; |
| ArrayUInt16::setElement(msg, 'e', i); i++; |
| ArrayUInt16::setElement(msg, 'd', i); i++; |
| ArrayUInt16::setElement(msg, ' ', i); i++; |
| |
| for (sint32 j = 0; j < name->size; ++j) { |
| if (name->elements[j] == '/') { |
| ArrayUInt16::setElement(msg, '.', i); i++; |
| } else { |
| ArrayUInt16::setElement(msg, name->elements[j], i); i++; |
| } |
| } |
| |
| assert(i == size && "Array overflow"); |
| |
| str = constructString(msg); |
| error(upcalls->NoClassDefFoundError, upcalls->InitNoClassDefFoundError, str); |
| } |
| |
| |
| void Jnjvm::classFormatError(const char* msg) { |
| JavaString* str = NULL; |
| llvm_gcroot(str, 0); |
| str = asciizToStr(msg); |
| error(upcalls->ClassFormatError, upcalls->InitClassFormatError, str); |
| } |
| |
| JavaString* Jnjvm::internalUTF8ToStr(const UTF8* utf8) { |
| ArrayUInt16* tmp = NULL; |
| llvm_gcroot(tmp, 0); |
| uint32 size = utf8->size; |
| tmp = (ArrayUInt16*)upcalls->ArrayOfChar->doNew(size, this); |
| |
| for (uint32 i = 0; i < size; i++) { |
| ArrayUInt16::setElement(tmp, utf8->elements[i], i); |
| } |
| |
| return constructString(tmp); |
| } |
| |
| JavaString* Jnjvm::constructString(const ArrayUInt16* array) { |
| JavaString* key = NULL; |
| llvm_gcroot(array, 0); |
| llvm_gcroot(key, 0); |
| key = JavaString::create(array, this); |
| if (upcalls->internString) { |
| return (JavaString*)upcalls->internString->invokeJavaObjectStatic( |
| this, upcalls->internString->classDef, &key); |
| } else { |
| return key; |
| } |
| } |
| |
| JavaString* Jnjvm::asciizToStr(const char* asciiz) { |
| ArrayUInt16* var = NULL; |
| llvm_gcroot(var, 0); |
| assert(asciiz && "No asciiz given"); |
| 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() == NULL) { |
| UserClass* cl = vm->upcalls->newClass; |
| delegatee = (JavaObjectClass*)cl->doNew(vm); |
| JavaObjectClass::setClass(delegatee, this); |
| if (pd == NULL && isArray()) { |
| base = (JavaObjectClass*) |
| asArrayClass()->baseClass()->getClassDelegatee(vm, pd); |
| JavaObjectClass::setProtectionDomain( |
| delegatee, JavaObjectClass::getProtectionDomain(base)); |
| } else { |
| JavaObjectClass::setProtectionDomain(delegatee, 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(); |
| } |
| |
| #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, ClassBytes* manifest, const char* entry, |
| uint32 len) { |
| sint32 index = sys_strnstr((char*)manifest->elements, entry); |
| if (index != -1) { |
| index += len; |
| sint32 end = sys_strnstr((char*)manifest->elements + 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, manifest->elements + index, length); |
| name[length] = 0; |
| return name; |
| } else { |
| return 0; |
| } |
| } |
| |
| void ClArgumentsInfo::extractClassFromJar(Jnjvm* vm, int argc, char** argv, |
| int i) { |
| ClassBytes* bytes = NULL; |
| ClassBytes* res = NULL; |
| jarFile = argv[i]; |
| |
| vm->setClasspath(jarFile); |
| |
| bytes = Reader::openFile(vm->bootstrapLoader, jarFile); |
| |
| if (bytes == NULL) { |
| printf("Unable to access jarfile %s\n", jarFile); |
| return; |
| } |
| |
| vmkit::BumpPtrAllocator allocator; |
| ZipArchive* archive = new(allocator, "TempZipArchive") |
| ZipArchive(bytes, allocator); |
| if (archive->getOfscd() != -1) { |
| ZipFile* file = archive->getFile(PATH_MANIFEST); |
| if (file != NULL) { |
| res = new (allocator, file->ucsize) ClassBytes(file->ucsize); |
| int ok = archive->readFile(res, file); |
| if (ok) { |
| char* mainClass = findInformation(vm, res, MAIN_CLASS, |
| LENGTH_MAIN_CLASS); |
| if (mainClass == NULL) { |
| mainClass = findInformation(vm, res, MAIN_LOWER_CLASS, |
| LENGTH_MAIN_CLASS); |
| } |
| if (mainClass != NULL) { |
| 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); |
| } |
| } else { |
| printf("Can't find Manifest file in archive %s\n", jarFile); |
| } |
| } else { |
| printf("Can't find archive %s\n", jarFile); |
| } |
| } |
| |
| void ClArgumentsInfo::nyi() { |
| fprintf(stdout, "Not yet implemented\n"); |
| } |
| |
| void ClArgumentsInfo::printVersion() { |
| fprintf(stdout, "J3 for Java 1.1 -- 1.5\n"); |
| } |
| |
| void ClArgumentsInfo::printInformation() { |
| fprintf(stdout, |
| "Usage: j3 [-options] class [args...] (to execute a class)\n" |
| "or j3 [-options] -jar jarfile [args...]\n" |
| "(to execute a jar file) where options include:\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, "-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 (!(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 (!(strncmp(cur, "-Xbootclasspath/a:", 18))) { |
| uint32 len = strlen(cur); |
| if (len == 18) { |
| printInformation(); |
| } else { |
| char* path = &cur[18]; |
| 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"))) { |
| vmkit::Collector::verbose = 1; |
| } 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 == NULL) { |
| 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() { |
| JavaObject* obj = NULL; |
| JavaObject* javaLoader = NULL; |
| llvm_gcroot(obj, 0); |
| llvm_gcroot(javaLoader, 0); |
| JnjvmBootstrapLoader* loader = bootstrapLoader; |
| |
| // First create system threads. |
| finalizerThread = new JavaFinalizerThread(this); |
| finalizerThread->start( |
| (void (*)(vmkit::Thread*))JavaFinalizerThread::finalizerStart); |
| |
| referenceThread = new JavaReferenceThread(this); |
| referenceThread->start( |
| (void (*)(vmkit::Thread*))JavaReferenceThread::enqueueStart); |
| |
| // Initialize the bootstrap class loader if it's not |
| // done already. |
| if (bootstrapLoader->upcalls->newString == NULL) { |
| bootstrapLoader->upcalls->initialiseClasspath(bootstrapLoader); |
| } |
| |
| #define LOAD_CLASS(cl) \ |
| cl->resolveClass(); \ |
| cl->initialiseClass(this); |
| |
| LOAD_CLASS(upcalls->newString); |
| |
| // 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->newStackTraceElement); |
| 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); |
| LOAD_CLASS(upcalls->cloneableClass); |
| LOAD_CLASS(upcalls->CloneNotSupportedException); |
| #undef LOAD_CLASS |
| |
| // Implementation-specific end-of-bootstrap initialization |
| upcalls->InitializeSystem(this); |
| |
| loadAppClassLoader(); |
| obj = JavaThread::get()->currentThread(); |
| javaLoader = appClassLoader->getJavaClassLoader(); |
| |
| 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->mathName, true, true, NULL); |
| math->asClass()->initialiseClass(this); |
| } |
| |
| void Jnjvm::executeClass(const char* className, ArrayObject* args) { |
| JavaObject* exc = NULL; |
| JavaObject* obj = NULL; |
| JavaObject* handler = NULL; |
| |
| llvm_gcroot(args, 0); |
| llvm_gcroot(exc, 0); |
| llvm_gcroot(obj, 0); |
| llvm_gcroot(handler, 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 == NULL) { |
| const UTF8* name = appClassLoader->asciizConstructUTF8(className); |
| cl = (UserClass*)appClassLoader->loadName(name, true, true, NULL); |
| } |
| |
| 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); |
| if (isPublic(method->access)) { |
| method->invokeIntStatic(this, method->classDef, &args); |
| } else { |
| fprintf(stderr, "Main method not public.\n"); |
| vmkit::System::Exit(1); |
| } |
| } CATCH { |
| } END_CATCH; |
| |
| exc = JavaThread::get()->pendingException; |
| if (exc != NULL) { |
| JavaThread* th = JavaThread::get(); |
| th->clearException(); |
| obj = th->currentThread(); |
| TRY { |
| handler = upcalls->getUncaughtExceptionHandler->invokeJavaObjectVirtual(this, upcalls->newThread, obj); |
| verifyNull(handler); |
| upcalls->uncaughtException->invokeIntVirtual(this, upcalls->uncaughtException->classDef, handler, &obj, &exc); |
| } CATCH { |
| fprintf(stderr, "Exception in thread \"main\": " |
| "Can not print stack trace.\n"); |
| } END_CATCH; |
| // Program failed. Exit with return code not 0. |
| vmkit::System::Exit(1); |
| } |
| } |
| |
| void Jnjvm::executePremain(const char* className, JavaString* args, |
| JavaObject* instrumenter) { |
| llvm_gcroot(args, 0); |
| llvm_gcroot(instrumenter, 0); |
| TRY { |
| const UTF8* name = appClassLoader->asciizConstructUTF8(className); |
| UserClass* cl = (UserClass*) |
| appClassLoader->loadName(name, true, true, NULL); |
| 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); |
| } IGNORE; |
| } |
| |
| void Jnjvm::mainJavaStart(JavaThread* thread) { |
| |
| JavaString* str = NULL; |
| JavaObject* instrumenter = NULL; |
| ArrayObject* args = NULL; |
| JavaObject* exc = NULL; |
| |
| llvm_gcroot(str, 0); |
| llvm_gcroot(instrumenter, 0); |
| llvm_gcroot(args, 0); |
| llvm_gcroot(exc, 0); |
| |
| Jnjvm* vm = thread->getJVM(); |
| vm->argumentsInfo.readArgs(vm); |
| if (vm->argumentsInfo.className == NULL) { |
| vm->threadSystem.leave(); |
| return; |
| } |
| |
| int pos = vm->argumentsInfo.appArgumentsPos; |
| vm->argumentsInfo.argv = vm->argumentsInfo.argv + pos - 1; |
| vm->argumentsInfo.argc = vm->argumentsInfo.argc - pos + 1; |
| |
| vm->mainThread = thread; |
| |
| TRY { |
| vm->loadBootstrap(); |
| } CATCH { |
| exc = JavaThread::get()->pendingException; |
| } END_CATCH; |
| |
| if (exc != NULL) { |
| fprintf(stderr, "Exception %s while bootstrapping VM.\n", |
| UTF8Buffer(JavaObject::getClass(exc)->name).cString()); |
| } else { |
| 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) { |
| str = vm->asciizToStr(info.argv[i]); |
| ArrayObject::setElement(args, str, i - 2); |
| } |
| |
| vm->executeClass(info.className, args); |
| } |
| vm->threadSystem.leave(); |
| } |
| |
| void ThreadSystem::leave() { |
| nonDaemonLock.lock(); |
| --nonDaemonThreads; |
| if (nonDaemonThreads == 0) vmkit::Thread::get()->MyVM->exit(); |
| nonDaemonLock.unlock(); |
| } |
| |
| void ThreadSystem::enter() { |
| nonDaemonLock.lock(); |
| ++nonDaemonThreads; |
| nonDaemonLock.unlock(); |
| } |
| |
| void Jnjvm::runApplication(int argc, char** argv) { |
| argumentsInfo.argc = argc; |
| argumentsInfo.argv = argv; |
| mainThread = new JavaThread(this); |
| mainThread->start((void (*)(vmkit::Thread*))mainJavaStart); |
| |
| JavaThread* th = new JavaThread(this); |
| th->start((void (*)(vmkit::Thread*))threadToDetectCtrl_C); |
| } |
| |
| Jnjvm::Jnjvm(vmkit::BumpPtrAllocator& Alloc, |
| vmkit::CompiledFrames** frames, |
| JnjvmBootstrapLoader* loader) : |
| VirtualMachine(Alloc, frames), lockSystem(Alloc) |
| { |
| classpath = getenv("CLASSPATH"); |
| if (classpath == NULL) classpath = "."; |
| |
| appClassLoader = NULL; |
| jniEnv = &JNI_JNIEnvTable; |
| javavmEnv = &JNI_JavaVMTable; |
| |
| bootstrapLoader = loader; |
| upcalls = bootstrapLoader->upcalls; |
| throwable = upcalls->newThrowable; |
| } |
| |
| Jnjvm::~Jnjvm() { |
| } |
| |
| ArrayUInt16* Jnjvm::asciizToArray(const char* asciiz) { |
| ArrayUInt16* tmp = NULL; |
| llvm_gcroot(tmp, 0); |
| |
| uint32 size = strlen(asciiz); |
| tmp = (ArrayUInt16*)upcalls->ArrayOfChar->doNew(size, this); |
| |
| for (uint32 i = 0; i < size; i++) { |
| ArrayUInt16::setElement(tmp, asciiz[i], i); |
| } |
| return tmp; |
| } |
| |
| void Jnjvm::startCollection() { |
| finalizerThread->FinalizationQueueLock.acquire(); |
| referenceThread->ToEnqueueLock.acquire(); |
| referenceThread->SoftReferencesQueue.acquire(); |
| referenceThread->WeakReferencesQueue.acquire(); |
| referenceThread->PhantomReferencesQueue.acquire(); |
| } |
| |
| void Jnjvm::endCollection() { |
| finalizerThread->FinalizationQueueLock.release(); |
| referenceThread->ToEnqueueLock.release(); |
| referenceThread->SoftReferencesQueue.release(); |
| referenceThread->WeakReferencesQueue.release(); |
| referenceThread->PhantomReferencesQueue.release(); |
| finalizerThread->FinalizationCond.broadcast(); |
| referenceThread->EnqueueCond.broadcast(); |
| } |
| |
| void Jnjvm::scanWeakReferencesQueue(word_t closure) { |
| referenceThread->WeakReferencesQueue.scan(referenceThread, closure); |
| } |
| |
| void Jnjvm::scanSoftReferencesQueue(word_t closure) { |
| referenceThread->SoftReferencesQueue.scan(referenceThread, closure); |
| } |
| |
| void Jnjvm::scanPhantomReferencesQueue(word_t closure) { |
| referenceThread->PhantomReferencesQueue.scan(referenceThread, closure); |
| } |
| |
| void Jnjvm::scanFinalizationQueue(word_t closure) { |
| finalizerThread->scanFinalizationQueue(closure); |
| } |
| |
| void Jnjvm::addFinalizationCandidate(gc* object) { |
| llvm_gcroot(object, 0); |
| finalizerThread->addFinalizationCandidate(object); |
| } |
| |
| void Jnjvm::setType(gc* header, void* type) { |
| JavaObject* src = 0; |
| llvm_gcroot(src, 0); |
| llvm_gcroot(header, 0); |
| src = (JavaObject*)header; |
| src->setVirtualTable((JavaVirtualTable*)type); |
| } |
| |
| //void Jnjvm::setType(void* header, void* type) |
| //{ |
| // ((JavaObject*)header)->setVirtualTable((JavaVirtualTable*)type); |
| //} |
| |
| void* Jnjvm::getType(gc* header) { |
| JavaObject* src = 0; |
| llvm_gcroot(src, 0); |
| llvm_gcroot(header, 0); |
| src = (JavaObject*)header; |
| return src->getVirtualTable(); |
| } |
| |
| // This method is called during GC so no llvm_gcroot needed. |
| void Jnjvm::traceObject(gc* _obj, word_t closure) { |
| JavaObject* obj = 0; |
| llvm_gcroot(obj, 0); |
| llvm_gcroot(_obj, 0); |
| obj = (JavaObject*)_obj; |
| assert(obj && "No object to trace"); |
| assert(obj->getVirtualTable() && "No virtual table"); |
| assert(obj->getVirtualTable()->tracer && "No tracer in VT"); |
| obj->tracer(closure); |
| } |
| |
| // This method is called during GC so no llvm_gcroot needed. |
| bool Jnjvm::isCorruptedType(gc* obj) { |
| JavaObject* _obj = 0; |
| llvm_gcroot(_obj, 0); |
| llvm_gcroot(obj, 0); |
| _obj = (JavaObject*)obj; |
| return _obj->getVirtualTable(); |
| } |
| |
| size_t Jnjvm::getObjectSize(gc* object) { |
| // TODO: because this is called during GC, there is no need to do |
| // llvm_gcroot. For clarity, it may be useful to have a special type |
| // in this case. |
| size_t size = 0; |
| JavaObject* src = 0; |
| llvm_gcroot(object, 0); |
| llvm_gcroot(src, 0); |
| src = (JavaObject*)object; |
| if (VMClassLoader::isVMClassLoader(src)) { |
| size = sizeof(VMClassLoader); |
| } else if (VMStaticInstance::isVMStaticInstance(src)) { |
| size = sizeof(VMStaticInstance); |
| } else { |
| CommonClass* cl = JavaObject::getClass(src); |
| if (cl->isArray()) { |
| UserClassArray* array = cl->asArrayClass(); |
| UserCommonClass* base = array->baseClass(); |
| uint32 logSize = base->isPrimitive() ? |
| base->asPrimitiveClass()->logSize : (sizeof(JavaObject*) == 8 ? 3 : 2); |
| |
| size = sizeof(JavaObject) + sizeof(ssize_t) + |
| (JavaArray::getSize(src) << logSize); |
| } else { |
| assert(cl->isClass() && "Not a class!"); |
| size = cl->asClass()->getVirtualSize(); |
| } |
| } |
| return size; |
| } |
| |
| const char* Jnjvm::getObjectTypeName(gc* object) { |
| JavaObject* src = 0; |
| llvm_gcroot(object, 0); |
| llvm_gcroot(src, 0); |
| src = (JavaObject*)object; |
| if (VMClassLoader::isVMClassLoader(src)) { |
| return "VMClassLoader"; |
| } else if (VMStaticInstance::isVMStaticInstance(src)) { |
| return "VMStaticInstance"; |
| } else { |
| CommonClass* cl = JavaObject::getClass(src); |
| // This code is only used for debugging on a fatal error. It is fine to |
| // allocate in the C++ heap. |
| return (new UTF8Buffer(cl->name))->cString(); |
| } |
| } |
| |
| // Helper function to run J3 without JIT. |
| extern "C" int StartJnjvmWithoutJIT(int argc, char** argv, char* mainClass) { |
| vmkit::Collector::initialise(argc, argv); |
| |
| vmkit::ThreadAllocator allocator; |
| char** newArgv = (char**)allocator.Allocate((argc + 1) * sizeof(char*)); |
| memcpy(newArgv + 1, argv, argc * sizeof(char*)); |
| newArgv[0] = newArgv[1]; |
| newArgv[1] = mainClass; |
| |
| vmkit::BumpPtrAllocator Allocator; |
| JavaCompiler* Comp = new JavaCompiler(); |
| JnjvmBootstrapLoader* loader = new(Allocator, "Bootstrap loader") |
| JnjvmBootstrapLoader(Allocator, Comp, true); |
| Jnjvm* vm = new(Allocator, "VM") Jnjvm(Allocator, NULL, loader); |
| |
| vm->runApplication(argc + 1, newArgv); |
| vm->waitForExit(); |
| |
| return 0; |
| } |
| |
| void Jnjvm::printMethod(vmkit::FrameInfo* FI, word_t ip, word_t addr) { |
| if (FI->Metadata == NULL) { |
| vmkit::MethodInfoHelper::print(ip, addr); |
| return; |
| } |
| JavaMethod* meth = (JavaMethod*)FI->Metadata; |
| |
| fprintf(stderr, "; %p (%p) in %s.%s (line %d, bytecode %d, code start %p)", |
| (void*)ip, |
| (void*)addr, |
| UTF8Buffer(meth->classDef->name).cString(), |
| UTF8Buffer(meth->name).cString(), |
| meth->lookupLineNumber(FI), |
| FI->SourceIndex, meth->code); |
| |
| fprintf(stderr, "\n"); |
| } |
| |
| void Jnjvm::printBacktrace() |
| { |
| std::cerr << "Back trace:" << std::endl; |
| JavaThread::get()->printJavaBacktrace(); |
| } |