blob: de52bcff4a417d5ac837bdaad28715a341bd4467 [file] [log] [blame]
//===-------------------- JavaRuntimeJIT.cpp ------------------------------===//
//=== ---- Runtime functions called by code compiled by the JIT -----------===//
//
// JnJVM
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClasspathReflect.h"
#include "JavaArray.h"
#include "JavaCache.h"
#include "JavaClass.h"
#include "JavaConstantPool.h"
#include "JavaString.h"
#include "JavaThread.h"
#include "JavaTypes.h"
#include "Jnjvm.h"
#include "jnjvm/OpcodeNames.def"
#include <cstdarg>
using namespace jnjvm;
// Throws if the method is not found.
extern "C" void* jnjvmInterfaceLookup(CacheNode* cache, JavaObject *obj) {
llvm_gcroot(obj, 0);
void* res = 0;
BEGIN_NATIVE_EXCEPTION(1)
Enveloppe* enveloppe = cache->enveloppe;
JavaVirtualTable* ovt = (JavaVirtualTable*)obj->getVirtualTable();
#ifndef SERVICE
assert((obj->getClass()->isClass() &&
obj->getClass()->asClass()->isInitializing()) &&
"Class not ready in a virtual lookup.");
#endif
enveloppe->cacheLock.acquire();
CacheNode* rcache = 0;
CacheNode* tmp = enveloppe->firstCache;
CacheNode* last = tmp;
while (tmp) {
if (ovt == tmp->lastCible) {
rcache = tmp;
break;
} else {
last = tmp;
tmp = tmp->next;
}
}
if (!rcache) {
UserCommonClass* ocl = ovt->cl;
UserClass* methodCl = 0;
UserClass* lookup = ocl->isArray() ? ocl->super : ocl->asClass();
JavaMethod* dmeth = lookup->lookupMethodDontThrow(enveloppe->methodName,
enveloppe->methodSign,
false, true, &methodCl);
if (!dmeth) {
enveloppe->cacheLock.release();
JavaThread::get()->getJVM()->noSuchMethodError(lookup,
enveloppe->methodName);
}
#if !defined(ISOLATE_SHARING) && !defined(SERVICE)
assert(dmeth->classDef->isInitializing() &&
"Class not ready in a virtual lookup.");
#endif
// Are we the first cache?
if (cache == &(enveloppe->bootCache) && cache->lastCible == 0) {
rcache = cache;
} else {
mvm::BumpPtrAllocator& alloc =
enveloppe->classDef->classLoader->allocator;
rcache = new(alloc, "CacheNode") CacheNode(enveloppe);
}
rcache->methPtr = dmeth->compiledPtr();
rcache->lastCible = ovt;
}
if (enveloppe->firstCache != rcache) {
CacheNode *f = enveloppe->firstCache;
enveloppe->firstCache = rcache;
last->next = rcache->next;
rcache->next = f;
}
enveloppe->cacheLock.release();
res = rcache->methPtr;
END_NATIVE_EXCEPTION
return res;
}
// Throws if the field is not found.
extern "C" void* jnjvmVirtualFieldLookup(UserClass* caller, uint32 index) {
void* res = 0;
BEGIN_NATIVE_EXCEPTION(1)
UserConstantPool* ctpInfo = caller->getConstantPool();
if (ctpInfo->ctpRes[index]) {
res = ctpInfo->ctpRes[index];
} else {
UserCommonClass* cl = 0;
const UTF8* utf8 = 0;
Typedef* sign = 0;
ctpInfo->resolveField(index, cl, utf8, sign);
UserClass* lookup = cl->isArray() ? cl->super : cl->asClass();
JavaField* field = lookup->lookupField(utf8, sign->keyName, false, true, 0);
ctpInfo->ctpRes[index] = (void*)field->ptrOffset;
res = (void*)field->ptrOffset;
}
END_NATIVE_EXCEPTION
// Since the function is marked readnone, LLVM may move it after the
// exception check. Therefore, we trick LLVM to check the return value of the
// function.
JavaObject* obj = JavaThread::get()->pendingException;
if (obj) return (void*)obj;
return res;
}
// Throws if the field or its class is not found.
extern "C" void* jnjvmStaticFieldLookup(UserClass* caller, uint32 index) {
void* res = 0;
BEGIN_NATIVE_EXCEPTION(1)
UserConstantPool* ctpInfo = caller->getConstantPool();
if (ctpInfo->ctpRes[index]) {
res = ctpInfo->ctpRes[index];
} else {
UserCommonClass* cl = 0;
UserClass* fieldCl = 0;
const UTF8* utf8 = 0;
Typedef* sign = 0;
ctpInfo->resolveField(index, cl, utf8, sign);
assert(cl->asClass() && "Lookup a field of something not an array");
JavaField* field = cl->asClass()->lookupField(utf8, sign->keyName, true,
true, &fieldCl);
fieldCl->initialiseClass(JavaThread::get()->getJVM());
void* obj = ((UserClass*)fieldCl)->getStaticInstance();
assert(obj && "No static instance in static field lookup");
void* ptr = (void*)((uint64)obj + field->ptrOffset);
ctpInfo->ctpRes[index] = ptr;
res = ptr;
}
END_NATIVE_EXCEPTION
// Since the function is marked readnone, LLVM may move it after the
// exception check. Therefore, we trick LLVM to check the return value of the
// function.
JavaObject* obj = JavaThread::get()->pendingException;
if (obj) return (void*)obj;
return res;
}
#ifndef WITHOUT_VTABLE
// Throws if the method is not found.
extern "C" void* jnjvmVirtualTableLookup(UserClass* caller, uint32 index, ...) {
void* res = 0;
BEGIN_NATIVE_EXCEPTION(1)
UserCommonClass* cl = 0;
const UTF8* utf8 = 0;
Signdef* sign = 0;
caller->getConstantPool()->resolveMethod(index, cl, utf8, sign);
UserClass* lookup = cl->isArray() ? cl->super : cl->asClass();
JavaMethod* dmeth = lookup->lookupMethodDontThrow(utf8, sign->keyName, false,
true, 0);
if (!dmeth) {
va_list ap;
va_start(ap, index);
JavaObject* obj = va_arg(ap, JavaObject*);
va_end(ap);
assert((obj->getClass()->isClass() &&
obj->getClass()->asClass()->isInitializing()) &&
"Class not ready in a virtual lookup.");
// Arg, the bytecode is buggy! Perform the lookup on the object class
// and do not update offset.
lookup = obj->getClass()->isArray() ? obj->getClass()->super :
obj->getClass()->asClass();
dmeth = lookup->lookupMethod(utf8, sign->keyName, false, true, 0);
} else {
caller->getConstantPool()->ctpRes[index] = (void*)dmeth->offset;
}
#if !defined(ISOLATE_SHARING) && !defined(SERVICE)
assert(dmeth->classDef->isInitializing() &&
"Class not ready in a virtual lookup.");
#endif
res = (void*)dmeth->offset;
END_NATIVE_EXCEPTION
// Since the function is marked readnone, LLVM may move it after the
// exception check. Therefore, we trick LLVM to check the return value of the
// function.
JavaObject* obj = JavaThread::get()->pendingException;
if (obj) return (void*)obj;
return res;
}
#endif
// Throws if the class is not found.
extern "C" void* jnjvmClassLookup(UserClass* caller, uint32 index) {
void* res = 0;
BEGIN_NATIVE_EXCEPTION(1)
UserConstantPool* ctpInfo = caller->getConstantPool();
UserClass* cl = (UserClass*)ctpInfo->loadClass(index);
// We can not initialize here, because bytecodes such as CHECKCAST
// or classes used in catch clauses do not trigger class initialization.
// This is really sad, because we need to insert class initialization checks
// in the LLVM code.
assert(cl && "No cl after class lookup");
res = (void*)cl;
END_NATIVE_EXCEPTION
// Since the function is marked readnone, LLVM may move it after the
// exception check. Therefore, we trick LLVM to check the return value of the
// function.
JavaObject* obj = JavaThread::get()->pendingException;
if (obj) return (void*)obj;
return res;
}
// Calls Java code.
// Throws if initializing the class throws an exception.
extern "C" UserCommonClass* jnjvmRuntimeInitialiseClass(UserClass* cl) {
BEGIN_NATIVE_EXCEPTION(1)
cl->resolveClass();
cl->initialiseClass(JavaThread::get()->getJVM());
END_NATIVE_EXCEPTION
// Since the function is marked readnone, LLVM may move it after the
// exception check. Therefore, we trick LLVM to check the return value of the
// function.
JavaObject* obj = JavaThread::get()->pendingException;
if (obj) return (UserCommonClass*)obj;
return cl;
}
// Calls Java code.
extern "C" JavaObject* jnjvmRuntimeDelegatee(UserCommonClass* cl) {
JavaObject* res = 0;
llvm_gcroot(res, 0);
BEGIN_NATIVE_EXCEPTION(1)
Jnjvm* vm = JavaThread::get()->getJVM();
res = cl->getClassDelegatee(vm);
END_NATIVE_EXCEPTION
// Since the function is marked readnone, LLVM may move it after the
// exception check. Therefore, we trick LLVM to check the return value of the
// function.
JavaObject* obj = JavaThread::get()->pendingException;
if (obj) return obj;
return res;
}
// Throws if one of the dimension is negative.
static JavaArray* multiCallNewIntern(UserClassArray* cl, uint32 len,
sint32* dims, Jnjvm* vm) {
assert(len > 0 && "Negative size given by VMKit");
JavaArray* _res = cl->doNew(dims[0], vm);
ArrayObject* res = 0;
llvm_gcroot(_res, 0);
llvm_gcroot(res, 0);
if (len > 1) {
res = (ArrayObject*)_res;
UserCommonClass* _base = cl->baseClass();
assert(_base->isArray() && "Base class not an array");
UserClassArray* base = (UserClassArray*)_base;
if (dims[0] > 0) {
for (sint32 i = 0; i < dims[0]; ++i) {
res->elements[i] = multiCallNewIntern(base, (len - 1),
&dims[1], vm);
}
} else {
for (uint32 i = 1; i < len; ++i) {
sint32 p = dims[i];
if (p < 0) JavaThread::get()->getJVM()->negativeArraySizeException(p);
}
}
}
return _res;
}
// Throws if one of the dimension is negative.
extern "C" JavaArray* jnjvmMultiCallNew(UserClassArray* cl, uint32 len, ...) {
JavaArray* res = 0;
llvm_gcroot(res, 0);
BEGIN_NATIVE_EXCEPTION(1)
va_list ap;
va_start(ap, len);
sint32* dims = (sint32*)alloca(sizeof(sint32) * len);
for (uint32 i = 0; i < len; ++i){
dims[i] = va_arg(ap, int);
}
Jnjvm* vm = JavaThread::get()->getJVM();
res = multiCallNewIntern(cl, len, dims, vm);
END_NATIVE_EXCEPTION
return res;
}
// Throws if the class can not be resolved.
extern "C" UserClassArray* jnjvmGetArrayClass(UserCommonClass* cl,
UserClassArray** dcl) {
UserClassArray* res = 0;
BEGIN_NATIVE_EXCEPTION(1)
JnjvmClassLoader* JCL = cl->classLoader;
if (cl->asClass()) cl->asClass()->resolveClass();
const UTF8* arrayName = JCL->constructArrayName(1, cl->getName());
res = JCL->constructArray(arrayName);
if (dcl) *dcl = res;
END_NATIVE_EXCEPTION
// Since the function is marked readnone, LLVM may move it after the
// exception check. Therefore, we trick LLVM to check the return value of the
// function.
JavaObject* obj = JavaThread::get()->pendingException;
if (obj) return (UserClassArray*)obj;
return res;
}
// Does not call Java code.
extern "C" void jnjvmEndJNI(uint32** oldLRN, void** oldBuffer) {
JavaThread* th = JavaThread::get();
// We're going back to Java
th->endJNI();
// Update the buffer.
th->currentSjljBuffer = *oldBuffer;
// Update the number of references.
th->currentAddedReferences = *oldLRN;
#ifdef DWARF_EXCEPTIONS
// If there's an exception, throw it now.
if (JavaThread::get()->pendingException) {
th->throwPendingException();
}
#endif
}
// Never throws.
extern "C" void** jnjvmStartJNI(uint32* localReferencesNumber,
uint32** oldLocalReferencesNumber,
void* newBuffer, void** oldBuffer) {
JavaThread* th = JavaThread::get();
*oldBuffer = th->currentSjljBuffer;
th->currentSjljBuffer = newBuffer;
*oldLocalReferencesNumber = th->currentAddedReferences;
th->currentAddedReferences = localReferencesNumber;
th->startJNI(2);
void** val = (void**)th->addresses.back();
return val;
}
// Never throws.
extern "C" void jnjvmJavaObjectAquire(JavaObject* obj) {
llvm_gcroot(obj, 0);
obj->acquire();
}
// Never throws.
extern "C" void jnjvmJavaObjectRelease(JavaObject* obj) {
llvm_gcroot(obj, 0);
obj->release();
}
// Does not call any Java code.
extern "C" void jnjvmThrowException(JavaObject* obj) {
return JavaThread::get()->throwException(obj);
}
// Never throws.
extern "C" void jnjvmOverflowThinLock(JavaObject* obj) {
obj->overflowThinLock();
}
// Creates a Java object and then throws it.
extern "C" JavaObject* jnjvmNullPointerException() {
JavaObject *exc = 0;
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
exc = th->getJVM()->CreateNullPointerException();
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
return exc;
}
// Creates a Java object and then throws it.
extern "C" JavaObject* jnjvmNegativeArraySizeException(sint32 val) {
JavaObject *exc = 0;
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
exc = th->getJVM()->CreateNegativeArraySizeException();
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
return exc;
}
// Creates a Java object and then throws it.
extern "C" JavaObject* jnjvmOutOfMemoryError(sint32 val) {
JavaObject *exc = 0;
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
exc = th->getJVM()->CreateOutOfMemoryError();
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
return exc;
}
// Creates a Java object and then throws it.
extern "C" JavaObject* jnjvmStackOverflowError() {
JavaObject *exc = 0;
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
exc = th->getJVM()->CreateStackOverflowError();
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
return exc;
}
// Creates a Java object and then throws it.
extern "C" JavaObject* jnjvmArithmeticException() {
JavaObject *exc = 0;
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
exc = th->getJVM()->CreateArithmeticException();
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
return exc;
}
// Creates a Java object and then throws it.
extern "C" JavaObject* jnjvmClassCastException(JavaObject* obj,
UserCommonClass* cl) {
JavaObject *exc = 0;
llvm_gcroot(obj, 0);
llvm_gcroot(exc, 0);
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
exc = th->getJVM()->CreateClassCastException(obj, cl);
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
return exc;
}
// Creates a Java object and then throws it.
extern "C" JavaObject* jnjvmIndexOutOfBoundsException(JavaObject* obj,
sint32 index) {
JavaObject *exc = 0;
llvm_gcroot(obj, 0);
llvm_gcroot(exc, 0);
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
exc = th->getJVM()->CreateIndexOutOfBoundsException(index);
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
return exc;
}
// Creates a Java object and then throws it.
extern "C" JavaObject* jnjvmArrayStoreException(JavaVirtualTable* VT) {
JavaObject *exc = 0;
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
exc = th->getJVM()->CreateArrayStoreException(VT);
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
return exc;
}
// Create an exception then throws it.
extern "C" void jnjvmThrowExceptionFromJIT() {
JavaObject *exc = 0;
JavaThread *th = JavaThread::get();
BEGIN_NATIVE_EXCEPTION(1)
JavaMethod* meth = th->getCallingMethod();
exc = th->getJVM()->CreateUnsatisfiedLinkError(meth);
END_NATIVE_EXCEPTION
#ifdef DWARF_EXCEPTIONS
th->throwException(exc);
#else
th->pendingException = exc;
#endif
}
extern "C" void* jnjvmStringLookup(UserClass* cl, uint32 index) {
UserConstantPool* ctpInfo = cl->getConstantPool();
const UTF8* utf8 = ctpInfo->UTF8At(ctpInfo->ctpDef[index]);
JavaString** str = cl->classLoader->UTF8ToStr(utf8);
#if defined(ISOLATE_SHARING) || !defined(ISOLATE)
ctpInfo->ctpRes[index] = str;
#endif
return (void*)str;
}
extern "C" void jnjvmPrintMethodStart(JavaMethod* meth) {
fprintf(stderr, "[%p] executing %s.%s\n", (void*)mvm::Thread::get(),
UTF8Buffer(meth->classDef->name).cString(),
UTF8Buffer(meth->name).cString());
}
extern "C" void jnjvmPrintMethodEnd(JavaMethod* meth) {
fprintf(stderr, "[%p] return from %s.%s\n", (void*)mvm::Thread::get(),
UTF8Buffer(meth->classDef->name).cString(),
UTF8Buffer(meth->name).cString());
}
extern "C" void jnjvmPrintExecution(uint32 opcode, uint32 index,
JavaMethod* meth) {
fprintf(stderr, "[%p] executing %s.%s %s at %d\n", (void*)mvm::Thread::get(),
UTF8Buffer(meth->classDef->name).cString(),
UTF8Buffer(meth->name).cString(),
OpcodeNames[opcode], index);
}
#ifdef SERVICE
extern "C" void jnjvmServiceCallStart(Jnjvm* OldService,
Jnjvm* NewService) {
fprintf(stderr, "I have switched from %d to %d\n", OldService->IsolateID,
NewService->IsolateID);
fprintf(stderr, "Now the thread id is %d\n", mvm::Thread::get()->IsolateID);
}
extern "C" void jnjvmServiceCallStop(Jnjvm* OldService,
Jnjvm* NewService) {
fprintf(stderr, "End service call\n");
}
#endif
#ifdef ISOLATE_SHARING
extern "C" void* jnjvmEnveloppeLookup(UserClass* cl, uint32 index) {
UserConstantPool* ctpInfo = cl->getConstantPool();
mvm::Allocator* allocator = cl->classLoader->allocator;
Enveloppe* enveloppe = new(allocator) Enveloppe(ctpInfo, index);
ctpInfo->ctpRes[index] = enveloppe;
return (void*)enveloppe;
}
extern "C" void* jnjvmStaticCtpLookup(UserClass* cl, uint32 index) {
UserConstantPool* ctpInfo = cl->getConstantPool();
JavaConstantPool* shared = ctpInfo->getSharedPool();
uint32 clIndex = shared->getClassIndexFromMethod(index);
UserClass* refCl = (UserClass*)ctpInfo->loadClass(clIndex);
refCl->initialiseClass(JavaThread::get()->getJVM());
CommonClass* baseCl = 0;
const UTF8* utf8 = 0;
Signdef* sign = 0;
shared->resolveMethod(index, baseCl, utf8, sign);
UserClass* methodCl = 0;
refCl->lookupMethod(utf8, sign->keyName, true, true, &methodCl);
ctpInfo->ctpRes[index] = methodCl->getConstantPool();
shared->ctpRes[clIndex] = refCl->classDef;
return (void*)methodCl->getConstantPool();
}
extern "C" UserConstantPool* jnjvmSpecialCtpLookup(UserConstantPool* ctpInfo,
uint32 index,
UserConstantPool** res) {
JavaConstantPool* shared = ctpInfo->getSharedPool();
uint32 clIndex = shared->getClassIndexFromMethod(index);
UserClass* refCl = (UserClass*)ctpInfo->loadClass(clIndex);
CommonClass* baseCl = 0;
const UTF8* utf8 = 0;
Signdef* sign = 0;
shared->resolveMethod(index, baseCl, utf8, sign);
UserClass* methodCl = 0;
refCl->lookupMethod(utf8, sign->keyName, false, true, &methodCl);
shared->ctpRes[clIndex] = refCl->classDef;
*res = methodCl->getConstantPool();
return methodCl->getConstantPool();
}
#endif