| //===-- JnjvmClassLoader.cpp - Jnjvm representation of a class loader ------===// |
| // |
| // The VMKit project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #define DEBUG_VERBOSE_CLASS_LOADER_UNLOADING 1 |
| |
| #include <iostream> |
| #include <climits> |
| #include <cstdlib> |
| |
| // for strrchr |
| #include <cstring> |
| |
| // for dlopen and dlsym |
| #include <dlfcn.h> |
| |
| // for stat, S_IFMT and S_IFDIR |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <string> |
| |
| #include "debug.h" |
| #include "vmkit/Allocator.h" |
| |
| #include "Classpath.h" |
| #include "ClasspathReflect.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 "JnjvmClassLoader.h" |
| #include "LockedMap.h" |
| #include "Reader.h" |
| #include "Zip.h" |
| |
| |
| using namespace j3; |
| using namespace std; |
| |
| typedef void (*static_init_t)(JnjvmClassLoader*); |
| |
| const UTF8* JavaCompiler::InlinePragma = 0; |
| const UTF8* JavaCompiler::NoInlinePragma = 0; |
| |
| |
| JnjvmBootstrapLoader::JnjvmBootstrapLoader(vmkit::BumpPtrAllocator& Alloc, |
| JavaCompiler* Comp, |
| bool dlLoad) : |
| JnjvmClassLoader(Alloc) { |
| |
| TheCompiler = Comp; |
| |
| javaTypes = new(allocator, "TypeMap") TypeMap(); |
| javaSignatures = new(allocator, "SignMap") SignMap(); |
| strings = new(allocator, "StringList") StringList(); |
| |
| bootClasspathEnv = ClasslibBootEnv; |
| libClasspathEnv = ClasslibLibEnv; |
| |
| upcalls = new(allocator, "Classpath") Classpath(); |
| bootstrapLoader = this; |
| |
| // Try to find if we have a pre-compiled rt.jar |
| bool bootLoaded = false; |
| if (dlLoad) { |
| bootLoaded = Precompiled::Init(this); |
| } |
| |
| if (!bootLoaded) { |
| classes = new(allocator, "ClassMap") ClassMap(); |
| hashUTF8 = new(allocator, "UTF8Map") UTF8Map(allocator); |
| // Analyze the boot classpath, we know bootstrap classes are not in the |
| // executable. |
| analyseClasspathEnv(bootClasspathEnv); |
| |
| // Allocate the interfaces array for array classes, so that VT construction |
| // can use it right away. |
| ClassArray::InterfacesArray = |
| (Class**)allocator.Allocate(2 * sizeof(UserClass*), "Interface array"); |
| |
| // Create the primitive classes. |
| upcalls->OfChar = UPCALL_PRIMITIVE_CLASS(this, "char", 1); |
| upcalls->OfBool = UPCALL_PRIMITIVE_CLASS(this, "boolean", 0); |
| upcalls->OfShort = UPCALL_PRIMITIVE_CLASS(this, "short", 1); |
| upcalls->OfInt = UPCALL_PRIMITIVE_CLASS(this, "int", 2); |
| upcalls->OfLong = UPCALL_PRIMITIVE_CLASS(this, "long", 3); |
| upcalls->OfFloat = UPCALL_PRIMITIVE_CLASS(this, "float", 2); |
| upcalls->OfDouble = UPCALL_PRIMITIVE_CLASS(this, "double", 3); |
| upcalls->OfVoid = UPCALL_PRIMITIVE_CLASS(this, "void", 0); |
| upcalls->OfByte = UPCALL_PRIMITIVE_CLASS(this, "byte", 0); |
| } |
| |
| // Create the primitive arrays. |
| upcalls->ArrayOfChar = constructArray(asciizConstructUTF8("[C"), |
| upcalls->OfChar); |
| |
| upcalls->ArrayOfByte = constructArray(asciizConstructUTF8("[B"), |
| upcalls->OfByte); |
| |
| upcalls->ArrayOfInt = constructArray(asciizConstructUTF8("[I"), |
| upcalls->OfInt); |
| |
| upcalls->ArrayOfBool = constructArray(asciizConstructUTF8("[Z"), |
| upcalls->OfBool); |
| |
| upcalls->ArrayOfLong = constructArray(asciizConstructUTF8("[J"), |
| upcalls->OfLong); |
| |
| upcalls->ArrayOfFloat = constructArray(asciizConstructUTF8("[F"), |
| upcalls->OfFloat); |
| |
| upcalls->ArrayOfDouble = constructArray(asciizConstructUTF8("[D"), |
| upcalls->OfDouble); |
| |
| upcalls->ArrayOfShort = constructArray(asciizConstructUTF8("[S"), |
| upcalls->OfShort); |
| |
| // Fill the maps. |
| primitiveMap[I_VOID] = upcalls->OfVoid; |
| primitiveMap[I_BOOL] = upcalls->OfBool; |
| primitiveMap[I_BYTE] = upcalls->OfByte; |
| primitiveMap[I_CHAR] = upcalls->OfChar; |
| primitiveMap[I_SHORT] = upcalls->OfShort; |
| primitiveMap[I_INT] = upcalls->OfInt; |
| primitiveMap[I_FLOAT] = upcalls->OfFloat; |
| primitiveMap[I_LONG] = upcalls->OfLong; |
| primitiveMap[I_DOUBLE] = upcalls->OfDouble; |
| |
| arrayTable[JavaArray::T_BOOLEAN - 4] = upcalls->ArrayOfBool; |
| arrayTable[JavaArray::T_BYTE - 4] = upcalls->ArrayOfByte; |
| arrayTable[JavaArray::T_CHAR - 4] = upcalls->ArrayOfChar; |
| arrayTable[JavaArray::T_SHORT - 4] = upcalls->ArrayOfShort; |
| arrayTable[JavaArray::T_INT - 4] = upcalls->ArrayOfInt; |
| arrayTable[JavaArray::T_FLOAT - 4] = upcalls->ArrayOfFloat; |
| arrayTable[JavaArray::T_LONG - 4] = upcalls->ArrayOfLong; |
| arrayTable[JavaArray::T_DOUBLE - 4] = upcalls->ArrayOfDouble; |
| |
| JavaAttribute::annotationsAttribute = |
| asciizConstructUTF8("RuntimeVisibleAnnotations"); |
| JavaAttribute::codeAttribute = asciizConstructUTF8("Code"); |
| JavaAttribute::exceptionsAttribute = asciizConstructUTF8("Exceptions"); |
| JavaAttribute::constantAttribute = asciizConstructUTF8("ConstantValue"); |
| JavaAttribute::lineNumberTableAttribute = asciizConstructUTF8("LineNumberTable"); |
| JavaAttribute::innerClassesAttribute = asciizConstructUTF8("InnerClasses"); |
| JavaAttribute::sourceFileAttribute = asciizConstructUTF8("SourceFile"); |
| JavaAttribute::signatureAttribute = asciizConstructUTF8("Signature"); |
| JavaAttribute::enclosingMethodAttribute = asciizConstructUTF8("EnclosingMethod"); |
| JavaAttribute::paramAnnotationsAttribute = asciizConstructUTF8("RuntimeVisibleParameterAnnotations"); |
| JavaAttribute::annotationDefaultAttribute = asciizConstructUTF8("AnnotationDefault"); |
| |
| JavaCompiler::InlinePragma = |
| asciizConstructUTF8("Lorg/vmmagic/pragma/Inline;"); |
| JavaCompiler::NoInlinePragma = |
| asciizConstructUTF8("Lorg/vmmagic/pragma/NoInline;"); |
| |
| initName = asciizConstructUTF8("<init>"); |
| initExceptionSig = asciizConstructUTF8("(Ljava/lang/String;)V"); |
| clinitName = asciizConstructUTF8("<clinit>"); |
| clinitType = asciizConstructUTF8("()V"); |
| runName = asciizConstructUTF8("run"); |
| prelib = asciizConstructUTF8("lib"); |
| postlib = asciizConstructUTF8(vmkit::System::GetDyLibExtension()); |
| mathName = asciizConstructUTF8("java/lang/Math"); |
| VMFloatName = asciizConstructUTF8("java/lang/VMFloat"); |
| VMDoubleName = asciizConstructUTF8("java/lang/VMDouble"); |
| stackWalkerName = asciizConstructUTF8("gnu/classpath/VMStackWalker"); |
| NoClassDefFoundError = asciizConstructUTF8("java/lang/NoClassDefFoundError"); |
| |
| #define DEF_UTF8(var) \ |
| var = asciizConstructUTF8(#var) |
| |
| DEF_UTF8(abs); |
| DEF_UTF8(sqrt); |
| DEF_UTF8(sin); |
| DEF_UTF8(cos); |
| DEF_UTF8(tan); |
| DEF_UTF8(asin); |
| DEF_UTF8(acos); |
| DEF_UTF8(atan); |
| DEF_UTF8(atan2); |
| DEF_UTF8(exp); |
| DEF_UTF8(log); |
| DEF_UTF8(pow); |
| DEF_UTF8(ceil); |
| DEF_UTF8(floor); |
| DEF_UTF8(rint); |
| DEF_UTF8(cbrt); |
| DEF_UTF8(cosh); |
| DEF_UTF8(expm1); |
| DEF_UTF8(hypot); |
| DEF_UTF8(log10); |
| DEF_UTF8(log1p); |
| DEF_UTF8(sinh); |
| DEF_UTF8(tanh); |
| DEF_UTF8(finalize); |
| DEF_UTF8(floatToRawIntBits); |
| DEF_UTF8(doubleToRawLongBits); |
| DEF_UTF8(intBitsToFloat); |
| DEF_UTF8(longBitsToDouble); |
| |
| #undef DEF_UTF8 |
| } |
| |
| JnjvmClassLoader::JnjvmClassLoader(vmkit::BumpPtrAllocator& Alloc) : |
| allocator(Alloc) |
| { |
| } |
| |
| JnjvmClassLoader::JnjvmClassLoader( |
| vmkit::BumpPtrAllocator& Alloc, JnjvmClassLoader& JCL, JavaObject* loader, |
| VMClassLoader* vmdata, Jnjvm* VM) : |
| allocator(Alloc) |
| { |
| llvm_gcroot(loader, 0); |
| llvm_gcroot(vmdata, 0); |
| bootstrapLoader = JCL.bootstrapLoader; |
| TheCompiler = bootstrapLoader->getCompiler()->Create( |
| "Applicative loader", |
| bootstrapLoader->getCompiler()->isCompilingGarbageCollector()); |
| |
| hashUTF8 = new(allocator, "UTF8Map") UTF8Map(allocator); |
| classes = new(allocator, "ClassMap") ClassMap(); |
| javaTypes = new(allocator, "TypeMap") TypeMap(); |
| javaSignatures = new(allocator, "SignMap") SignMap(); |
| strings = new(allocator, "StringList") StringList(); |
| |
| vmdata->JCL = this; |
| vmkit::Collector::objectReferenceNonHeapWriteBarrier( |
| (gc**)&javaLoader, (gc*)loader); |
| vm = VM; |
| |
| JavaMethod* meth = bootstrapLoader->upcalls->loadInClassLoader; |
| loadClassMethod = |
| JavaObject::getClass(loader)->asClass()->lookupMethodDontThrow( |
| meth->name, meth->type, false, true, &loadClass); |
| assert(loadClass && "Loader does not have a loadClass function"); |
| } |
| |
| void JnjvmClassLoader::setCompiler(JavaCompiler* Comp) { |
| // Set the new compiler. |
| TheCompiler = Comp; |
| } |
| |
| ClassBytes* JnjvmBootstrapLoader::openName(const UTF8* utf8) { |
| ClassBytes* res = reinterpret_cast<ClassBytes*>(dlsym(vmkit::System::GetSelfHandle(), |
| UTF8Buffer(utf8).toCompileName("_bytes")->cString())); |
| if (res != NULL) return res; |
| |
| vmkit::ThreadAllocator threadAllocator; |
| |
| char* asciiz = (char*)threadAllocator.Allocate(utf8->size + 1); |
| for (sint32 i = 0; i < utf8->size; ++i) |
| asciiz[i] = utf8->elements[i]; |
| asciiz[utf8->size] = 0; |
| |
| uint32 alen = utf8->size; |
| |
| for (std::vector<const char*>::iterator i = bootClasspath.begin(), |
| e = bootClasspath.end(); i != e; ++i) { |
| const char* str = *i; |
| unsigned int strLen = strlen(str); |
| char* buf = (char*)threadAllocator.Allocate(strLen + alen + 7); |
| |
| sprintf(buf, "%s%s.class", str, asciiz); |
| res = Reader::openFile(this, buf); |
| if (res != NULL) return res; |
| } |
| |
| for (std::vector<ZipArchive*>::iterator i = bootArchives.begin(), |
| e = bootArchives.end(); i != e; ++i) { |
| |
| ZipArchive* archive = *i; |
| char* buf = (char*)threadAllocator.Allocate(alen + 7); |
| sprintf(buf, "%s.class", asciiz); |
| res = Reader::openZip(this, archive, buf); |
| if (res != NULL) return res; |
| } |
| |
| return NULL; |
| } |
| |
| |
| UserClass* JnjvmBootstrapLoader::internalLoad(const UTF8* name, |
| bool doResolve, |
| JavaString* strName) { |
| ClassBytes* bytes = NULL; |
| llvm_gcroot(strName, 0); |
| |
| UserCommonClass* cl = lookupClass(name); |
| |
| if (!cl) { |
| bytes = openName(name); |
| if (bytes != NULL) { |
| cl = constructClass(name, bytes); |
| } |
| } |
| |
| if (cl) { |
| assert(!cl->isArray()); |
| if (doResolve) cl->asClass()->resolveClass(); |
| } |
| |
| if (cl) { |
| // If we don't have knowledge of the package containing this class, |
| // add it to the set of packages defined in this classLoader |
| UTF8Buffer buffer(name); |
| const char * cname = buffer.cString(); |
| const char * slash = strrchr(cname, '/'); |
| if (slash) { |
| int packagelen = slash - cname; |
| const UTF8 * package = name->extract(hashUTF8, 0, packagelen); |
| //classes->lock.lock(); |
| lock.lock(); |
| packages.insert(package); |
| lock.unlock(); |
| //classes->lock.unlock(); |
| } |
| } |
| |
| |
| |
| return (UserClass*)cl; |
| } |
| |
| UserCommonClass* JnjvmClassLoader::internalLoadCreateClass(const UTF8* name, JavaString* strName) |
| { |
| JavaObjectClass* jcl = 0; |
| JavaObject* obj = 0; |
| llvm_gcroot(strName, 0); |
| llvm_gcroot(obj, 0); |
| llvm_gcroot(jcl, 0); |
| |
| UserClass* forCtp = loadClass; |
| if (strName == NULL) { |
| strName = JavaString::internalToJava(name, vm); |
| } |
| |
| obj = loadClassMethod->invokeJavaObjectVirtual(vm, forCtp, javaLoader, &strName); |
| return JavaObjectClass::getClass(jcl = (JavaObjectClass*)obj); |
| } |
| |
| UserClass* JnjvmClassLoader::internalLoad(const UTF8* name, bool doResolve, |
| JavaString* strName) { |
| llvm_gcroot(strName, 0); |
| |
| UserCommonClass* cl = lookupClass(name); |
| |
| if (!cl) { |
| cl = internalLoadCreateClass(name, strName); |
| } |
| |
| if (cl && doResolve && cl->isClass()) { |
| cl->asClass()->resolveClass(); |
| } |
| |
| return (UserClass*)cl; |
| } |
| |
| UserClass* JnjvmClassLoader::loadName(const UTF8* name, bool doResolve, |
| bool doThrow, JavaString* strName) { |
| |
| llvm_gcroot(strName, 0); |
| |
| UserClass* cl = internalLoad(name, doResolve, strName); |
| |
| if (!cl && doThrow) { |
| Jnjvm* vm = JavaThread::get()->getJVM(); |
| if (name->equals(bootstrapLoader->NoClassDefFoundError)) { |
| fprintf(stderr, "Unable to load NoClassDefFoundError"); |
| abort(); |
| } |
| if (TheCompiler->isStaticCompiling()) { |
| fprintf(stderr, "Could not find %s, needed for static compiling\n", |
| UTF8Buffer(name).cString()); |
| abort(); |
| } |
| vm->noClassDefFoundError(name); |
| } |
| |
| ensureCached(cl); |
| |
| return cl; |
| } |
| |
| void JnjvmClassLoader::ensureCached(UserCommonClass* cl) { |
| if (cl && cl->classLoader != this) { |
| classes->lock.lock(); |
| ClassMap::iterator End = classes->map.end(); |
| ClassMap::iterator I = classes->map.find(cl->name); |
| if (I == End) |
| classes->map.insert(std::make_pair(cl->name, cl)); |
| classes->lock.unlock(); |
| } |
| } |
| |
| |
| const UTF8* JnjvmClassLoader::lookupComponentName(const UTF8* name, |
| UTF8* holder, |
| bool& prim) { |
| uint32 len = name->size; |
| uint32 start = 0; |
| uint32 origLen = len; |
| |
| while (true) { |
| --len; |
| if (len == 0) { |
| return 0; |
| } else { |
| ++start; |
| if (name->elements[start] != I_TAB) { |
| if (name->elements[start] == I_REF) { |
| uint32 size = (uint32)name->size; |
| if ((size == (start + 1)) || (size == (start + 2)) || |
| (name->elements[start + 1] == I_TAB) || |
| (name->elements[origLen - 1] != I_END_REF)) { |
| return 0; |
| } else { |
| const uint16* buf = &(name->elements[start + 1]); |
| uint32 bufLen = len - 2; |
| const UTF8* componentName = hashUTF8->lookupReader(buf, bufLen); |
| if (!componentName && holder) { |
| holder->size = len - 2; |
| for (uint32 i = 0; i < len - 2; ++i) { |
| holder->elements[i] = name->elements[start + 1 + i]; |
| } |
| componentName = holder; |
| } |
| return componentName; |
| } |
| } else { |
| uint16 cur = name->elements[start]; |
| if ((cur == I_BOOL || cur == I_BYTE || |
| cur == I_CHAR || cur == I_SHORT || |
| cur == I_INT || cur == I_FLOAT || |
| cur == I_DOUBLE || cur == I_LONG) |
| && ((uint32)name->size) == start + 1) { |
| prim = true; |
| } |
| return 0; |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| UserCommonClass* JnjvmClassLoader::lookupClassOrArray(const UTF8* name) { |
| UserCommonClass* temp = lookupClass(name); |
| if (temp) return temp; |
| |
| if (this != bootstrapLoader) { |
| temp = bootstrapLoader->lookupClassOrArray(name); |
| ensureCached(temp); |
| if (temp) return temp; |
| } |
| |
| |
| if (name->elements[0] == I_TAB) { |
| bool prim = false; |
| const UTF8* componentName = lookupComponentName(name, 0, prim); |
| if (prim) return constructArray(name); |
| if (componentName) { |
| UserCommonClass* temp = lookupClass(componentName); |
| if (temp) return constructArray(name); |
| } |
| } |
| |
| return 0; |
| } |
| |
| UserCommonClass* JnjvmClassLoader::loadClassFromUserUTF8(const UTF8* name, |
| bool doResolve, |
| bool doThrow, |
| JavaString* strName) { |
| llvm_gcroot(strName, 0); |
| |
| if (name->size == 0) { |
| return 0; |
| } else if (name->elements[0] == I_TAB) { |
| vmkit::ThreadAllocator threadAllocator; |
| bool prim = false; |
| UTF8* holder = (UTF8*)threadAllocator.Allocate( |
| sizeof(UTF8) + name->size * sizeof(uint16)); |
| if (!holder) return 0; |
| |
| const UTF8* componentName = lookupComponentName(name, holder, prim); |
| if (prim) return constructArray(name); |
| if (componentName) { |
| UserCommonClass* temp = loadName(componentName, doResolve, doThrow, NULL); |
| ensureCached(temp); |
| if (temp) return constructArray(name); |
| } |
| } else { |
| return loadName(name, doResolve, doThrow, strName); |
| } |
| |
| return NULL; |
| } |
| |
| UserCommonClass* JnjvmClassLoader::loadClassFromAsciiz(const char* asciiz, |
| bool doResolve, |
| bool doThrow) { |
| const UTF8* name = hashUTF8->lookupAsciiz(asciiz); |
| vmkit::ThreadAllocator threadAllocator; |
| if (!name) name = bootstrapLoader->hashUTF8->lookupAsciiz(asciiz); |
| if (!name) { |
| uint32 size = strlen(asciiz); |
| UTF8* temp = (UTF8*)threadAllocator.Allocate( |
| sizeof(UTF8) + size * sizeof(uint16)); |
| temp->size = size; |
| |
| for (uint32 i = 0; i < size; ++i) { |
| temp->elements[i] = asciiz[i]; |
| } |
| name = temp; |
| } |
| |
| |
| return loadClassFromUserUTF8(name, doResolve, doThrow, NULL); |
| } |
| |
| |
| UserCommonClass* |
| JnjvmClassLoader::loadClassFromJavaString(JavaString* str, bool doResolve, |
| bool doThrow) { |
| |
| llvm_gcroot(str, 0); |
| vmkit::ThreadAllocator allocator; |
| UTF8* name = (UTF8*)allocator.Allocate(sizeof(UTF8) + str->count * sizeof(uint16)); |
| |
| name->size = str->count; |
| for (sint32 i = 0; i < str->count; ++i) { |
| uint16 cur = ArrayUInt16::getElement(JavaString::getValue(str), str->offset + i); |
| if (cur == '.') name->elements[i] = '/'; |
| else if (cur == '/') { |
| return 0; |
| } |
| else name->elements[i] = cur; |
| } |
| |
| return loadClassFromUserUTF8(name, doResolve, doThrow, str); |
| } |
| |
| UserCommonClass* JnjvmClassLoader::lookupClassFromJavaString(JavaString* str) { |
| |
| const ArrayUInt16* value = NULL; |
| llvm_gcroot(str, 0); |
| llvm_gcroot(value, 0); |
| value = JavaString::getValue(str); |
| vmkit::ThreadAllocator allocator; |
| |
| UTF8* name = (UTF8*)allocator.Allocate(sizeof(UTF8) + str->count * sizeof(uint16)); |
| name->size = str->count; |
| for (sint32 i = 0; i < str->count; ++i) { |
| uint16 cur = ArrayUInt16::getElement(value, str->offset + i); |
| if (cur == '.') name->elements[i] = '/'; |
| else name->elements[i] = cur; |
| } |
| UserCommonClass* cls = lookupClass(name); |
| return cls; |
| } |
| |
| UserCommonClass* JnjvmClassLoader::lookupClass(const UTF8* utf8) { |
| classes->lock.lock(); |
| UserCommonClass* cl = classes->map.lookup(utf8); |
| classes->lock.unlock(); |
| return cl; |
| } |
| |
| UserCommonClass* JnjvmClassLoader::loadBaseClass(const UTF8* name, |
| uint32 start, uint32 len) { |
| |
| if (name->elements[start] == I_TAB) { |
| UserCommonClass* baseClass = loadBaseClass(name, start + 1, len - 1); |
| JnjvmClassLoader* loader = baseClass->classLoader; |
| const UTF8* arrayName = name->extract(loader->hashUTF8, start, start + len); |
| return loader->constructArray(arrayName, baseClass); |
| } else if (name->elements[start] == I_REF) { |
| const UTF8* componentName = name->extract(hashUTF8, |
| start + 1, start + len - 1); |
| UserCommonClass* cl = loadName(componentName, false, true, NULL); |
| return cl; |
| } else { |
| Classpath* upcalls = bootstrapLoader->upcalls; |
| UserClassPrimitive* prim = |
| UserClassPrimitive::byteIdToPrimitive(name->elements[start], upcalls); |
| assert(prim && "No primitive found"); |
| return prim; |
| } |
| } |
| |
| |
| UserClassArray* JnjvmClassLoader::constructArray(const UTF8* name) { |
| ClassArray* res = (ClassArray*)lookupClass(name); |
| if (res) return res; |
| |
| UserCommonClass* cl = loadBaseClass(name, 1, name->size - 1); |
| assert(cl && "no base class for an array"); |
| JnjvmClassLoader* ld = cl->classLoader; |
| res = ld->constructArray(name, cl); |
| |
| ensureCached(res); |
| return res; |
| } |
| |
| UserClass* JnjvmClassLoader::constructClass(const UTF8* name, |
| ClassBytes* bytes) { |
| JavaObject* excp = NULL; |
| llvm_gcroot(excp, 0); |
| UserClass* res = NULL; |
| lock2.lock(); |
| classes->lock.lock(); |
| res = (UserClass*) classes->map.lookup(name); |
| classes->lock.unlock(); |
| if (res == NULL) { |
| TRY { |
| const UTF8* internalName = readerConstructUTF8(name->elements, name->size); |
| res = new(allocator, "Class") UserClass(this, internalName, bytes); |
| res->readClass(); |
| res->makeVT(); |
| getCompiler()->resolveVirtualClass(res); |
| getCompiler()->resolveStaticClass(res); |
| classes->lock.lock(); |
| assert(res->getDelegatee() == NULL); |
| assert(res->getStaticInstance() == NULL); |
| assert(classes->map.lookup(internalName) == NULL); |
| classes->map[internalName] = res; |
| classes->lock.unlock(); |
| } CATCH { |
| excp = JavaThread::get()->pendingException; |
| JavaThread::get()->clearException(); |
| } END_CATCH; |
| } |
| lock2.unlock(); |
| if (excp != NULL) { |
| JavaThread::get()->throwException(excp); |
| } |
| |
| if (res->super == NULL) { |
| // java.lang.Object just got created, initialise VTs of arrays. |
| ClassArray::initialiseVT(res); |
| } |
| return res; |
| } |
| |
| UserClassArray* JnjvmClassLoader::constructArray(const UTF8* name, |
| UserCommonClass* baseClass) { |
| assert(baseClass && "constructing an array class without a base class"); |
| assert(baseClass->classLoader == this && |
| "constructing an array with wrong loader"); |
| UserClassArray* res = 0; |
| classes->lock.lock(); |
| res = (UserClassArray*) classes->map.lookup(name); |
| if (res == NULL) { |
| const UTF8* internalName = readerConstructUTF8(name->elements, name->size); |
| res = new(allocator, "Array class") UserClassArray(this, internalName, |
| baseClass); |
| classes->map.insert(std::make_pair(internalName, res)); |
| |
| } |
| classes->lock.unlock(); |
| return res; |
| } |
| |
| Typedef* JnjvmClassLoader::internalConstructType(const UTF8* name) { |
| short int cur = name->elements[0]; |
| Typedef* res = 0; |
| switch (cur) { |
| case I_TAB : |
| res = new(allocator, "ArrayTypedef") ArrayTypedef(name); |
| break; |
| case I_REF : |
| res = new(allocator, "ObjectTypedef") ObjectTypedef(name, hashUTF8); |
| break; |
| default : |
| UserClassPrimitive* cl = |
| bootstrapLoader->getPrimitiveClass((char)name->elements[0]); |
| assert(cl && "No primitive"); |
| bool unsign = (cl == bootstrapLoader->upcalls->OfChar || |
| cl == bootstrapLoader->upcalls->OfBool); |
| res = new(allocator, "PrimitiveTypedef") PrimitiveTypedef(name, cl, |
| unsign, cur); |
| } |
| return res; |
| } |
| |
| |
| Typedef* JnjvmClassLoader::constructType(const UTF8* name) { |
| javaTypes->lock.lock(); |
| Typedef* res = javaTypes->map.lookup(name); |
| if (res == 0) { |
| res = internalConstructType(name); |
| javaTypes->map[name] = res; |
| } |
| javaTypes->lock.unlock(); |
| return res; |
| } |
| |
| static void typeError(const UTF8* name, short int l) { |
| if (l != 0) { |
| fprintf(stderr, "wrong type %d in %s", l, UTF8Buffer(name).cString()); |
| } else { |
| fprintf(stderr, "wrong type %s", UTF8Buffer(name).cString()); |
| } |
| abort(); |
| } |
| |
| |
| static bool analyseIntern(const UTF8* name, uint32 pos, uint32 meth, |
| uint32& ret) { |
| short int cur = name->elements[pos]; |
| switch (cur) { |
| case I_PARD : |
| ret = pos + 1; |
| return true; |
| case I_BOOL : |
| ret = pos + 1; |
| return false; |
| case I_BYTE : |
| ret = pos + 1; |
| return false; |
| case I_CHAR : |
| ret = pos + 1; |
| return false; |
| case I_SHORT : |
| ret = pos + 1; |
| return false; |
| case I_INT : |
| ret = pos + 1; |
| return false; |
| case I_FLOAT : |
| ret = pos + 1; |
| return false; |
| case I_DOUBLE : |
| ret = pos + 1; |
| return false; |
| case I_LONG : |
| ret = pos + 1; |
| return false; |
| case I_VOID : |
| ret = pos + 1; |
| return false; |
| case I_TAB : |
| if (meth == 1) { |
| pos++; |
| } else { |
| while (name->elements[++pos] == I_TAB) {} |
| analyseIntern(name, pos, 1, pos); |
| } |
| ret = pos; |
| return false; |
| case I_REF : |
| if (meth != 2) { |
| while (name->elements[++pos] != I_END_REF) {} |
| } |
| ret = pos + 1; |
| return false; |
| default : |
| typeError(name, cur); |
| } |
| return false; |
| } |
| |
| Signdef* JnjvmClassLoader::constructSign(const UTF8* name) { |
| javaSignatures->lock.lock(); |
| Signdef* res = javaSignatures->map.lookup(name); |
| if (res == 0) { |
| std::vector<Typedef*> buf; |
| uint32 len = (uint32)name->size; |
| uint32 pos = 1; |
| uint32 pred = 0; |
| |
| while (pos < len) { |
| pred = pos; |
| bool end = analyseIntern(name, pos, 0, pos); |
| if (end) break; |
| else { |
| buf.push_back(constructType(name->extract(hashUTF8, pred, pos))); |
| } |
| } |
| |
| if (pos == len) { |
| typeError(name, 0); |
| } |
| |
| analyseIntern(name, pos, 0, pred); |
| |
| if (pred != len) { |
| typeError(name, 0); |
| } |
| |
| Typedef* ret = constructType(name->extract(hashUTF8, pos, pred)); |
| |
| res = new(allocator, buf.size()) Signdef(name, this, buf, ret); |
| |
| javaSignatures->map[name] = res; |
| } |
| javaSignatures->lock.unlock(); |
| return res; |
| } |
| |
| JnjvmClassLoader* JnjvmClassLoader::createForJavaObject( |
| Jnjvm* vm, JavaObject* loader, VMClassLoader**vmdata) |
| { |
| llvm_gcroot(loader, 0); |
| |
| *vmdata = VMClassLoader::allocate(); |
| vmkit::BumpPtrAllocator* A = new vmkit::BumpPtrAllocator(); |
| |
| JnjvmClassLoader* JCL = new(*A, "Class loader") |
| JnjvmClassLoader(*A, *vm->bootstrapLoader, loader, *vmdata, vm); |
| |
| Classpath* upcalls = vm->bootstrapLoader->upcalls; |
| upcalls->vmdataClassLoader->setInstanceObjectField(loader, *vmdata); |
| return JCL; |
| } |
| |
| JnjvmClassLoader* |
| JnjvmClassLoader::getJnjvmLoaderFromJavaObject(JavaObject* loader, Jnjvm* vm) { |
| |
| VMClassLoader* vmdata = 0; |
| |
| llvm_gcroot(loader, 0); |
| llvm_gcroot(vmdata, 0); |
| |
| if (loader == NULL) |
| return vm->bootstrapLoader; |
| |
| JnjvmClassLoader* JCL = 0; |
| Classpath* upcalls = vm->bootstrapLoader->upcalls; |
| vmdata = |
| (VMClassLoader*)(upcalls->vmdataClassLoader->getInstanceObjectField(loader)); |
| |
| // If the vmdata field isn't set yet, or it's set to something other than |
| // a VMClassLoader (which happens in the OpenJDK port), then initialize |
| // the field now. |
| if (vmdata == NULL) { |
| JavaObject::acquire(loader); |
| vmdata = |
| (VMClassLoader*)(upcalls->vmdataClassLoader->getInstanceObjectField(loader)); |
| if (!vmdata || !VMClassLoader::isVMClassLoader(vmdata)) { |
| JCL = JnjvmClassLoader::createForJavaObject(vm, loader, &vmdata); |
| } |
| JavaObject::release(loader); |
| } |
| else if (!VMClassLoader::isVMClassLoader(vmdata)) { |
| JavaObject::acquire(loader); |
| JCL = JnjvmClassLoader::createForJavaObject(vm, loader, &vmdata); |
| JavaObject::release(loader); |
| } else { |
| JCL = vmdata->getClassLoader(); |
| assert(JCL->javaLoader == loader); |
| } |
| |
| if (!JCL) { |
| assert( vmdata->getClassLoader() == JCL && "Loader is not equal to stored value"); |
| fprintf(stderr, "Error in method %s, %d because condition 1: %d\n", |
| __FILE__, __LINE__, (vmdata == NULL || !VMClassLoader::isVMClassLoader(vmdata))); |
| } |
| return JCL; |
| } |
| |
| const UTF8* JnjvmClassLoader::asciizConstructUTF8(const char* asciiz) { |
| return hashUTF8->lookupOrCreateAsciiz(asciiz); |
| } |
| |
| const UTF8* JnjvmClassLoader::readerConstructUTF8(const uint16* buf, |
| uint32 size) { |
| return hashUTF8->lookupOrCreateReader(buf, size); |
| } |
| |
| JnjvmClassLoader::~JnjvmClassLoader() { |
| |
| if (vm) { |
| vm->removeFrameInfos(TheCompiler); |
| } |
| |
| if (classes) { |
| classes->~ClassMap(); |
| allocator.Deallocate(classes); |
| classes = NULL; |
| } |
| |
| if (hashUTF8) { |
| hashUTF8->~UTF8Map(); |
| allocator.Deallocate(hashUTF8); |
| hashUTF8 = NULL; |
| } |
| |
| if (javaTypes) { |
| javaTypes->~TypeMap(); |
| allocator.Deallocate(javaTypes); |
| javaTypes = NULL; |
| } |
| |
| if (javaSignatures) { |
| javaSignatures->~SignMap(); |
| allocator.Deallocate(javaSignatures); |
| javaSignatures = NULL; |
| } |
| |
| for (std::vector<void*>::iterator |
| i = nativeLibs.begin(), e = nativeLibs.end(); i != e; ++i) |
| { |
| dlclose(*i); |
| } |
| nativeLibs.clear(); |
| |
| vm = NULL; |
| |
| delete TheCompiler; |
| TheCompiler = NULL; |
| |
| // Don't delete the allocator. The caller of this method must |
| // delete it after the current object is deleted. |
| } |
| |
| |
| JnjvmBootstrapLoader::~JnjvmBootstrapLoader() { |
| } |
| |
| JavaString** JnjvmClassLoader::UTF8ToStr(const UTF8* val) { |
| JavaString* res = NULL; |
| llvm_gcroot(res, 0); |
| res = vm->internalUTF8ToStr(val); |
| return strings->addString(this, res); |
| } |
| |
| JavaString** JnjvmBootstrapLoader::UTF8ToStr(const UTF8* val) { |
| JavaString* res = NULL; |
| llvm_gcroot(res, 0); |
| Jnjvm* vm = JavaThread::get()->getJVM(); |
| res = vm->internalUTF8ToStr(val); |
| return strings->addString(this, res); |
| } |
| |
| void JnjvmBootstrapLoader::analyseClasspathEnv(const char* str) { |
| ClassBytes* bytes = NULL; |
| vmkit::ThreadAllocator threadAllocator; |
| if (str != 0) { |
| unsigned int len = strlen(str); |
| char* buf = (char*)threadAllocator.Allocate((len + 1) * sizeof(char)); |
| const char* cur = str; |
| int top = 0; |
| char c = 1; |
| while (c != 0) { |
| while (((c = cur[top]) != 0) && c != Jnjvm::envSeparator[0]) { |
| top++; |
| } |
| if (top != 0) { |
| memcpy(buf, cur, top); |
| buf[top] = 0; |
| char* rp = (char*)threadAllocator.Allocate(PATH_MAX); |
| memset(rp, 0, PATH_MAX); |
| rp = realpath(buf, rp); |
| if (rp && rp[PATH_MAX - 1] == 0 && strlen(rp) != 0) { |
| struct stat st; |
| stat(rp, &st); |
| if ((st.st_mode & S_IFMT) == S_IFDIR) { |
| unsigned int len = strlen(rp); |
| char* temp = (char*)allocator.Allocate(len + 2, "Boot classpath"); |
| memcpy(temp, rp, len); |
| temp[len] = Jnjvm::dirSeparator[0]; |
| temp[len + 1] = 0; |
| bootClasspath.push_back(temp); |
| } else { |
| bytes = Reader::openFile(this, rp); |
| if (bytes) { |
| ZipArchive *archive = new(allocator, "ZipArchive") |
| ZipArchive(bytes, allocator); |
| if (archive) { |
| bootArchives.push_back(archive); |
| } |
| } |
| } |
| } |
| } |
| cur = cur + top + 1; |
| top = 0; |
| } |
| } |
| } |
| |
| // constructArrayName can allocate the UTF8 directly in the classloader |
| // memory because it is called by safe places, ie only valid names are |
| // created. |
| const UTF8* JnjvmClassLoader::constructArrayName(uint32 steps, |
| const UTF8* className) { |
| uint32 len = className->size; |
| uint32 pos = steps; |
| bool isTab = (className->elements[0] == I_TAB ? true : false); |
| uint32 n = steps + len + (isTab ? 0 : 2); |
| vmkit::ThreadAllocator allocator; |
| uint16* buf = (uint16*)allocator.Allocate(n * sizeof(uint16)); |
| |
| for (uint32 i = 0; i < steps; i++) { |
| buf[i] = I_TAB; |
| } |
| |
| if (!isTab) { |
| ++pos; |
| buf[steps] = I_REF; |
| } |
| |
| for (uint32 i = 0; i < len; i++) { |
| buf[pos + i] = className->elements[i]; |
| } |
| |
| if (!isTab) { |
| buf[n - 1] = I_END_REF; |
| } |
| |
| const UTF8* res = readerConstructUTF8(buf, n); |
| return res; |
| } |
| |
| word_t JnjvmClassLoader::loadInLib(const char* buf, bool& j3) { |
| // Check 'self'. Should only check our process, however it's possible native |
| // code dlopen'd something itself (with RTLD_GLOBAL; OpenJDK does this). |
| // To handle this, we search both ourselves and the libraries we loaded. |
| word_t sym = |
| (word_t)TheCompiler->loadMethod(vmkit::System::GetSelfHandle(), buf); |
| |
| // Search loaded libraries as well, both as fallback and to determine |
| // whether or not the symbol in question is defined by vmkit. |
| word_t symFromLib = 0; |
| for (std::vector<void*>::iterator i = nativeLibs.begin(), |
| e = nativeLibs.end(); i!= e; ++i) { |
| symFromLib = (word_t)TheCompiler->loadMethod((*i), buf); |
| if (symFromLib) break; |
| } |
| |
| if (sym) { |
| // Always use the definition from 'self', if it exists. |
| // Furthermore, claim it's defined in j3 iff it wasn't found in one of our |
| // libraries. This might be wrong if we do a lookup on a symbol that's |
| // neither in vmkit nor a VM-loaded library (but /is/ in a different library |
| // that has been dlopen'd by native code), but that should never |
| // be called from java code anyway. |
| j3 = (sym != symFromLib); |
| return sym; |
| } |
| |
| // Otherwise return what we found in the libraries, if anything |
| if (symFromLib) return symFromLib; |
| |
| if (this != bootstrapLoader) |
| return bootstrapLoader->loadInLib(buf, j3); |
| |
| return 0; |
| } |
| |
| void* JnjvmClassLoader::loadLib(const char* buf) { |
| void* handle = dlopen(buf, RTLD_LAZY | RTLD_LOCAL); |
| if (handle) nativeLibs.push_back(handle); |
| return handle; |
| } |
| |
| char* JnjvmClassLoader::getErrorMessage() { |
| return dlerror(); |
| } |
| |
| word_t JnjvmClassLoader::loadInLib(const char* name, void* handle) { |
| return (word_t)TheCompiler->loadMethod(handle, name); |
| } |
| |
| word_t JnjvmClassLoader::nativeLookup(JavaMethod* meth, bool& j3, |
| char* buf) { |
| |
| // Is this method defined via registerNatives()? |
| // If so, use that definition. |
| word_t res = getRegisteredNative(meth); |
| if (res != 0) return res; |
| |
| // Otherwise, try to resolve the method with a symbol lookup |
| // First as the base method |
| meth->jniConsFromMeth(buf); |
| res = loadInLib(buf, j3); |
| if (!res) { |
| // Failing that, try the overloaded symbol name |
| meth->jniConsFromMethOverloaded(buf); |
| res = loadInLib(buf, j3); |
| } |
| return res; |
| } |
| |
| |
| JavaString** StringList::addString(JnjvmClassLoader* JCL, JavaString* obj, bool hasTheLock) { |
| llvm_gcroot(obj, 0); |
| if (!hasTheLock) |
| JCL->lockForStrings.lock(); |
| if (length == MAXIMUM_STRINGS) { |
| StringList* next = new(JCL->allocator, "StringList") StringList(); |
| next->prev = this; |
| JCL->strings = next; |
| JavaString** res = next->addString(JCL, obj, true); |
| if (!hasTheLock) |
| JCL->lockForStrings.unlock(); |
| return res; |
| } else { |
| vmkit::Collector::objectReferenceNonHeapWriteBarrier( |
| (gc**)&(strings[length]), (gc*)obj); |
| JavaString** res = &strings[length++]; |
| if (!hasTheLock) |
| JCL->lockForStrings.unlock(); |
| return res; |
| } |
| } |
| |
| void JnjvmClassLoader::registerNative(JavaMethod * meth, word_t fnPtr) { |
| nativesLock.lock(); |
| // Don't support multiple levels of registerNatives |
| assert(registeredNatives.find(meth) == registeredNatives.end()); |
| |
| registeredNatives[meth] = fnPtr; |
| nativesLock.unlock(); |
| } |
| |
| word_t JnjvmClassLoader::getRegisteredNative(const JavaMethod * meth) { |
| nativesLock.lock(); |
| word_t res = registeredNatives[meth]; |
| nativesLock.unlock(); |
| return res; |
| } |
| |
| ArrayObject* JnjvmBootstrapLoader::getBootPackages(Jnjvm * vm) { |
| ArrayObject* res = 0; |
| llvm_gcroot(res, 0); |
| |
| lock.lock(); |
| res = (ArrayObject*)vm->upcalls->ArrayOfString->doNew(packages.size(), vm); |
| std::set<const UTF8*>::const_iterator I = packages.begin(), |
| E = packages.end(); |
| for (int i = 0; I != E; ++I, ++i) { |
| ArrayObject::setElement(res, *UTF8ToStr(*I), i); |
| } |
| |
| lock.unlock(); |
| |
| return res; |
| } |