| |
| #include <stdio.h> |
| #include <dlfcn.h> |
| #include <cxxabi.h> |
| |
| #include "j3/j3class.h" |
| #include "j3/j3.h" |
| #include "j3/j3classloader.h" |
| #include "j3/j3constants.h" |
| #include "j3/j3method.h" |
| #include "j3/j3thread.h" |
| #include "j3/j3trampoline.h" |
| #include "j3/j3lib.h" |
| #include "j3/j3field.h" |
| #include "j3/j3utf16.h" |
| #include "j3/j3codegen.h" |
| |
| #include "llvm/IR/Type.h" |
| #include "llvm/IR/DerivedTypes.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Linker.h" |
| #include "llvm/Support/ToolOutputFile.h" |
| #include "llvm/PassManager.h" |
| #include "llvm/Bitcode/ReaderWriter.h" |
| |
| #include "vmkit/safepoint.h" |
| #include "vmkit/system.h" |
| |
| using namespace j3; |
| |
| vmkit::T_ptr_less_t<J3ObjectHandle*> J3::charArrayLess; |
| vmkit::T_ptr_less_t<llvm::FunctionType*> J3::llvmFunctionTypeLess; |
| |
| J3::J3(vmkit::BumpAllocator* allocator) : |
| VMKit(allocator), |
| nameToCharArrays(vmkit::Name::less, allocator), |
| charArrayToStrings(charArrayLess, allocator), |
| _names(allocator), |
| monitorManager(allocator), |
| llvmSignatures(llvmFunctionTypeLess, allocator) { |
| |
| pthread_mutex_init(&stringsMutex, 0); |
| interfaceTrampoline = J3Trampoline::buildInterfaceTrampoline(allocator); |
| } |
| |
| J3* J3::create() { |
| vmkit::BumpAllocator* allocator = vmkit::BumpAllocator::create(); |
| return new(allocator) J3(allocator); |
| } |
| |
| void J3::introspect() { |
| typeJNIEnvPtr = llvm::PointerType::getUnqual(introspectType("struct.JNIEnv_")); |
| typeJ3VirtualTable = introspectType("class.j3::J3VirtualTable"); |
| typeJ3VirtualTablePtr = llvm::PointerType::getUnqual(typeJ3VirtualTable); |
| typeJ3Type = introspectType("class.j3::J3Type"); |
| typeJ3TypePtr = llvm::PointerType::getUnqual(typeJ3Type); |
| typeJ3LayoutPtr = llvm::PointerType::getUnqual(introspectType("class.j3::J3Layout")); |
| typeJ3ObjectType = introspectType("class.j3::J3ObjectType"); |
| typeJ3ObjectTypePtr = llvm::PointerType::getUnqual(typeJ3ObjectType); |
| typeJ3Thread = llvm::PointerType::getUnqual(introspectType("class.j3::J3Thread")); |
| typeJ3Class = introspectType("class.j3::J3Class"); |
| typeJ3ClassPtr = llvm::PointerType::getUnqual(typeJ3Class); |
| typeJ3ArrayClass = introspectType("class.j3::J3ArrayClass"); |
| typeJ3ArrayClassPtr = llvm::PointerType::getUnqual(typeJ3ArrayClass); |
| typeJ3ArrayObject = introspectType("class.j3::J3ArrayObject"); |
| typeJ3ArrayObjectPtr = llvm::PointerType::getUnqual(typeJ3ArrayObject); |
| typeJ3Method = introspectType("class.j3::J3Method"); |
| typeJ3Object = introspectType("class.j3::J3Object"); |
| typeJ3ObjectPtr = llvm::PointerType::getUnqual(typeJ3Object); |
| typeJ3ObjectHandle = introspectType("class.j3::J3ObjectHandle"); |
| typeJ3ObjectHandlePtr = llvm::PointerType::getUnqual(typeJ3ObjectHandle); |
| typeJ3LockRecord = introspectType("class.j3::J3LockRecord"); |
| |
| typeGXXException = llvm::StructType::get(llvm::Type::getInt8Ty(llvmContext())->getPointerTo(), |
| llvm::Type::getInt32Ty(llvmContext()), NULL); |
| } |
| |
| void J3::start(int argc, char** argv) { |
| _options.process(argc, argv); |
| J3Lib::processOptions(this); |
| |
| vmkit::ThreadAllocator::initialize(sizeof(J3Thread)); |
| |
| J3Thread* thread = new J3ThreadBootstrap(this); |
| |
| vmkitBootstrap(thread, options()->selfBitCodePath); |
| |
| if(options()->debugLifeCycle) |
| fprintf(stderr, " VM terminate\n"); |
| } |
| |
| void J3::run() { |
| #define defJavaConstantName(name, id) \ |
| name = names()->get(id); |
| onJavaConstantNames(defJavaConstantName) |
| #undef defJavaConstantName |
| |
| introspect(); |
| |
| if(options()->isAOT) { |
| #define _x(name, id, forceInline) \ |
| if(forceInline) \ |
| introspectFunction(0, id)->addFnAttr(llvm::Attribute::AlwaysInline); |
| #include "j3/j3meta.def" |
| #undef _x |
| } |
| |
| vmkit::BumpAllocator* loaderAllocator = vmkit::BumpAllocator::create(); |
| initialClassLoader = new(loaderAllocator) J3InitialClassLoader(loaderAllocator); |
| |
| vmkit::BumpAllocator* a = initialClassLoader->allocator(); |
| |
| #define defPrimitive(name, ctype, llvmtype, scale) \ |
| type##name = new(a) J3Primitive(initialClassLoader, J3Cst::ID_##name, llvm::Type::get##llvmtype##Ty(llvmContext()), scale); |
| onJavaTypes(defPrimitive) |
| #undef defPrimitive |
| |
| clinitSign = initialClassLoader->getSignature(0, clinitSignName); |
| |
| #define z_class(clName) initialClassLoader->loadClass(names()->get(clName)) |
| #define z_method(access, cl, name, signature) cl->findMethod(access, name, initialClassLoader->getSignature(cl, signature)) |
| #define z_field(access, cl, name, type) cl->findField(access, names()->get(name), type) |
| |
| |
| nbArrayInterfaces = 2; |
| arrayInterfaces = (J3Class**)initialClassLoader->allocator()->allocate(2*sizeof(J3Type*)); |
| arrayInterfaces[0] = z_class("java.lang.Cloneable"); |
| arrayInterfaces[1] = z_class("java.io.Serializable"); |
| |
| charArrayClass = typeCharacter->getArray(); |
| objectClass = z_class("java.lang.Object"); |
| objectClass->resolve(); |
| |
| stringClass = z_class("java.lang.String"); |
| stringClassInit = z_method(0, stringClass, initName, names()->get("([CZ)V")); |
| stringClassValue = z_field(0, stringClass, "value", charArrayClass); |
| |
| classClass = z_class("java.lang.Class"); |
| J3Field vmData[] = { J3Field(J3Cst::ACC_PRIVATE, names()->get("** vmData **"), typeLong) }; |
| classClass->resolve(vmData, 1); |
| classClassInit = z_method(0, classClass, initName, names()->get("()V")); |
| classClassVMData = classClass->findField(0, vmData[0].name(), vmData[0].type()); |
| |
| classLoaderClass = z_class("java.lang.ClassLoader"); |
| classLoaderClass->resolve(vmData, 1); |
| classLoaderClassVMData = classLoaderClass->findField(0, vmData[0].name(), vmData[0].type()); |
| classLoaderClassLoadClass = z_method(0, classLoaderClass, names()->get("loadClass"), |
| names()->get("(Ljava/lang/String;)Ljava/lang/Class;")); |
| classLoaderClassGetSystemClassLoader = z_method(J3Cst::ACC_STATIC, |
| classLoaderClass, |
| names()->get("getSystemClassLoader"), |
| names()->get("()Ljava/lang/ClassLoader;")); |
| |
| threadClass = z_class("java.lang.Thread"); |
| threadClassRun = z_method(0, threadClass, names()->get("run"), names()->get("()V")); |
| threadClassVMData = z_field(0, threadClass, "eetop", typeLong); |
| |
| fieldClass = z_class("java.lang.reflect.Field"); |
| fieldClassClass = z_field(0, fieldClass, "clazz", classClass); |
| fieldClassSlot = z_field(0, fieldClass, "slot", typeInteger); |
| fieldClassAccess = z_field(0, fieldClass, "modifiers", typeInteger); |
| fieldClassInit = z_method(0, fieldClass, initName, |
| names()->get("(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;IILjava/lang/String;[B)V")); |
| |
| constructorClass = z_class("java.lang.reflect.Constructor"); |
| constructorClassClass = z_field(0, constructorClass, "clazz", classClass); |
| constructorClassSlot = z_field(0, constructorClass, "slot", typeInteger); |
| constructorClassInit = z_method(0, constructorClass, initName, |
| names()->get("(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B)V")); |
| |
| methodClass = z_class("java.lang.reflect.Method"); |
| methodClassClass = z_field(0, methodClass, "clazz", classClass); |
| methodClassSlot = z_field(0, methodClass, "slot", typeInteger); |
| methodClassInit = z_method(0, methodClass, initName, |
| names()->get("(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B[B)V")); |
| |
| throwableClassBacktrace = z_field(0, z_class("java.lang.Throwable"), "backtrace", objectClass); |
| |
| stackTraceElementClass = z_class("java.lang.StackTraceElement"); |
| stackTraceElementClassInit = z_method(0, stackTraceElementClass, initName, |
| names()->get("(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V")); |
| |
| #define defJavaClassPrimitive(name, ctype, llvmtype, scale) \ |
| type##name->defineJavaClass("java/lang/"#name); |
| onJavaTypes(defJavaClassPrimitive) |
| #undef defJavaClassPrimitive |
| |
| if(options()->debugLifeCycle) |
| fprintf(stderr, " Bootstrap the system library\n"); |
| |
| J3Lib::bootstrap(this); |
| |
| if(options()->isAOT) |
| compileApplication(); |
| else |
| runApplication(); |
| } |
| |
| void J3::compileApplication() { |
| if(options()->debugLifeCycle) |
| fprintf(stderr, " Find the class loader\n"); |
| |
| J3ClassLoader* loader = J3ClassLoader::nativeClassLoader(z_method(J3Cst::ACC_STATIC, z_class("java.lang.ClassLoader"), |
| names()->get("getSystemClassLoader"), |
| names()->get("()Ljava/lang/ClassLoader;"))->invokeStatic().valObject); |
| |
| |
| loader->setCompilationMode(J3CodeGen::OnlyTranslate | |
| J3CodeGen::NotUseStub | |
| J3CodeGen::NotNeedGC | |
| J3CodeGen::SupposeClinited | |
| J3CodeGen::NoRuntimeCheck); |
| |
| if(options()->mainClass) |
| J3::internalError("compiling a single class is not yet supported"); |
| else { |
| J3Class* jarFileClass = z_class("java.util.jar.JarFile"); |
| J3ObjectHandle* jarFile = J3ObjectHandle::doNewObject(jarFileClass); |
| z_method(0, jarFileClass, initName, names()->get("(Ljava/lang/String;)V"))->invokeSpecial(jarFile, utfToString(options()->jarFile)); |
| J3ObjectHandle* it = z_method(0, jarFileClass, names()->get("entries"), |
| names()->get("()Ljava/util/Enumeration;"))->invokeVirtual(jarFile).valObject; |
| J3Method* hasMore = z_method(0, it->vt()->type()->asObjectType(), names()->get("hasMoreElements"), names()->get("()Z")); |
| J3Method* nextEl = z_method(0, it->vt()->type()->asObjectType(), names()->get("nextElement"), names()->get("()Ljava/lang/Object;")); |
| J3Method* getName = z_method(0, z_class("java.util.zip.ZipEntry"), names()->get("getName"), names()->get("()Ljava/lang/String;")); |
| |
| while(hasMore->invokeVirtual(it).valBoolean) { |
| const vmkit::Name* name = stringToName(getName->invokeVirtual(nextEl->invokeVirtual(it).valObject).valObject); |
| |
| if(strcmp(name->cStr() + name->length() - 6, ".class") == 0) { |
| char buf[name->length() - 5]; |
| memcpy(buf, name->cStr(), name->length() - 6); |
| buf[name->length()-6] = 0; |
| loader->getTypeFromQualified(0, buf)->asClass()->compileAll(); |
| } |
| } |
| } |
| |
| llvm::Module* res = new llvm::Module("yop", llvmContext()); |
| llvm::Linker* linker = new llvm::Linker(res); |
| loader->aotSnapshot(linker); |
| |
| std::string err; |
| llvm::tool_output_file out(options()->aotFile, err, llvm::sys::fs::F_Binary); |
| if(!err.empty()) |
| fprintf(stderr, "%s\n", err.c_str()); |
| else { |
| llvm::PassManager pm; |
| pm.add(llvm::createBitcodeWriterPass(out.os())); |
| pm.run(*res); |
| out.keep(); |
| } |
| } |
| |
| void J3::runApplication() { |
| if(options()->debugLifeCycle) |
| fprintf(stderr, " Find the application\n"); |
| |
| //options()->debugExecute = 0; |
| |
| #define LM_CLASS 1 |
| #define LM_JAR 2 |
| |
| uint32_t mode; |
| const char* what; |
| |
| if(options()->mainClass) { |
| mode = LM_CLASS; |
| what = options()->mainClass; |
| } else { |
| mode = LM_JAR; |
| what = options()->jarFile; |
| } |
| |
| J3Class* main = J3ObjectType::nativeClass(z_method(J3Cst::ACC_STATIC, |
| z_class("sun.launcher.LauncherHelper"), |
| names()->get("checkAndLoadMain"), |
| names()->get("(ZILjava/lang/String;)Ljava/lang/Class;")) |
| ->invokeStatic(1, mode, utfToString(what)).valObject)->asClass(); |
| |
| |
| if(options()->debugLifeCycle) |
| fprintf(stderr, " Launch the application\n"); |
| |
| J3ObjectHandle* args = J3ObjectHandle::doNewArray(stringClass->getArray(), options()->nbArgs); |
| for(uint32_t i=0; i<options()->nbArgs; i++) |
| args->setObjectAt(i, utfToString(options()->args[i])); |
| |
| main |
| ->findMethod(J3Cst::ACC_STATIC, names()->get("main"), initialClassLoader->getSignature(0, names()->get("([Ljava/lang/String;)V"))) |
| ->invokeStatic(args); |
| } |
| |
| JNIEnv* J3::jniEnv() { |
| return J3Thread::get()->jniEnv(); |
| } |
| |
| const vmkit::Name* J3::qualifiedToBinaryName(const char* type, size_t length) { |
| if(length == -1) |
| length = strlen(type); |
| char buf[length+1]; |
| for(size_t i=0; i<length; i++) { |
| char c = type[i]; |
| buf[i] = c == '/' ? '.' : c; |
| } |
| buf[length] = 0; |
| return names()->get(buf); |
| } |
| |
| J3ObjectHandle* J3::arrayToString(J3ObjectHandle* array, bool doPush) { |
| pthread_mutex_lock(&stringsMutex); |
| J3ObjectHandle* res = charArrayToStrings[array]; |
| if(!res) { |
| J3ObjectHandle* prev = J3Thread::get()->tell(); |
| res = initialClassLoader->globalReferences()->add(J3ObjectHandle::doNewObject(stringClass)); |
| J3Thread::get()->restore(prev); |
| |
| stringClassInit->invokeSpecial(res, array, 0); |
| |
| charArrayToStrings[array] = res; |
| } |
| pthread_mutex_unlock(&stringsMutex); |
| return doPush ? J3Thread::get()->push(res) : res; |
| } |
| |
| J3ObjectHandle* J3::nameToString(const vmkit::Name* name, bool doPush) { |
| pthread_mutex_lock(&stringsMutex); |
| J3ObjectHandle* res = nameToCharArrays[name]; |
| if(!res) { |
| J3ObjectHandle* prev = J3Thread::get()->tell(); |
| uint16_t buf[name->length()]; |
| size_t pos = 0; |
| J3Utf16Encoder encoder(name); |
| |
| while(!encoder.isEof()) |
| buf[pos++] = encoder.nextUtf16(); |
| |
| res = initialClassLoader->globalReferences()->add(J3ObjectHandle::doNewArray(charArrayClass, pos)); |
| res->setRegionCharacter(0, buf, 0, pos); |
| J3Thread::get()->restore(prev); |
| |
| nameToCharArrays[name] = res; |
| } |
| pthread_mutex_unlock(&stringsMutex); |
| return arrayToString(res, doPush); |
| } |
| |
| J3ObjectHandle* J3::utfToString(const char* name, bool doPush) { |
| return nameToString(names()->get(name), doPush); |
| } |
| |
| const vmkit::Name* J3::arrayToName(J3ObjectHandle* array) { |
| char copy[J3Utf16Decoder::maxSize(array)]; |
| J3Utf16Decoder::decode(array, copy); |
| return names()->get(copy); |
| } |
| |
| const vmkit::Name* J3::stringToName(J3ObjectHandle* str) { |
| return arrayToName(str->getObject(stringClassValue)); |
| } |
| |
| void J3::classCastException() { |
| internalError("implement me: class cast exception"); |
| } |
| |
| void J3::nullPointerException() { |
| internalError("implement me: null pointer exception"); |
| } |
| |
| void J3::classNotFoundException(const vmkit::Name* name) { |
| internalError("ClassNotFoundException: %s", name); |
| } |
| |
| void J3::noClassDefFoundError(const vmkit::Name* name) { |
| internalError("NoClassDefFoundError: %s", name); |
| } |
| |
| void J3::noSuchMethodError(const char* msg, J3ObjectType* cl, const vmkit::Name* name, J3Signature* signature) { |
| internalError("%s: %s::%s %s", msg, cl->name()->cStr(), name->cStr(), signature->name()->cStr()); |
| } |
| |
| void J3::noSuchFieldError(const char* msg, J3ObjectType* cl, const vmkit::Name* name, J3Type* type) { |
| internalError("%s: %s::%s %s", msg, cl->name()->cStr(), name->cStr(), type->name()->cStr()); |
| } |
| |
| void J3::classFormatError(J3ObjectType* cl, const char* reason, ...) { |
| char buf[65536]; |
| va_list va; |
| va_start(va, reason); |
| vsnprintf(buf, 65536, reason, va); |
| va_end(va); |
| internalError("ClassFormatError in '%s' caused by '%s'", cl->name()->cStr(), buf); |
| } |
| |
| void J3::linkageError(J3Method* method) { |
| internalError("unable to find native method '%s::%s%s'", |
| method->cl()->name()->cStr(), |
| method->name()->cStr(), |
| method->signature()->name()->cStr()); |
| } |
| |
| void J3::outOfMemoryError() { |
| internalError("out of memory error"); |
| } |
| |
| void J3::negativeArraySizeException(int32_t length) { |
| internalError("negative array size exception: %ld", length); |
| } |
| |
| void J3::arrayStoreException() { |
| internalError("array store exception"); |
| } |
| |
| void J3::arrayIndexOutOfBoundsException() { |
| internalError("array bound check exception"); |
| } |
| |
| void J3::illegalMonitorStateException() { |
| internalError("illegal monitor state exception"); |
| } |
| |
| void J3::illegalArgumentException(const char* msg) { |
| internalError("illegal argument exception: %s", msg); |
| } |
| |
| void J3::vinternalError(const char* msg, va_list va) { |
| char buf[65536]; |
| vsnprintf(buf, 65536, msg, va); |
| fprintf(stderr, "Internal error: %s\n", buf); |
| printStackTrace(); |
| //exit(1); |
| abort(); |
| } |
| |
| void J3::printStackTrace() { |
| vmkit::StackWalker walker; |
| |
| while(walker.next()) { |
| vmkit::Safepoint* sf = J3Thread::get()->vm()->getSafepoint(walker.ip()); |
| |
| if(sf) { |
| for(uint32_t i=0; i<sf->inlineDepth(); i++) { |
| J3Method* m = (J3Method*)sf->unit()->getSymbol(sf->functionName(i)); |
| fprintf(stderr, " in %s::%s%s index %d\n", m->cl()->name()->cStr(), m->name()->cStr(), |
| m->signature()->name()->cStr(), sf->sourceIndex()); |
| } |
| } else { |
| Dl_info info; |
| |
| if(dladdr((void*)((uintptr_t)walker.ip()-1), &info)) { |
| int status; |
| const char* demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, &status); |
| const char* name = demangled ? demangled : info.dli_sname; |
| fprintf(stderr, " in %s + %lu\n", name, (uintptr_t)walker.ip() - (uintptr_t)info.dli_saddr); |
| } else { |
| fprintf(stderr, " in %p\n", walker.ip()); |
| } |
| } |
| } |
| } |
| |
| void J3::uncatchedException(void* e) { |
| J3Thread* thread = J3Thread::get(); |
| J3ObjectHandle* prev = thread->tell(); |
| |
| try { |
| J3ObjectHandle* excp = thread->push((J3Object*)e); |
| |
| J3ObjectHandle* handler = |
| thread->javaThread()->vt()->type()->asObjectType() |
| ->findMethod(0, |
| names()->get("getUncaughtExceptionHandler"), |
| initialClassLoader->getSignature(0, names()->get("()Ljava/lang/Thread$UncaughtExceptionHandler;"))) |
| ->invokeVirtual(thread->javaThread()) |
| .valObject; |
| |
| handler->vt()->type()->asObjectType() |
| ->findMethod(0, |
| names()->get("uncaughtException"), |
| initialClassLoader->getSignature(0, names()->get("(Ljava/lang/Thread;Ljava/lang/Throwable;)V"))) |
| ->invokeVirtual(handler, thread->javaThread(), excp); |
| |
| } catch(void* e2) { |
| fprintf(stderr, "Fatal: double exception %p and %p\n", e, e2); |
| } |
| thread->restore(prev); |
| } |
| |
| void J3::forceSymbolDefinition() { |
| J3ArrayObject a; a.length(); /* J3ArrayObject */ |
| J3LockRecord* l = new J3LockRecord(); /* J3LockRecord */ |
| try { |
| throw (void*)0; |
| } catch(void* e) { |
| } |
| } |