| //===-------- JavaClass.cpp - Java class representation -------------------===// |
| // |
| // 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 "debug.h" |
| #include "types.h" |
| |
| #include "ClasspathReflect.h" |
| #include "JavaArray.h" |
| #include "JavaClass.h" |
| #include "JavaCompiler.h" |
| #include "JavaConstantPool.h" |
| #include "JavaObject.h" |
| #include "JavaThread.h" |
| #include "JavaTypes.h" |
| #include "JavaUpcalls.h" |
| #include "Jnjvm.h" |
| #include "LockedMap.h" |
| #include "Reader.h" |
| |
| #include <cstring> |
| |
| using namespace j3; |
| |
| const UTF8* Attribut::annotationsAttribut = 0; |
| const UTF8* Attribut::codeAttribut = 0; |
| const UTF8* Attribut::exceptionsAttribut = 0; |
| const UTF8* Attribut::constantAttribut = 0; |
| const UTF8* Attribut::lineNumberTableAttribut = 0; |
| const UTF8* Attribut::innerClassesAttribut = 0; |
| const UTF8* Attribut::sourceFileAttribut = 0; |
| |
| Class* ClassArray::SuperArray; |
| Class** ClassArray::InterfacesArray; |
| |
| extern "C" void JavaArrayTracer(JavaObject*); |
| extern "C" void JavaObjectTracer(JavaObject*); |
| extern "C" void ArrayObjectTracer(JavaObject*); |
| extern "C" void RegularObjectTracer(JavaObject*); |
| |
| Attribut::Attribut(const UTF8* name, uint32 length, |
| uint32 offset) { |
| |
| this->start = offset; |
| this->nbb = length; |
| this->name = name; |
| |
| } |
| |
| Attribut* Class::lookupAttribut(const UTF8* key ) { |
| for (uint32 i = 0; i < nbAttributs; ++i) { |
| Attribut* cur = &(attributs[i]); |
| if (cur->name->equals(key)) return cur; |
| } |
| |
| return 0; |
| } |
| |
| Attribut* JavaField::lookupAttribut(const UTF8* key ) { |
| for (uint32 i = 0; i < nbAttributs; ++i) { |
| Attribut* cur = &(attributs[i]); |
| if (cur->name->equals(key)) return cur; |
| } |
| |
| return 0; |
| } |
| |
| Attribut* JavaMethod::lookupAttribut(const UTF8* key ) { |
| for (uint32 i = 0; i < nbAttributs; ++i) { |
| Attribut* cur = &(attributs[i]); |
| if (cur->name->equals(key)) return cur; |
| } |
| |
| return 0; |
| } |
| |
| CommonClass::~CommonClass() { |
| } |
| |
| Class::~Class() { |
| for (uint32 i = 0; i < nbAttributs; ++i) { |
| Attribut* cur = &(attributs[i]); |
| cur->~Attribut(); |
| classLoader->allocator.Deallocate(cur); |
| } |
| |
| for (uint32 i = 0; i < nbStaticFields; ++i) { |
| JavaField* cur = &(staticFields[i]); |
| cur->~JavaField(); |
| classLoader->allocator.Deallocate(cur); |
| } |
| |
| for (uint32 i = 0; i < nbVirtualFields; ++i) { |
| JavaField* cur = &(virtualFields[i]); |
| cur->~JavaField(); |
| classLoader->allocator.Deallocate(cur); |
| } |
| |
| for (uint32 i = 0; i < nbVirtualMethods; ++i) { |
| JavaMethod* cur = &(virtualMethods[i]); |
| cur->~JavaMethod(); |
| classLoader->allocator.Deallocate(cur); |
| } |
| |
| for (uint32 i = 0; i < nbStaticMethods; ++i) { |
| JavaMethod* cur = &(staticMethods[i]); |
| cur->~JavaMethod(); |
| classLoader->allocator.Deallocate(cur); |
| } |
| |
| if (ctpInfo) { |
| ctpInfo->~JavaConstantPool(); |
| classLoader->allocator.Deallocate(ctpInfo); |
| } |
| |
| classLoader->allocator.Deallocate(IsolateInfo); |
| |
| // Currently, only regular classes have a heap allocated virtualVT. |
| // Array classes have a C++ allocated virtualVT and primitive classes |
| // do not have a virtualVT. |
| classLoader->allocator.Deallocate(virtualVT); |
| } |
| |
| JavaField::~JavaField() { |
| for (uint32 i = 0; i < nbAttributs; ++i) { |
| Attribut* cur = &(attributs[i]); |
| cur->~Attribut(); |
| classDef->classLoader->allocator.Deallocate(cur); |
| } |
| } |
| |
| JavaMethod::~JavaMethod() { |
| |
| for (uint32 i = 0; i < nbAttributs; ++i) { |
| Attribut* cur = &(attributs[i]); |
| cur->~Attribut(); |
| classDef->classLoader->allocator.Deallocate(cur); |
| } |
| |
| } |
| |
| UserClassPrimitive* CommonClass::toPrimitive(Jnjvm* vm) const { |
| if (this == vm->upcalls->voidClass) { |
| return vm->upcalls->OfVoid; |
| } else if (this == vm->upcalls->intClass) { |
| return vm->upcalls->OfInt; |
| } else if (this == vm->upcalls->shortClass) { |
| return vm->upcalls->OfShort; |
| } else if (this == vm->upcalls->charClass) { |
| return vm->upcalls->OfChar; |
| } else if (this == vm->upcalls->doubleClass) { |
| return vm->upcalls->OfDouble; |
| } else if (this == vm->upcalls->byteClass) { |
| return vm->upcalls->OfByte; |
| } else if (this == vm->upcalls->boolClass) { |
| return vm->upcalls->OfBool; |
| } else if (this == vm->upcalls->longClass) { |
| return vm->upcalls->OfLong; |
| } else if (this == vm->upcalls->floatClass) { |
| return vm->upcalls->OfFloat; |
| } else { |
| return 0; |
| } |
| } |
| |
| |
| UserClassPrimitive* |
| ClassPrimitive::byteIdToPrimitive(char id, Classpath* upcalls) { |
| switch (id) { |
| case I_FLOAT : |
| return upcalls->OfFloat; |
| case I_INT : |
| return upcalls->OfInt; |
| case I_SHORT : |
| return upcalls->OfShort; |
| case I_CHAR : |
| return upcalls->OfChar; |
| case I_DOUBLE : |
| return upcalls->OfDouble; |
| case I_BYTE : |
| return upcalls->OfByte; |
| case I_BOOL : |
| return upcalls->OfBool; |
| case I_LONG : |
| return upcalls->OfLong; |
| case I_VOID : |
| return upcalls->OfVoid; |
| default : |
| return 0; |
| } |
| } |
| |
| CommonClass::CommonClass(JnjvmClassLoader* loader, const UTF8* n) { |
| name = n; |
| classLoader = loader; |
| nbInterfaces = 0; |
| interfaces = 0; |
| access = 0; |
| super = 0; |
| memset(delegatee, 0, sizeof(JavaObject*) * NR_ISOLATES); |
| } |
| |
| ClassPrimitive::ClassPrimitive(JnjvmClassLoader* loader, const UTF8* n, |
| uint32 nb) : |
| CommonClass(loader, n) { |
| |
| uint32 size = JavaVirtualTable::getBaseSize(); |
| virtualVT = new(loader->allocator, size) JavaVirtualTable(this); |
| access = ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC | JNJVM_PRIMITIVE; |
| logSize = nb; |
| } |
| |
| Class::Class(JnjvmClassLoader* loader, const UTF8* n, ArrayUInt8* B) : |
| CommonClass(loader, n) { |
| virtualVT = 0; |
| bytes = B; |
| super = 0; |
| ctpInfo = 0; |
| outerClass = 0; |
| innerOuterResolved = false; |
| nbInnerClasses = 0; |
| nbVirtualMethods = 0; |
| nbStaticMethods = 0; |
| nbStaticFields = 0; |
| nbVirtualFields = 0; |
| virtualMethods = 0; |
| staticMethods = 0; |
| virtualFields = 0; |
| staticFields = 0; |
| ownerClass = 0; |
| innerAccess = 0; |
| access = JNJVM_CLASS; |
| memset(IsolateInfo, 0, sizeof(TaskClassMirror) * NR_ISOLATES); |
| } |
| |
| ClassArray::ClassArray(JnjvmClassLoader* loader, const UTF8* n, |
| UserCommonClass* base) : CommonClass(loader, n) { |
| _baseClass = base; |
| super = ClassArray::SuperArray; |
| interfaces = ClassArray::InterfacesArray; |
| nbInterfaces = 2; |
| |
| uint32 size = JavaVirtualTable::getBaseSize(); |
| virtualVT = new(loader->allocator, size) JavaVirtualTable(this); |
| |
| access = ACC_FINAL | ACC_ABSTRACT | ACC_PUBLIC | JNJVM_ARRAY; |
| } |
| |
| JavaArray* UserClassArray::doNew(sint32 n, Jnjvm* vm) { |
| if (n < 0) |
| vm->negativeArraySizeException(n); |
| else if (n > JavaArray::MaxArraySize) |
| vm->outOfMemoryError(); |
| |
| return doNew(n); |
| } |
| |
| JavaArray* UserClassArray::doNew(sint32 n) { |
| UserCommonClass* cl = baseClass(); |
| |
| uint32 logSize = cl->isPrimitive() ? |
| cl->asPrimitiveClass()->logSize : (sizeof(JavaObject*) == 8 ? 3 : 2); |
| VirtualTable* VT = virtualVT; |
| uint32 size = sizeof(JavaObject) + sizeof(ssize_t) + (n << logSize); |
| JavaArray* res = (JavaArray*)gc::operator new(size, VT); |
| res->size = n; |
| return res; |
| } |
| |
| JavaArray* UserClassArray::doNew(sint32 n, mvm::BumpPtrAllocator& allocator, |
| bool temp) { |
| UserCommonClass* cl = baseClass(); |
| |
| uint32 logSize = cl->isPrimitive() ? |
| cl->asPrimitiveClass()->logSize : (sizeof(JavaObject*) == 8 ? 3 : 2); |
| VirtualTable* VT = virtualVT; |
| uint32 size = sizeof(JavaObject) + sizeof(ssize_t) + (n << logSize); |
| |
| JavaArray* res = 0; |
| |
| // If the array is not temporary, use the allocator. |
| if (!temp) { |
| res = (JavaArray*)allocator.Allocate(size, "Array"); |
| } else { |
| // Otherwise, allocate with the malloc |
| res = (JavaArray*)malloc(size); |
| } |
| ((void**)res)[0] = VT; |
| res->size = n; |
| return res; |
| } |
| |
| void* JavaMethod::compiledPtr() { |
| if (code != 0) return code; |
| else { |
| #ifdef SERVICE |
| Jnjvm *vm = classDef->classLoader->getIsolate(); |
| if (vm && vm->status == 0) { |
| JavaThread* th = JavaThread::get(); |
| th->throwException(th->ServiceException); |
| } |
| #endif |
| code = classDef->classLoader->getCompiler()->materializeFunction(this); |
| } |
| |
| return code; |
| } |
| |
| void JavaStaticMethodInfo::print(void* ip, void* addr) { |
| void* new_ip = NULL; |
| if (ip) new_ip = mvm::MethodInfo::isStub(ip, addr); |
| fprintf(stderr, "; %p in %s.%s", new_ip, |
| UTF8Buffer(meth->classDef->name).cString(), |
| UTF8Buffer(meth->name).cString()); |
| if (ip != new_ip) fprintf(stderr, " (from stub)"); |
| fprintf(stderr, "\n"); |
| } |
| |
| void JavaMethod::setCompiledPtr(void* ptr, const char* name) { |
| classDef->acquire(); |
| if (code == 0) { |
| code = ptr; |
| Jnjvm* vm = JavaThread::get()->getJVM(); |
| JavaStaticMethodInfo* MI = |
| new (classDef->classLoader->allocator, "JavaStaticMethodInfo") |
| JavaStaticMethodInfo(0, code, this); |
| vm->StaticFunctions.addMethodInfo(MI, code); |
| classDef->classLoader->getCompiler()->setMethod(this, ptr, name); |
| } |
| access |= ACC_NATIVE; |
| classDef->release(); |
| } |
| |
| void JavaVirtualTable::setNativeTracer(uintptr_t ptr, const char* name) { |
| tracer = ptr; |
| } |
| |
| void JavaVirtualTable::setNativeDestructor(uintptr_t ptr, const char* name) { |
| if (!cl->classLoader->getCompiler()->isStaticCompiling()) { |
| destructor = ptr; |
| operatorDelete = ptr; |
| } |
| } |
| |
| JavaMethod* Class::lookupInterfaceMethodDontThrow(const UTF8* name, |
| const UTF8* type) { |
| JavaMethod* cur = lookupMethodDontThrow(name, type, false, false, 0); |
| if (!cur) { |
| for (uint16 i = 0; i < nbInterfaces; ++i) { |
| Class* I = interfaces[i]; |
| cur = I->lookupInterfaceMethodDontThrow(name, type); |
| if (cur) return cur; |
| } |
| } |
| return cur; |
| } |
| |
| JavaMethod* Class::lookupSpecialMethodDontThrow(const UTF8* name, |
| const UTF8* type, |
| Class* current) { |
| JavaMethod* meth = lookupMethodDontThrow(name, type, false, true, NULL); |
| |
| if (isSuper(current->access) && |
| current != meth->classDef && |
| meth->classDef->isAssignableFrom(current) && |
| !name->equals(classLoader->bootstrapLoader->initName)) { |
| meth = current->super->lookupMethodDontThrow(name, type, false, true, NULL); |
| } |
| |
| return meth; |
| } |
| |
| JavaMethod* Class::lookupMethodDontThrow(const UTF8* name, const UTF8* type, |
| bool isStatic, bool recurse, |
| Class** methodCl) { |
| |
| JavaMethod* methods = 0; |
| uint32 nb = 0; |
| if (isStatic) { |
| methods = getStaticMethods(); |
| nb = nbStaticMethods; |
| } else { |
| methods = getVirtualMethods(); |
| nb = nbVirtualMethods; |
| } |
| |
| for (uint32 i = 0; i < nb; ++i) { |
| JavaMethod& res = methods[i]; |
| if (res.name->equals(name) && res.type->equals(type)) { |
| if (methodCl) *methodCl = (Class*)this; |
| return &res; |
| } |
| } |
| |
| JavaMethod *cur = 0; |
| |
| if (recurse) { |
| if (super) cur = super->lookupMethodDontThrow(name, type, isStatic, |
| recurse, methodCl); |
| if (cur) return cur; |
| if (isStatic) { |
| for (uint16 i = 0; i < nbInterfaces; ++i) { |
| Class* I = interfaces[i]; |
| cur = I->lookupMethodDontThrow(name, type, isStatic, recurse, |
| methodCl); |
| if (cur) return cur; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| JavaMethod* Class::lookupMethod(const UTF8* name, const UTF8* type, |
| bool isStatic, bool recurse, |
| Class** methodCl) { |
| JavaMethod* res = lookupMethodDontThrow(name, type, isStatic, recurse, |
| methodCl); |
| if (!res) { |
| JavaThread::get()->getJVM()->noSuchMethodError(this, name); |
| } |
| return res; |
| } |
| |
| JavaField* |
| Class::lookupFieldDontThrow(const UTF8* name, const UTF8* type, |
| bool isStatic, bool recurse, |
| Class** definingClass) { |
| JavaField* fields = 0; |
| uint32 nb = 0; |
| if (isStatic) { |
| fields = getStaticFields(); |
| nb = nbStaticFields; |
| } else { |
| fields = getVirtualFields(); |
| nb = nbVirtualFields; |
| } |
| |
| for (uint32 i = 0; i < nb; ++i) { |
| JavaField& res = fields[i]; |
| if (res.name->equals(name) && res.type->equals(type)) { |
| if (definingClass) *definingClass = this; |
| return &res; |
| } |
| } |
| |
| JavaField *cur = 0; |
| |
| if (recurse) { |
| if (super) cur = super->lookupFieldDontThrow(name, type, isStatic, |
| recurse, definingClass); |
| if (cur) return cur; |
| if (isStatic) { |
| for (uint16 i = 0; i < nbInterfaces; ++i) { |
| Class* I = interfaces[i]; |
| cur = I->lookupFieldDontThrow(name, type, isStatic, recurse, |
| definingClass); |
| if (cur) return cur; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| JavaField* Class::lookupField(const UTF8* name, const UTF8* type, |
| bool isStatic, bool recurse, |
| Class** definingClass) { |
| |
| JavaField* res = lookupFieldDontThrow(name, type, isStatic, recurse, |
| definingClass); |
| if (!res) { |
| JavaThread::get()->getJVM()->noSuchFieldError(this, name); |
| } |
| return res; |
| } |
| |
| JavaObject* UserClass::doNew(Jnjvm* vm) { |
| JavaObject* res = 0; |
| llvm_gcroot(res, 0); |
| assert(this && "No class when allocating."); |
| assert((this->isInitializing() || |
| classLoader->getCompiler()->isStaticCompiling()) |
| && "Uninitialized class when allocating."); |
| assert(getVirtualVT() && "No VT\n"); |
| res = (JavaObject*)gc::operator new(getVirtualSize(), getVirtualVT()); |
| |
| return res; |
| } |
| |
| bool UserCommonClass::inheritName(const uint16* buf, uint32 len) { |
| if (getName()->equals(buf, len)) { |
| return true; |
| } else if (isPrimitive()) { |
| return false; |
| } else if (super) { |
| if (getSuper()->inheritName(buf, len)) return true; |
| } |
| |
| for (uint32 i = 0; i < nbInterfaces; ++i) { |
| if (interfaces[i]->inheritName(buf, len)) return true; |
| } |
| return false; |
| } |
| |
| bool UserCommonClass::isOfTypeName(const UTF8* Tname) { |
| if (inheritName(Tname->elements, Tname->size)) { |
| return true; |
| } else if (isArray()) { |
| UserCommonClass* curS = this; |
| uint32 prof = 0; |
| uint32 len = Tname->size; |
| bool res = true; |
| |
| while (res && Tname->elements[prof] == I_TAB) { |
| UserCommonClass* cl = ((UserClassArray*)curS)->baseClass(); |
| ++prof; |
| if (cl->isClass()) cl->asClass()->resolveClass(); |
| res = curS->isArray() && cl && (prof < len); |
| curS = cl; |
| } |
| |
| return (Tname->elements[prof] == I_REF) && |
| (res && curS->inheritName(&(Tname->elements[prof + 1]), len - 1)); |
| } else { |
| return false; |
| } |
| } |
| |
| bool UserCommonClass::isAssignableFrom(UserCommonClass* cl) { |
| assert(virtualVT && cl->virtualVT); |
| return virtualVT->isSubtypeOf(cl->virtualVT); |
| } |
| |
| bool JavaVirtualTable::isSubtypeOf(JavaVirtualTable* otherVT) { |
| |
| assert(this); |
| assert(otherVT); |
| if (otherVT == ((JavaVirtualTable**)this)[otherVT->offset]) return true; |
| else if (otherVT->offset != getCacheIndex()) return false; |
| else if (this == otherVT) return true; |
| else { |
| for (uint32 i = 0; i < nbSecondaryTypes; ++i) { |
| if (secondaryTypes[i] == otherVT) { |
| cache = otherVT; |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| void JavaField::InitField(void* obj, uint64 val) { |
| |
| Typedef* type = getSignature(); |
| if (!type->isPrimitive()) { |
| ((JavaObject**)((uint64)obj + ptrOffset))[0] = (JavaObject*)val; |
| return; |
| } |
| |
| PrimitiveTypedef* prim = (PrimitiveTypedef*)type; |
| if (prim->isLong()) { |
| ((sint64*)((uint64)obj + ptrOffset))[0] = val; |
| } else if (prim->isInt()) { |
| ((sint32*)((uint64)obj + ptrOffset))[0] = (sint32)val; |
| } else if (prim->isChar()) { |
| ((uint16*)((uint64)obj + ptrOffset))[0] = (uint16)val; |
| } else if (prim->isShort()) { |
| ((sint16*)((uint64)obj + ptrOffset))[0] = (sint16)val; |
| } else if (prim->isByte()) { |
| ((sint8*)((uint64)obj + ptrOffset))[0] = (sint8)val; |
| } else if (prim->isBool()) { |
| ((uint8*)((uint64)obj + ptrOffset))[0] = (uint8)val; |
| } else { |
| // 0 value for everything else |
| ((sint32*)((uint64)obj + ptrOffset))[0] = (sint32)val; |
| } |
| } |
| |
| void JavaField::InitField(void* obj, JavaObject* val) { |
| ((JavaObject**)((uint64)obj + ptrOffset))[0] = val; |
| } |
| |
| void JavaField::InitField(void* obj, double val) { |
| ((double*)((uint64)obj + ptrOffset))[0] = val; |
| } |
| |
| void JavaField::InitField(void* obj, float val) { |
| ((float*)((uint64)obj + ptrOffset))[0] = val; |
| } |
| |
| void JavaField::initField(void* obj, Jnjvm* vm) { |
| const Typedef* type = getSignature(); |
| Attribut* attribut = lookupAttribut(Attribut::constantAttribut); |
| |
| if (!attribut) { |
| InitField(obj); |
| } else { |
| Reader reader(attribut, &(classDef->bytes)); |
| JavaConstantPool * ctpInfo = classDef->ctpInfo; |
| uint16 idx = reader.readU2(); |
| if (type->isPrimitive()) { |
| UserCommonClass* cl = type->assocClass(vm->bootstrapLoader); |
| if (cl == vm->upcalls->OfLong) { |
| InitField(obj, (uint64)ctpInfo->LongAt(idx)); |
| } else if (cl == vm->upcalls->OfDouble) { |
| InitField(obj, ctpInfo->DoubleAt(idx)); |
| } else if (cl == vm->upcalls->OfFloat) { |
| InitField(obj, ctpInfo->FloatAt(idx)); |
| } else { |
| InitField(obj, (uint64)ctpInfo->IntegerAt(idx)); |
| } |
| } else if (type->isReference()){ |
| const UTF8* utf8 = ctpInfo->UTF8At(ctpInfo->ctpDef[idx]); |
| InitField(obj, (JavaObject*)ctpInfo->resolveString(utf8, idx)); |
| } else { |
| fprintf(stderr, "I haven't verified your class file and it's malformed:" |
| " unknown constant %s!\n", |
| UTF8Buffer(type->keyName).cString()); |
| abort(); |
| } |
| } |
| } |
| |
| void* UserClass::allocateStaticInstance(Jnjvm* vm) { |
| #ifdef USE_GC_BOEHM |
| void* val = GC_MALLOC(getStaticSize()); |
| #else |
| void* val = classLoader->allocator.Allocate(getStaticSize(), |
| "Static instance"); |
| #endif |
| setStaticInstance(val); |
| return val; |
| } |
| |
| |
| void JavaMethod::initialise(Class* cl, const UTF8* N, const UTF8* T, uint16 A) { |
| name = N; |
| type = T; |
| classDef = cl; |
| _signature = 0; |
| code = 0; |
| access = A; |
| canBeInlined = false; |
| offset = 0; |
| } |
| |
| void JavaField::initialise(Class* cl, const UTF8* N, const UTF8* T, uint16 A) { |
| name = N; |
| type = T; |
| classDef = cl; |
| _signature = 0; |
| ptrOffset = 0; |
| access = A; |
| } |
| |
| void Class::readParents(Reader& reader) { |
| uint16 superEntry = reader.readU2(); |
| if (superEntry) { |
| const UTF8* superUTF8 = ctpInfo->resolveClassName(superEntry); |
| super = classLoader->loadName(superUTF8, false, true); |
| } |
| |
| uint16 nbI = reader.readU2(); |
| |
| interfaces = (Class**) |
| classLoader->allocator.Allocate(nbI * sizeof(Class*), "Interfaces"); |
| |
| // Do not set nbInterfaces yet, we may be interrupted by the GC |
| // in anon-cooperative environment. |
| for (int i = 0; i < nbI; i++) { |
| const UTF8* name = ctpInfo->resolveClassName(reader.readU2()); |
| interfaces[i] = classLoader->loadName(name, false, true); |
| } |
| nbInterfaces = nbI; |
| |
| } |
| |
| void UserClass::loadParents() { |
| if (super == 0) { |
| virtualTableSize = JavaVirtualTable::getFirstJavaMethodIndex(); |
| } else { |
| super->resolveClass(); |
| virtualTableSize = super->virtualTableSize; |
| } |
| |
| for (unsigned i = 0; i < nbInterfaces; i++) |
| interfaces[i]->resolveClass(); |
| } |
| |
| |
| static void internalLoadExceptions(JavaMethod& meth) { |
| |
| Attribut* codeAtt = meth.lookupAttribut(Attribut::codeAttribut); |
| |
| if (codeAtt) { |
| Reader reader(codeAtt, &(meth.classDef->bytes)); |
| //uint16 maxStack = |
| reader.readU2(); |
| //uint16 maxLocals = |
| reader.readU2(); |
| uint16 codeLen = reader.readU4(); |
| |
| reader.seek(codeLen, Reader::SeekCur); |
| |
| uint16 nbe = reader.readU2(); |
| for (uint16 i = 0; i < nbe; ++i) { |
| //startpc = |
| reader.readU2(); |
| //endpc= |
| reader.readU2(); |
| //handlerpc = |
| reader.readU2(); |
| |
| uint16 catche = reader.readU2(); |
| if (catche) meth.classDef->ctpInfo->loadClass(catche); |
| } |
| } |
| } |
| |
| void UserClass::loadExceptions() { |
| for (uint32 i = 0; i < nbVirtualMethods; ++i) |
| internalLoadExceptions(virtualMethods[i]); |
| |
| for (uint32 i = 0; i < nbStaticMethods; ++i) |
| internalLoadExceptions(staticMethods[i]); |
| } |
| |
| Attribut* Class::readAttributs(Reader& reader, uint16& size) { |
| uint16 nba = reader.readU2(); |
| |
| Attribut* attributs = new(classLoader->allocator, "Attributs") Attribut[nba]; |
| |
| for (int i = 0; i < nba; i++) { |
| const UTF8* attName = ctpInfo->UTF8At(reader.readU2()); |
| uint32 attLen = reader.readU4(); |
| Attribut& att = attributs[i]; |
| att.start = reader.cursor; |
| att.nbb = attLen; |
| att.name = attName; |
| reader.seek(attLen, Reader::SeekCur); |
| } |
| |
| size = nba; |
| return attributs; |
| } |
| |
| void Class::readFields(Reader& reader) { |
| uint16 nbFields = reader.readU2(); |
| virtualFields = new (classLoader->allocator, "Fields") JavaField[nbFields]; |
| staticFields = virtualFields + nbFields; |
| for (int i = 0; i < nbFields; i++) { |
| uint16 access = reader.readU2(); |
| const UTF8* name = ctpInfo->UTF8At(reader.readU2()); |
| const UTF8* type = ctpInfo->UTF8At(reader.readU2()); |
| JavaField* field = 0; |
| if (isStatic(access)) { |
| --staticFields; |
| field = &(staticFields[0]); |
| field->initialise(this, name, type, access); |
| ++nbStaticFields; |
| } else { |
| field = &(virtualFields[nbVirtualFields]); |
| field->initialise(this, name, type, access); |
| ++nbVirtualFields; |
| } |
| field->attributs = readAttributs(reader, field->nbAttributs); |
| } |
| } |
| |
| void Class::fillIMT(std::set<JavaMethod*>* meths) { |
| for (uint32 i = 0; i < nbInterfaces; ++i) { |
| interfaces[i]->fillIMT(meths); |
| } |
| |
| if (super) super->fillIMT(meths); |
| |
| if (isInterface()) { |
| for (uint32 i = 0; i < nbVirtualMethods; ++i) { |
| JavaMethod& meth = virtualMethods[i]; |
| uint32_t index = InterfaceMethodTable::getIndex(meth.name, meth.type); |
| if (meths[index].find(&meth) == meths[index].end()) |
| meths[index].insert(&meth); |
| } |
| } |
| } |
| |
| void Class::makeVT() { |
| |
| for (uint32 i = 0; i < nbVirtualMethods; ++i) { |
| JavaMethod& meth = virtualMethods[i]; |
| if (meth.name->equals(classLoader->bootstrapLoader->finalize) && |
| meth.type->equals(classLoader->bootstrapLoader->clinitType)) { |
| meth.offset = 0; |
| } else { |
| JavaMethod* parent = super? |
| super->lookupMethodDontThrow(meth.name, meth.type, false, true, 0) : |
| 0; |
| |
| uint64_t offset = 0; |
| if (!parent) { |
| offset = virtualTableSize++; |
| meth.offset = offset; |
| } else { |
| offset = parent->offset; |
| meth.offset = parent->offset; |
| } |
| } |
| } |
| |
| if (super) { |
| assert(virtualTableSize >= super->virtualTableSize && |
| "Size of virtual table less than super!"); |
| } |
| |
| mvm::BumpPtrAllocator& allocator = classLoader->allocator; |
| virtualVT = new(allocator, virtualTableSize) JavaVirtualTable(this); |
| } |
| |
| |
| void Class::readMethods(Reader& reader) { |
| uint16 nbMethods = reader.readU2(); |
| virtualMethods = new(classLoader->allocator, "Methods") JavaMethod[nbMethods]; |
| staticMethods = virtualMethods + nbMethods; |
| for (int i = 0; i < nbMethods; i++) { |
| uint16 access = reader.readU2(); |
| const UTF8* name = ctpInfo->UTF8At(reader.readU2()); |
| const UTF8* type = ctpInfo->UTF8At(reader.readU2()); |
| JavaMethod* meth = 0; |
| if (isStatic(access)) { |
| --staticMethods; |
| meth = &(staticMethods[0]); |
| meth->initialise(this, name, type, access); |
| ++nbStaticMethods; |
| } else { |
| meth = &(virtualMethods[nbVirtualMethods]); |
| meth->initialise(this, name, type, access); |
| ++nbVirtualMethods; |
| } |
| meth->attributs = readAttributs(reader, meth->nbAttributs); |
| } |
| } |
| |
| void Class::readClass() { |
| |
| assert(getInitializationState() == loaded && "Wrong init state"); |
| |
| PRINT_DEBUG(JNJVM_LOAD, 0, COLOR_NORMAL, "; ", 0); |
| PRINT_DEBUG(JNJVM_LOAD, 0, LIGHT_GREEN, "reading ", 0); |
| PRINT_DEBUG(JNJVM_LOAD, 0, COLOR_NORMAL, "%s\n", mvm::PrintBuffer(this).cString()); |
| |
| Reader reader(&bytes); |
| uint32 magic = reader.readU4(); |
| assert(magic == Jnjvm::Magic && "I've created a class but magic is no good!"); |
| |
| /* uint16 minor = */ reader.readU2(); |
| /* uint16 major = */ reader.readU2(); |
| uint32 ctpSize = reader.readU2(); |
| ctpInfo = new(classLoader->allocator, ctpSize) JavaConstantPool(this, reader, |
| ctpSize); |
| access |= (reader.readU2() & 0x0FFF); |
| |
| if (!isPublic(access)) access |= ACC_PRIVATE; |
| |
| const UTF8* thisClassName = |
| ctpInfo->resolveClassName(reader.readU2()); |
| |
| if (!(thisClassName->equals(name))) { |
| JavaThread::get()->getJVM()->classFormatError(this, thisClassName); |
| } |
| |
| readParents(reader); |
| readFields(reader); |
| readMethods(reader); |
| attributs = readAttributs(reader, nbAttributs); |
| setIsRead(); |
| } |
| |
| #ifndef ISOLATE_SHARING |
| void Class::resolveClass() { |
| if (!isResolved() && !isErroneous()) { |
| acquire(); |
| if (isResolved() || isErroneous()) { |
| release(); |
| } else if (!isResolving()) { |
| setOwnerClass(JavaThread::get()); |
| |
| JavaObject* exc = 0; |
| try { |
| readClass(); |
| } catch (...) { |
| exc = JavaThread::get()->pendingException; |
| JavaThread::get()->clearException(); |
| } |
| |
| if (exc) { |
| setErroneous(); |
| setOwnerClass(0); |
| broadcastClass(); |
| release(); |
| JavaThread::get()->throwException(exc); |
| } |
| |
| release(); |
| |
| try { |
| loadParents(); |
| } catch (...) { |
| setInitializationState(loaded); |
| exc = JavaThread::get()->pendingException; |
| JavaThread::get()->clearException(); |
| } |
| |
| if (exc) { |
| setErroneous(); |
| setOwnerClass(0); |
| JavaThread::get()->throwException(exc); |
| } |
| |
| makeVT(); |
| JavaCompiler *Comp = classLoader->getCompiler(); |
| Comp->resolveVirtualClass(this); |
| Comp->resolveStaticClass(this); |
| loadExceptions(); |
| setResolved(); |
| if (!needsInitialisationCheck()) { |
| setInitializationState(ready); |
| } |
| if (!super) ClassArray::initialiseVT(this); |
| |
| bool needInit = needsInitialisationCheck(); |
| |
| acquire(); |
| if (needInit) setResolved(); |
| setOwnerClass(0); |
| broadcastClass(); |
| release(); |
| } else if (JavaThread::get() != getOwnerClass()) { |
| while (!isResolved()) { |
| waitClass(); |
| if (isErroneous()) break; |
| } |
| release(); |
| |
| } |
| } |
| |
| if (isErroneous()) { |
| JavaThread* th = JavaThread::get(); |
| th->getJVM()->noClassDefFoundError(name); |
| } |
| |
| assert(virtualVT && "No virtual VT after resolution"); |
| } |
| #else |
| void Class::resolveClass() { |
| assert(status >= resolved && |
| "Asking to resolve a not resolved-class in a isolate environment"); |
| } |
| #endif |
| |
| void UserClass::resolveInnerOuterClasses() { |
| if (!innerOuterResolved) { |
| Attribut* attribut = lookupAttribut(Attribut::innerClassesAttribut); |
| if (attribut != 0) { |
| Reader reader(attribut, getBytesPtr()); |
| uint16 nbi = reader.readU2(); |
| for (uint16 i = 0; i < nbi; ++i) { |
| uint16 inner = reader.readU2(); |
| uint16 outer = reader.readU2(); |
| uint16 innerName = reader.readU2(); |
| uint16 accessFlags = reader.readU2(); |
| UserClass* clInner = 0; |
| UserClass* clOuter = 0; |
| if (inner) clInner = (UserClass*)ctpInfo->loadClass(inner); |
| if (outer) clOuter = (UserClass*)ctpInfo->loadClass(outer); |
| |
| if (clInner == this) { |
| outerClass = clOuter; |
| } else if (clOuter == this) { |
| if (!innerClasses) { |
| innerClasses = (Class**) |
| classLoader->allocator.Allocate(nbi * sizeof(Class*), |
| "Inner classes"); |
| } |
| clInner->setInnerAccess(accessFlags); |
| if (!innerName) isAnonymous = true; |
| innerClasses[nbInnerClasses++] = clInner; |
| } |
| } |
| } |
| innerOuterResolved = true; |
| } |
| } |
| |
| static JavaObject* getClassType(Jnjvm* vm, JnjvmClassLoader* loader, |
| Typedef* type) { |
| UserCommonClass* res = type->assocClass(loader); |
| assert(res && "No associated class"); |
| return res->getClassDelegatee(vm); |
| } |
| |
| ArrayObject* JavaMethod::getParameterTypes(JnjvmClassLoader* loader) { |
| |
| ArrayObject* res = 0; |
| llvm_gcroot(res, 0); |
| |
| Jnjvm* vm = JavaThread::get()->getJVM(); |
| Signdef* sign = getSignature(); |
| Typedef* const* arguments = sign->getArgumentsType(); |
| res = (ArrayObject*)vm->upcalls->classArrayClass->doNew(sign->nbArguments,vm); |
| |
| for (uint32 index = 0; index < sign->nbArguments; ++index) { |
| res->elements[index] = getClassType(vm, loader, arguments[index]); |
| } |
| |
| return res; |
| |
| } |
| |
| JavaObject* JavaMethod::getReturnType(JnjvmClassLoader* loader) { |
| Jnjvm* vm = JavaThread::get()->getJVM(); |
| Typedef* ret = getSignature()->getReturnType(); |
| return getClassType(vm, loader, ret); |
| } |
| |
| ArrayObject* JavaMethod::getExceptionTypes(JnjvmClassLoader* loader) { |
| |
| ArrayObject* res = 0; |
| llvm_gcroot(res, 0); |
| |
| Attribut* exceptionAtt = lookupAttribut(Attribut::exceptionsAttribut); |
| Jnjvm* vm = JavaThread::get()->getJVM(); |
| if (exceptionAtt == 0) { |
| return (ArrayObject*)vm->upcalls->classArrayClass->doNew(0, vm); |
| } else { |
| UserConstantPool* ctp = classDef->getConstantPool(); |
| Reader reader(exceptionAtt, classDef->getBytesPtr()); |
| uint16 nbe = reader.readU2(); |
| res = (ArrayObject*)vm->upcalls->classArrayClass->doNew(nbe, vm); |
| |
| for (uint16 i = 0; i < nbe; ++i) { |
| uint16 idx = reader.readU2(); |
| UserCommonClass* cl = ctp->loadClass(idx); |
| assert(cl->asClass() && "Wrong exception type"); |
| cl->asClass()->resolveClass(); |
| res->elements[i] = cl->getClassDelegatee(vm); |
| } |
| return res; |
| } |
| } |
| |
| |
| #ifdef ISOLATE |
| TaskClassMirror& Class::getCurrentTaskClassMirror() { |
| return IsolateInfo[JavaThread::get()->getJVM()->IsolateID]; |
| } |
| |
| JavaObject* CommonClass::getDelegatee() { |
| return delegatee[JavaThread::get()->getJVM()->IsolateID]; |
| } |
| |
| JavaObject** CommonClass::getDelegateePtr() { |
| return &(delegatee[JavaThread::get()->getJVM()->IsolateID]); |
| } |
| |
| JavaObject* CommonClass::setDelegatee(JavaObject* val) { |
| JavaObject** obj = &(delegatee[JavaThread::get()->getJVM()->IsolateID]); |
| |
| JavaObject* prev = (JavaObject*) |
| __sync_val_compare_and_swap((uintptr_t)obj, NULL, val); |
| |
| if (!prev) return val; |
| else return prev; |
| } |
| |
| #else |
| |
| JavaObject* CommonClass::setDelegatee(JavaObject* val) { |
| JavaObject* prev = (JavaObject*) |
| __sync_val_compare_and_swap(&(delegatee[0]), NULL, val); |
| |
| if (!prev) return val; |
| else return prev; |
| } |
| |
| #endif |
| |
| |
| |
| UserCommonClass* UserCommonClass::resolvedImplClass(Jnjvm* vm, |
| JavaObject* clazz, |
| bool doClinit) { |
| |
| llvm_gcroot(clazz, 0); |
| |
| UserCommonClass* cl = ((JavaObjectClass*)clazz)->getClass(); |
| assert(cl && "No class in Class object"); |
| if (cl->isClass()) { |
| cl->asClass()->resolveClass(); |
| if (doClinit) cl->asClass()->initialiseClass(vm); |
| } |
| return cl; |
| } |
| |
| void JavaMethod::jniConsFromMeth(char* buf, const UTF8* jniConsClName, |
| const UTF8* jniConsName, |
| const UTF8* jniConsType, |
| bool synthetic) { |
| sint32 clen = jniConsClName->size; |
| sint32 mnlen = jniConsName->size; |
| |
| uint32 cur = 0; |
| char* ptr = &(buf[JNI_NAME_PRE_LEN]); |
| |
| memcpy(buf, JNI_NAME_PRE, JNI_NAME_PRE_LEN); |
| |
| for (sint32 i =0; i < clen; ++i) { |
| cur = jniConsClName->elements[i]; |
| if (cur == '/') ptr[0] = '_'; |
| else if (cur == '_') { |
| ptr[0] = '_'; |
| ptr[1] = '1'; |
| ++ptr; |
| } |
| else ptr[0] = (uint8)cur; |
| ++ptr; |
| } |
| |
| ptr[0] = '_'; |
| ++ptr; |
| |
| for (sint32 i =0; i < mnlen; ++i) { |
| cur = jniConsName->elements[i]; |
| if (cur == '/') ptr[0] = '_'; |
| else if (cur == '_') { |
| ptr[0] = '_'; |
| ptr[1] = '1'; |
| ++ptr; |
| } |
| else ptr[0] = (uint8)cur; |
| ++ptr; |
| } |
| |
| ptr[0] = 0; |
| |
| |
| } |
| |
| void JavaMethod::jniConsFromMethOverloaded(char* buf, const UTF8* jniConsClName, |
| const UTF8* jniConsName, |
| const UTF8* jniConsType, |
| bool synthetic) { |
| sint32 clen = jniConsClName->size; |
| sint32 mnlen = jniConsName->size; |
| |
| uint32 cur = 0; |
| char* ptr = &(buf[JNI_NAME_PRE_LEN]); |
| |
| memcpy(buf, JNI_NAME_PRE, JNI_NAME_PRE_LEN); |
| |
| for (sint32 i =0; i < clen; ++i) { |
| cur = jniConsClName->elements[i]; |
| if (cur == '/') { |
| ptr[0] = '_'; |
| ++ptr; |
| } else if (cur == '_') { |
| ptr[0] = '_'; |
| ptr[1] = '1'; |
| ptr += 2; |
| } else if (cur == '$') { |
| ptr[0] = '_'; |
| ptr[1] = '0'; |
| ptr[2] = '0'; |
| ptr[3] = '0'; |
| ptr[4] = '2'; |
| ptr[5] = '4'; |
| ptr += 6; |
| } else { |
| ptr[0] = (uint8)cur; |
| ++ptr; |
| } |
| } |
| |
| ptr[0] = '_'; |
| ++ptr; |
| |
| for (sint32 i =0; i < mnlen; ++i) { |
| cur = jniConsName->elements[i]; |
| if (cur == '/') ptr[0] = '_'; |
| else if (cur == '_') { |
| ptr[0] = '_'; |
| ptr[1] = '1'; |
| ptr += 2; |
| } else if (cur == '<') { |
| ptr[0] = '_'; |
| ptr[1] = '0'; |
| ptr[2] = '0'; |
| ptr[3] = '0'; |
| ptr[4] = '3'; |
| ptr[5] = 'C'; |
| ptr += 6; |
| } else if (cur == '>') { |
| ptr[0] = '_'; |
| ptr[1] = '0'; |
| ptr[2] = '0'; |
| ptr[3] = '0'; |
| ptr[4] = '3'; |
| ptr[5] = 'E'; |
| ptr += 6; |
| } else { |
| ptr[0] = (uint8)cur; |
| ++ptr; |
| } |
| } |
| |
| sint32 i = 0; |
| while (i < jniConsType->size) { |
| char c = jniConsType->elements[i++]; |
| if (c == I_PARG) { |
| ptr[0] = '_'; |
| ptr[1] = '_'; |
| ptr += 2; |
| } else if (c == '/') { |
| ptr[0] = '_'; |
| ++ptr; |
| } else if (c == '_') { |
| ptr[0] = '_'; |
| ptr[1] = '1'; |
| ptr += 2; |
| } else if (c == '$') { |
| ptr[0] = '_'; |
| ptr[1] = '0'; |
| ptr[2] = '0'; |
| ptr[3] = '0'; |
| ptr[4] = '2'; |
| ptr[5] = '4'; |
| ptr += 6; |
| } else if (c == I_END_REF) { |
| ptr[0] = '_'; |
| ptr[1] = '2'; |
| ptr += 2; |
| } else if (c == I_TAB) { |
| ptr[0] = '_'; |
| ptr[1] = '3'; |
| ptr += 2; |
| } else if (c == I_PARD) { |
| break; |
| } else { |
| ptr[0] = c; |
| ++ptr; |
| } |
| } |
| |
| if (synthetic) { |
| ptr[0] = 'S'; |
| ++ptr; |
| } |
| ptr[0] = 0; |
| |
| } |
| |
| bool UserClass::isNativeOverloaded(JavaMethod* meth) { |
| |
| for (uint32 i = 0; i < nbVirtualMethods; ++i) { |
| JavaMethod& cur = virtualMethods[i]; |
| if (&cur != meth && isNative(cur.access) && cur.name->equals(meth->name)) |
| return true; |
| } |
| |
| for (uint32 i = 0; i < nbStaticMethods; ++i) { |
| JavaMethod& cur = staticMethods[i]; |
| if (&cur != meth && isNative(cur.access) && cur.name->equals(meth->name)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| ArrayUInt16* JavaMethod::toString() const { |
| |
| Jnjvm* vm = JavaThread::get()->getJVM(); |
| uint32 size = classDef->name->size + name->size + type->size + 1; |
| ArrayUInt16* res = (ArrayUInt16*)vm->upcalls->ArrayOfChar->doNew(size, vm); |
| llvm_gcroot(res, 0); |
| |
| uint32 i = 0; |
| |
| for (sint32 j = 0; j < classDef->name->size; ++j) { |
| if (classDef->name->elements[j] == '/') res->elements[i++] = '.'; |
| else res->elements[i++] = classDef->name->elements[j]; |
| } |
| |
| res->elements[i++] = '.'; |
| |
| for (sint32 j = 0; j < name->size; ++j) { |
| res->elements[i++] = name->elements[j]; |
| } |
| |
| for (sint32 j = 0; j < type->size; ++j) { |
| res->elements[i++] = type->elements[j]; |
| } |
| |
| return res; |
| } |
| |
| bool UserClass::needsInitialisationCheck() { |
| |
| if (!isClassRead()) return true; |
| |
| if (isReady()) return false; |
| |
| if (super && super->needsInitialisationCheck()) |
| return true; |
| |
| if (nbStaticFields) return true; |
| |
| JavaMethod* meth = |
| lookupMethodDontThrow(classLoader->bootstrapLoader->clinitName, |
| classLoader->bootstrapLoader->clinitType, |
| true, false, 0); |
| |
| if (meth) return true; |
| |
| setInitializationState(ready); |
| return false; |
| } |
| |
| void ClassArray::initialiseVT(Class* javaLangObject) { |
| |
| ClassArray::SuperArray = javaLangObject; |
| JnjvmClassLoader* JCL = javaLangObject->classLoader; |
| Classpath* upcalls = JCL->bootstrapLoader->upcalls; |
| |
| assert(javaLangObject->virtualVT->init && |
| "Initializing array VT before JavaObjectVT"); |
| |
| // Load and resolve interfaces of array classes. We resolve them now |
| // so that the secondary type list of array VTs can reference them. |
| ClassArray::InterfacesArray[0] = |
| JCL->loadName(JCL->asciizConstructUTF8("java/lang/Cloneable"), |
| true, false); |
| |
| ClassArray::InterfacesArray[1] = |
| JCL->loadName(JCL->asciizConstructUTF8("java/io/Serializable"), |
| true, false); |
| |
| // Load base array classes that JnJVM internally uses. Now that the interfaces |
| // have been loaded, the secondary type can be safely created. |
| upcalls->ArrayOfObject = |
| JCL->constructArray(JCL->asciizConstructUTF8("[Ljava/lang/Object;")); |
| |
| upcalls->ArrayOfString = |
| JCL->constructArray(JCL->asciizConstructUTF8("[Ljava/lang/String;")); |
| |
| // Update native array classes. A few things have not been set properly |
| // when loading these classes because java.lang.Object and java.lang.Object[] |
| // were not loaded yet. Correct that now by updating these classes. |
| #define COPY(CLASS) \ |
| memcpy(CLASS->virtualVT->getFirstJavaMethod(), \ |
| javaLangObject->virtualVT->getFirstJavaMethod(), \ |
| sizeof(uintptr_t) * JavaVirtualTable::getNumJavaMethods()); \ |
| CLASS->super = javaLangObject; \ |
| CLASS->virtualVT->display[0] = javaLangObject->virtualVT; \ |
| CLASS->virtualVT->secondaryTypes = \ |
| upcalls->ArrayOfObject->virtualVT->secondaryTypes; \ |
| |
| COPY(upcalls->ArrayOfBool) |
| COPY(upcalls->ArrayOfByte) |
| COPY(upcalls->ArrayOfChar) |
| COPY(upcalls->ArrayOfShort) |
| COPY(upcalls->ArrayOfInt) |
| COPY(upcalls->ArrayOfFloat) |
| COPY(upcalls->ArrayOfDouble) |
| COPY(upcalls->ArrayOfLong) |
| |
| #undef COPY |
| |
| } |
| |
| JavaVirtualTable::JavaVirtualTable(Class* C) { |
| |
| if (C->super) { |
| |
| assert(C->super->virtualVT && "Super has no VT"); |
| |
| // Set the regular object tracer, destructor and delete. |
| tracer = (uintptr_t)RegularObjectTracer; |
| destructor = 0; |
| operatorDelete = 0; |
| |
| // Set IMT. |
| if (!isAbstract(C->access)) |
| IMT = new (C->classLoader->allocator, "IMT") InterfaceMethodTable(); |
| |
| // Set the class of this VT. |
| cl = C; |
| |
| // Set depth and display for fast dynamic type checking. |
| JavaVirtualTable* superVT = C->super->virtualVT; |
| depth = superVT->depth + 1; |
| nbSecondaryTypes = superVT->nbSecondaryTypes + cl->nbInterfaces; |
| |
| for (uint32 i = 0; i < cl->nbInterfaces; ++i) { |
| nbSecondaryTypes += cl->interfaces[i]->virtualVT->nbSecondaryTypes; |
| } |
| |
| uint32 length = getDisplayLength() < depth ? getDisplayLength() : depth; |
| memcpy(display, superVT->display, length * sizeof(JavaVirtualTable*)); |
| uint32 outOfDepth = 0; |
| if (C->isInterface()) { |
| offset = getCacheIndex(); |
| } else if (depth < getDisplayLength()) { |
| display[depth] = this; |
| offset = getCacheIndex() + depth + 1; |
| } else { |
| offset = getCacheIndex(); |
| // Add the super in the list of secondary types only if it is |
| // out of depth. |
| if (depth > getDisplayLength()) { |
| ++nbSecondaryTypes; |
| outOfDepth = 1; |
| } |
| } |
| |
| mvm::BumpPtrAllocator& allocator = C->classLoader->allocator; |
| secondaryTypes = (JavaVirtualTable**) |
| allocator.Allocate(sizeof(JavaVirtualTable*) * nbSecondaryTypes, |
| "Secondary types"); |
| |
| if (outOfDepth) { |
| secondaryTypes[0] = this; |
| } |
| |
| if (superVT->nbSecondaryTypes) { |
| memcpy(secondaryTypes + outOfDepth, superVT->secondaryTypes, |
| sizeof(JavaVirtualTable*) * superVT->nbSecondaryTypes); |
| } |
| |
| for (uint32 i = 0; i < cl->nbInterfaces; ++i) { |
| JavaVirtualTable* cur = cl->interfaces[i]->virtualVT; |
| assert(cur && "Interface not resolved!\n"); |
| uint32 index = superVT->nbSecondaryTypes + outOfDepth + i; |
| secondaryTypes[index] = cur; |
| } |
| |
| uint32 lastIndex = superVT->nbSecondaryTypes + cl->nbInterfaces + |
| outOfDepth; |
| |
| for (uint32 i = 0; i < cl->nbInterfaces; ++i) { |
| JavaVirtualTable* cur = cl->interfaces[i]->virtualVT; |
| memcpy(secondaryTypes + lastIndex, cur->secondaryTypes, |
| sizeof(JavaVirtualTable*) * cur->nbSecondaryTypes); |
| lastIndex += cur->nbSecondaryTypes; |
| } |
| |
| } else { |
| // Set the tracer, destructor and delete. |
| tracer = (uintptr_t)JavaObjectTracer; |
| destructor = 0; |
| operatorDelete = 0; |
| |
| // Set the class of this VT. |
| cl = C; |
| |
| // Set depth and display for fast dynamic type checking. |
| // java.lang.Object does not have any secondary types. |
| offset = getCacheIndex() + 1; |
| depth = 0; |
| display[0] = this; |
| destructor = 0; |
| nbSecondaryTypes = 0; |
| } |
| |
| |
| } |
| |
| JavaVirtualTable::JavaVirtualTable(ClassArray* C) { |
| |
| if (C->baseClass()->isClass()) |
| C->baseClass()->asClass()->resolveClass(); |
| |
| if (!C->baseClass()->isPrimitive()) { |
| baseClassVT = C->baseClass()->virtualVT; |
| |
| // Copy the super VT into the current VT. |
| uint32 size = (getBaseSize() - getFirstJavaMethodIndex()); |
| memcpy(this->getFirstJavaMethod(), |
| C->super->virtualVT->getFirstJavaMethod(), |
| size * sizeof(uintptr_t)); |
| tracer = (uintptr_t)ArrayObjectTracer; |
| |
| // Set the class of this VT. |
| cl = C; |
| |
| // Set depth and display for fast dynamic type checking. |
| JnjvmClassLoader* JCL = cl->classLoader; |
| Classpath* upcalls = JCL->bootstrapLoader->upcalls; |
| |
| if (upcalls->ArrayOfObject) { |
| UserCommonClass* base = C->baseClass(); |
| uint32 dim = 1; |
| while (base->isArray()) { |
| base = base->asArrayClass()->baseClass(); |
| ++dim; |
| } |
| |
| bool newSecondaryTypes = false; |
| bool intf = base->isInterface(); |
| ClassArray* super = 0; |
| |
| if (base->isPrimitive()) { |
| // If the base class is primitive, then the super is one |
| // dimension below, e.g. the super of int[][] is Object[]. |
| --dim; |
| const UTF8* superName = JCL->constructArrayName(dim, C->super->name); |
| super = JCL->constructArray(superName); |
| } else if (base == C->super) { |
| // If the base class is java.lang.Object, then the super is one |
| // dimension below, e.g. the super of Object[][] is Object[]. |
| // Also, the class is the first class in the dimension hierarchy, |
| // so it must create a new secondary type list. |
| --dim; |
| newSecondaryTypes = true; |
| super = C->baseClass()->asArrayClass(); |
| } else { |
| // If the base class is any other class, interface or not, |
| // the super is of the dimension of the current array class, |
| // and whose base class is the super of this base class. |
| const UTF8* superName = JCL->constructArrayName(dim, base->super->name); |
| JnjvmClassLoader* superLoader = base->super->classLoader; |
| super = superLoader->constructArray(superName); |
| } |
| |
| assert(super && "No super found"); |
| JavaVirtualTable* superVT = super->virtualVT; |
| depth = superVT->depth + 1; |
| |
| // Record if we need to add the super in the list of secondary types. |
| uint32 addSuper = 0; |
| |
| uint32 length = getDisplayLength() < depth ? getDisplayLength() : depth; |
| memcpy(display, superVT->display, length * sizeof(JavaVirtualTable*)); |
| if (depth < getDisplayLength() && !intf) { |
| display[depth] = this; |
| offset = getCacheIndex() + depth + 1; |
| } else { |
| offset = getCacheIndex(); |
| // We add the super if the current class is an interface or if the super |
| // class is out of depth. |
| if (intf || depth != getDisplayLength()) addSuper = 1; |
| } |
| |
| mvm::BumpPtrAllocator& allocator = JCL->allocator; |
| |
| if (!newSecondaryTypes) { |
| if (base->nbInterfaces || addSuper) { |
| // If the base class implements interfaces, we must also add the |
| // arrays of these interfaces, of the same dimension than this array |
| // class and add them to the secondary types list. |
| nbSecondaryTypes = base->nbInterfaces + superVT->nbSecondaryTypes + |
| addSuper; |
| secondaryTypes = (JavaVirtualTable**) |
| allocator.Allocate(sizeof(JavaVirtualTable*) * nbSecondaryTypes, |
| "Secondary types"); |
| |
| // Put the super in the list of secondary types. |
| if (addSuper) secondaryTypes[0] = superVT; |
| |
| // Copy the list of secondary types of the super. |
| memcpy(secondaryTypes + addSuper, superVT->secondaryTypes, |
| superVT->nbSecondaryTypes * sizeof(JavaVirtualTable*)); |
| |
| // Add our own secondary types: the interfaces of the base class put |
| // in the dimension of the current array class. |
| for (uint32 i = 0; i < base->nbInterfaces; ++i) { |
| const UTF8* name = |
| JCL->constructArrayName(dim, base->interfaces[i]->name); |
| ClassArray* interface = JCL->constructArray(name); |
| JavaVirtualTable* CurVT = interface->virtualVT; |
| secondaryTypes[i + superVT->nbSecondaryTypes + addSuper] = CurVT; |
| } |
| } else { |
| // If the super is not a secondary type and the base class does not |
| // implement any interface, we can reuse the list of secondary types |
| // of super. |
| nbSecondaryTypes = superVT->nbSecondaryTypes; |
| secondaryTypes = superVT->secondaryTypes; |
| } |
| } else { |
| |
| // This is an Object[....] array class. It will create the list of |
| // secondary types and all array classes of the same dimension whose |
| // base class does not have interfaces point to this array. |
| |
| // If we're superior than the display limit, we must make room for one |
| // slot that will contain the current VT. |
| uint32 outOfDepth = 0; |
| if (depth >= getDisplayLength()) outOfDepth = 1; |
| |
| assert(cl->nbInterfaces == 2 && "Arrays have more than 2 interface?"); |
| |
| // The list of secondary types is composed of: |
| // (1) The list of secondary types of super array. |
| // (2) The array of inherited interfaces with the same dimensions. |
| // (3) This VT, if its depth is superior than the display size. |
| nbSecondaryTypes = superVT->nbSecondaryTypes + 2 + outOfDepth; |
| |
| secondaryTypes = (JavaVirtualTable**) |
| allocator.Allocate(sizeof(JavaVirtualTable*) * nbSecondaryTypes, |
| "Secondary types"); |
| |
| // First, copy the list of secondary types from super array. |
| memcpy(secondaryTypes + outOfDepth, superVT->secondaryTypes, |
| superVT->nbSecondaryTypes * sizeof(JavaVirtualTable*)); |
| |
| // If the depth is superior than the display size, put the current VT |
| // at the beginning of the list. |
| if (outOfDepth) secondaryTypes[0] = this; |
| |
| // Load Cloneable[...] and Serializable[...] |
| const UTF8* name = JCL->constructArrayName(dim, cl->interfaces[0]->name); |
| ClassArray* firstInterface = JCL->constructArray(name); |
| name = JCL->constructArrayName(dim, cl->interfaces[1]->name); |
| ClassArray* secondInterface = JCL->constructArray(name); |
| |
| uint32 index = superVT->nbSecondaryTypes + outOfDepth; |
| |
| // Put Cloneable[...] and Serializable[...] at the end of the list. |
| secondaryTypes[index] = firstInterface->virtualVT; |
| secondaryTypes[index + 1] = secondInterface->virtualVT; |
| |
| // If the depth is greater than the display size, |
| // Cloneable[...] and Serializable[...] have their own list of |
| // secondary types, and we must therefore tell them that they |
| // implement themselves. |
| // If the depth is less than than the display size, there is nothing |
| // to do: the array of secondary types has been created before loading |
| // the interface arrays, so the interface arrays already reference |
| // the array. |
| if (outOfDepth) { |
| firstInterface->virtualVT->secondaryTypes[index] = |
| firstInterface->virtualVT; |
| firstInterface->virtualVT->secondaryTypes[index + 1] = |
| secondInterface->virtualVT; |
| secondInterface->virtualVT->secondaryTypes[index] = |
| firstInterface->virtualVT; |
| secondInterface->virtualVT->secondaryTypes[index + 1] = |
| secondInterface->virtualVT; |
| } |
| } |
| |
| } else { |
| // This is java.lang.Object[]. |
| depth = 1; |
| display[0] = C->super->virtualVT; |
| display[1] = this; |
| offset = getCacheIndex() + 2; |
| nbSecondaryTypes = 2; |
| |
| mvm::BumpPtrAllocator& allocator = JCL->allocator; |
| secondaryTypes = (JavaVirtualTable**) |
| allocator.Allocate(sizeof(JavaVirtualTable*) * nbSecondaryTypes, |
| "Secondary types"); |
| |
| // The interfaces have already been resolved. |
| secondaryTypes[0] = cl->interfaces[0]->virtualVT; |
| secondaryTypes[1] = cl->interfaces[1]->virtualVT; |
| } |
| |
| } else { |
| // Set the tracer, destructor and delete |
| tracer = (uintptr_t)JavaArrayTracer; |
| destructor = 0; |
| operatorDelete = 0; |
| |
| // Set the class of this VT. |
| cl = C; |
| |
| // Set depth and display for fast dynamic type checking. Since |
| // JavaObject has not been loaded yet, don't use super. |
| depth = 1; |
| display[0] = 0; |
| display[1] = this; |
| nbSecondaryTypes = 2; |
| offset = getCacheIndex() + 2; |
| |
| // The list of secondary types has not been allocated yet by |
| // java.lang.Object[]. The initialiseVT function will update the current |
| // array to point to java.lang.Object[]'s secondary list. |
| } |
| } |
| |
| |
| JavaVirtualTable::JavaVirtualTable(ClassPrimitive* C) { |
| // Only used for subtype checking |
| cl = C; |
| depth = 0; |
| display[0] = this; |
| nbSecondaryTypes = 0; |
| offset = getCacheIndex() + 1; |
| } |
| |
| void AnnotationReader::readAnnotation() { |
| uint16 typeIndex = reader.readU2(); |
| uint16 numPairs = reader.readU2(); |
| |
| for (uint16 j = 0; j < numPairs; ++j) { |
| uint16 nameIndex = reader.readU2(); |
| readElementValue(); |
| } |
| AnnotationNameIndex = typeIndex; |
| } |
| |
| void AnnotationReader::readElementValue() { |
| uint8 tag = reader.readU1(); |
| if ((tag == 'B') || (tag == 'C') || (tag == 'D') || (tag == 'F') || |
| (tag == 'J') || (tag == 'S') || (tag == 'I') || (tag == 'Z') || |
| (tag == 's')) { |
| uint16 constValue = reader.readU2(); |
| } else if (tag == 'e') { |
| uint16 typeName = reader.readU2(); |
| uint16 constName = reader.readU2(); |
| } else if (tag == 'c') { |
| uint16 classInfoIndex = reader.readU2(); |
| } else if (tag == '@') { |
| readAnnotation(); |
| } else if (tag == '[') { |
| uint16 numValues = reader.readU2(); |
| for (uint32 i = 0; i < numValues; ++i) { |
| readElementValue(); |
| } |
| } |
| } |
| |
| uint16 JavaMethod::lookupLineNumber(uintptr_t ip) { |
| for(uint16 i = 0; i < codeInfoLength; ++i) { |
| if (codeInfo[i].address > ip) { |
| assert(i > 0 && "Wrong ip address for method"); |
| return codeInfo[i - 1].lineNumber; |
| } |
| } |
| if (codeInfoLength) return codeInfo[codeInfoLength - 1].lineNumber; |
| return 0; |
| } |
| |
| uint16 JavaMethod::lookupCtpIndex(uintptr_t ip) { |
| for(uint16 i = 0; i < codeInfoLength; ++i) { |
| if (codeInfo[i].address > ip) { |
| assert(i > 0 && "Wrong ip address for method"); |
| return codeInfo[i - 1].ctpIndex; |
| } |
| } |
| if (codeInfoLength) return codeInfo[codeInfoLength - 1].ctpIndex; |
| return 0; |
| } |