static void wrapClassNotFoundException(Env* env, const char* className) { Object* exception = rvmExceptionOccurred(env); if (exception && exception->clazz == java_lang_ClassNotFoundException) { // If ClassNotFoundException is thrown we have to wrap it in a NoClassDefFoundError exception = rvmExceptionClear(env); Method* constructor = rvmGetInstanceMethod(env, java_lang_NoClassDefFoundError, "<init>", "(Ljava/lang/String;)V"); if (!constructor) return; Object* message = rvmNewStringUTF(env, className, -1); if (!message) return; Object* wrappedException = rvmNewObject(env, java_lang_NoClassDefFoundError, constructor, message); if (!wrappedException) return; Class* java_lang_StackTraceElement = rvmFindClassUsingLoader(env, "java/lang/StackTraceElement", NULL); if (!java_lang_StackTraceElement) return; ObjectArray* stackTrace = rvmNewObjectArray(env, 0, java_lang_StackTraceElement, NULL, NULL); if (!stackTrace) return; Method* setStackTrace = rvmGetInstanceMethod(env, java_lang_Throwable, "setStackTrace", "([Ljava/lang/StackTraceElement;)V"); if (!setStackTrace) return; rvmCallVoidInstanceMethod(env, wrappedException, setStackTrace, stackTrace); if (rvmExceptionCheck(env)) return; Method* initCause = rvmGetInstanceMethod(env, java_lang_NoClassDefFoundError, "initCause", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); if (!initCause) return; rvmCallObjectInstanceMethod(env, wrappedException, initCause, exception); if (!rvmExceptionCheck(env)) rvmThrow(env, wrappedException); } }
void _bcSetObjectArrayElement(Env* env, ObjectArray* array, jint index, Object* value) { if (!value) { array->values[index] = value; return; } ENTER; Class* componentType = array->object.clazz->componentType; jboolean assignable = rvmIsAssignableFrom(env, value->clazz, componentType); if (!rvmExceptionCheck(env) && !assignable) { rvmThrowArrayStoreException(env, value->clazz, array->object.clazz); } if (!rvmExceptionCheck(env)) array->values[index] = value; LEAVEV; }
/** * Implements all abstract methods in {@code proxyClass}. This should be called after * {@link #addProxyMethods()} which will override all methods defined by the proxy's * ancestor classes (abstract or concrete). */ static jboolean implementAbstractInterfaceMethods(Env* env, Class* proxyClass, Interface* interface, ProxyClassData* proxyClassData) { if (!interface) return TRUE; Method* method = rvmGetMethods(env, interface->interface); if (rvmExceptionOccurred(env)) return FALSE; for (; method != NULL; method = method->next) { if (!METHOD_IS_CLASS_INITIALIZER(method)) { ProxyMethod* proxyMethod = hasMethod(env, proxyClass, method->name, method->desc); if (rvmExceptionOccurred(env)) return FALSE; if (!proxyMethod) { jint access = (method->access & (~ACC_ABSTRACT)) | ACC_FINAL; proxyMethod = addProxyMethod(env, proxyClass, method, access, _proxy0); if (!proxyMethod) return FALSE; } // Record the lookup function in proxyClassData LookupEntry* entry = rvmAllocateMemory(env, sizeof(LookupEntry)); if (!entry) return FALSE; entry->key.name = method->name; entry->key.desc = method->desc; entry->method = proxyMethod; HASH_ADD(hh, proxyClassData->lookupsHash, key, sizeof(LookupKey), entry); } } if (!implementAbstractInterfaceMethods(env, proxyClass, interface->next, proxyClassData)) return FALSE; Interface* interfaceInterfaces = rvmGetInterfaces(env, interface->interface); if (rvmExceptionCheck(env)) return FALSE; if (!implementAbstractInterfaceMethods(env, proxyClass, interfaceInterfaces, proxyClassData)) return FALSE; return TRUE; }
static jboolean getRuntimeVisibleAnnotationsIterator(Env* env, jbyte type, void* attributes, void* data) { ObjectArray** result = (ObjectArray**) ((void**) data)[0]; Object* classLoader = (Object*) ((void**) data)[1]; if (type == RUNTIME_VISIBLE_ANNOTATIONS) { jint length = getInt(&attributes); ObjectArray* annotations = rvmNewObjectArray(env, length, java_lang_annotation_Annotation, NULL, NULL); if (!annotations) return FALSE; jint i = 0; jint actualLength = 0; for (i = 0; i < length; i++) { jvalue value = {0}; if (getAnnotationValue(env, &attributes, NULL, classLoader, &value, TRUE)) { annotations->values[actualLength++] = (Object*) value.l; } else if (rvmExceptionCheck(env)) { return FALSE; } } if (actualLength != length) { // One or more annotations could not be loaded due to a missing class. // Reallocate the result array and copy over the non null values. ObjectArray* annotations2 = rvmNewObjectArray(env, actualLength, java_lang_annotation_Annotation, NULL, NULL); if (!annotations2) return FALSE; memcpy(annotations2->values, annotations->values, actualLength * sizeof(Object*)); annotations = annotations2; } *result = annotations; return FALSE; // Stop iterating } return TRUE; // Continue with next attribute }
void* lookupInterfaceMethod(Env* env, ClassInfoHeader* header, Object* thiz, char* name, char* desc) { initializeClass(env, header); if (rvmExceptionCheck(env)) return NULL; Class* ownerInterface = header->clazz; if (!rvmIsInstanceOf(env, thiz, ownerInterface)) { char message[256]; snprintf(message, 256, "Class %s does not implement the requested interface %s", rvmToBinaryClassName(env, thiz->clazz->name), rvmToBinaryClassName(env, ownerInterface->name)); rvmThrowIncompatibleClassChangeError(env, message); return NULL; } Method* method = rvmGetInstanceMethod(env, thiz->clazz, name, desc); Object* throwable = rvmExceptionClear(env); if (!method && throwable->clazz != java_lang_NoSuchMethodError) { rvmThrow(env, throwable); return NULL; } if (!method || METHOD_IS_ABSTRACT(method)) { rvmThrowAbstractMethodError(env, ""); // TODO: Message return NULL; } if (!METHOD_IS_PUBLIC(method)) { rvmThrowIllegalAccessError(env, ""); // TODO: Message return NULL; } return method->synchronizedImpl ? method->synchronizedImpl : method->impl; }
Class* rvmProxyCreateProxyClass(Env* env, Class* superclass, ClassLoader* classLoader, char* className, jint interfacesCount, Class** interfaces, jint instanceDataSize, jint instanceDataOffset, unsigned short instanceRefCount, ProxyHandler handler) { // Allocate the proxy class. Class* proxyClass = rvmAllocateClass(env, className, superclass, classLoader, CLASS_TYPE_PROXY | ACC_PUBLIC | ACC_FINAL, offsetof(Class, data) + sizeof(ProxyClassData), instanceDataSize, instanceDataOffset, 1, instanceRefCount, NULL, NULL); if (!proxyClass) return NULL; ProxyClassData* proxyClassData = (ProxyClassData*) proxyClass->data; proxyClassData->handler = handler; // Add interfaces jint i; for (i = 0; i < interfacesCount; i++) { if (!rvmAddInterface(env, proxyClass, (Class*) interfaces[i])) return NULL; } // Initialize methods to NULL to prevent rvmGetMethods() from trying to load the methods if called with this proxy class proxyClass->_methods = NULL; Class* c = proxyClass; while (c) { Interface* interface = rvmGetInterfaces(env, c); if (rvmExceptionCheck(env)) return NULL; if (!implementAbstractInterfaceMethods(env, proxyClass, interface, proxyClassData)) return NULL; c = c->superclass; } if (!addProxyMethods(env, proxyClass, superclass, proxyClassData)) return NULL; if (!rvmRegisterClass(env, proxyClass)) return NULL; return proxyClass; }
Class* Java_java_lang_Class_classForName(Env* env, Class* cls, Object* className, jboolean initializeBoolean, ClassLoader* classLoader) { if (!className) { rvmThrowNullPointerException(env); return NULL; } char* classNameUTF = toBinaryName(env, className); if (!classNameUTF) return NULL; Class* clazz = rvmFindClassUsingLoader(env, classNameUTF, classLoader); if (!clazz) { char* p = classNameUTF; while (*p != '\0') { if (*p == '/') *p = '.'; p++; } WARNF("Class.forName() failed to load '%s'. " "Use the -forcelinkclasses command line option " "or add <forceLinkClasses><pattern>%s</pattern></forceLinkClasses> " "to your robovm.xml file to link it in.", classNameUTF, classNameUTF); return NULL; } if (initializeBoolean) { rvmInitialize(env, clazz); if (rvmExceptionCheck(env)) return NULL; } return clazz; }
ObjectArray* Java_java_lang_Class_getDeclaredFields0(Env* env, Class* clazz, jboolean publicOnly) { if (CLASS_IS_PRIMITIVE(clazz) || CLASS_IS_ARRAY(clazz)) return NULL; Field* fields = rvmGetFields(env, clazz); if (rvmExceptionCheck(env)) return NULL; Field* field; jint length = 0; for (field = fields; field != NULL; field = field->next) { if (!publicOnly || FIELD_IS_PUBLIC(field)) { length++; } } ObjectArray* result = NULL; jint i = 0; for (field = fields; field != NULL; field = field->next) { if (!publicOnly || FIELD_IS_PUBLIC(field)) { Object* c = createFieldObject(env, field); if (!c) return NULL; if (!result) { result = rvmNewObjectArray(env, length, c->clazz, NULL, NULL); if (!result) return NULL; } result->values[i++] = c; } } return result; }
ObjectArray* Java_java_lang_Class_getDeclaredMethods0(Env* env, Class* clazz, jboolean publicOnly) { if (CLASS_IS_PRIMITIVE(clazz) || CLASS_IS_ARRAY(clazz)) return NULL; Method* methods = rvmGetMethods(env, clazz); if (rvmExceptionCheck(env)) return NULL; Method* method; jint length = 0; for (method = methods; method != NULL; method = method->next) { if (!METHOD_IS_CONSTRUCTOR(method) && !METHOD_IS_CLASS_INITIALIZER(method)) { if (!publicOnly || METHOD_IS_PUBLIC(method)) { length++; } } } ObjectArray* result = NULL; jint i = 0; for (method = methods; method != NULL; method = method->next) { if (!METHOD_IS_CONSTRUCTOR(method) && !METHOD_IS_CLASS_INITIALIZER(method)) { if (!publicOnly || METHOD_IS_PUBLIC(method)) { Object* c = createMethodObject(env, method); if (!c) return NULL; if (!result) { result = rvmNewObjectArray(env, length, c->clazz, NULL, NULL); if (!result) return NULL; } result->values[i++] = c; } } } return result; }
Method* rvmGetMethods(Env* env, Class* clazz) { if (clazz->_methods != &METHODS_NOT_LOADED) return clazz->_methods; // TODO: Double checked locking obtainClassLock(); if (clazz->_methods == &METHODS_NOT_LOADED) { env->vm->options->loadMethods(env, clazz); if (clazz->_methods == &METHODS_NOT_LOADED) { // The class has no methods clazz->_methods = NULL; } } if (rvmExceptionCheck(env)) clazz->_methods = &METHODS_NOT_LOADED; releaseClassLock(); if (rvmExceptionCheck(env)) return NULL; return clazz->_methods; }
Interface* rvmGetInterfaces(Env* env, Class* clazz) { if (clazz->_interfaces != &INTERFACES_NOT_LOADED) return clazz->_interfaces; // TODO: Double checked locking obtainClassLock(); if (clazz->_interfaces == &INTERFACES_NOT_LOADED) { env->vm->options->loadInterfaces(env, clazz); if (clazz->_interfaces == &INTERFACES_NOT_LOADED) { // The class has no interfaces clazz->_interfaces = NULL; } } if (rvmExceptionCheck(env)) clazz->_interfaces = &INTERFACES_NOT_LOADED; releaseClassLock(); if (rvmExceptionCheck(env)) return NULL; return clazz->_interfaces; }
Object* _bcCheckcastArray(Env* env, Class* arrayClass, Object* o) { if (!o) return o; ENTER; jboolean b = rvmIsAssignableFrom(env, o->clazz, arrayClass); if (!rvmExceptionCheck(env) && !b) { rvmThrowClassCastException(env, arrayClass, o->clazz); } LEAVE(o); }
Method* rvmGetMethod(Env* env, Class* clazz, const char* name, const char* desc) { Method* method = getMethod(env, clazz, name, desc); if (rvmExceptionCheck(env)) return NULL; if (!method) { rvmThrowNoSuchMethodError(env, name); return NULL; } return method; }
static Method* findMethod(Env* env, Class* clazz, const char* name, const char* desc) { Method* method = rvmGetMethods(env, clazz); if (rvmExceptionCheck(env)) return NULL; for (; method != NULL; method = method->next) { if (!strcmp(method->name, name) && !strcmp(method->desc, desc)) { return method; } } return NULL; }
Object* _bcLdcArrayClass(Env* env, Class** arrayClassPtr, char* name) { Class* arrayClass = *arrayClassPtr; if (arrayClass) return (Object*) arrayClass; ENTER; arrayClass = rvmFindClassUsingLoader(env, name, systemClassLoader); wrapClassNotFoundException(env, name); if (!rvmExceptionCheck(env)) { *arrayClassPtr = arrayClass; } LEAVE((Object*) arrayClass); }
Class* Java_java_lang_Class_getEnclosingClass(Env* env, Class* thiz) { Class* enclosingClass = rvmAttributeGetEnclosingClass(env, thiz); if (rvmExceptionCheck(env) && rvmExceptionOccurred(env)->clazz != java_lang_ClassNotFoundException) { return NULL; } if (!enclosingClass) { rvmExceptionClear(env); return rvmAttributeGetDeclaringClass(env, thiz); } return enclosingClass; }
Object* _bcLdcString(Env* env, Object** ptr, char* s, jint length) { Object* o = *ptr; if (o) return o; ENTER; o = rvmNewInternedStringUTF(env, s, length); if (!rvmExceptionCheck(env)) { *ptr = o; rvmRegisterDisappearingLink(env, (void**) ptr, o); } LEAVE(o); }
Object* _bcCheckcast(Env* env, ClassInfoHeader* header, Object* o) { if (!o) return o; ENTER; Class* clazz = ldcClass(env, header); if (clazz) { jboolean b = rvmIsAssignableFrom(env, o->clazz, clazz); if (!rvmExceptionCheck(env) && !b) { rvmThrowClassCastException(env, clazz, o->clazz); } } LEAVE(o); }
static void iterateAttributes(Env* env, void* attributes, jboolean (*f)(Env*, jbyte, void*, void*), void* data) { if (!attributes) return; jint i, j; jint length = 0; jint numParams = 0; jint count = getInt(&attributes); while (count > 0) { jbyte type = getByte(&attributes); if (!f(env, type, attributes, data)) { return; } if (rvmExceptionCheck(env)) return; switch (type) { case SOURCE_FILE: case SIGNATURE: attributes += sizeof(char*); break; case INNER_CLASS: attributes += 3 * sizeof(char*) + sizeof(jint); break; case ENCLOSING_METHOD: attributes += 3 * sizeof(char*); break; case EXCEPTIONS: length = getInt(&attributes); attributes += length * sizeof(char*); break; case ANNOTATION_DEFAULT: skipElementValue(&attributes); break; case RUNTIME_VISIBLE_ANNOTATIONS: length = getInt(&attributes); while (length > 0) { skipAnnotationElementValue(&attributes); length--; } break; case RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS: numParams = getInt(&attributes); for (i = 0; i < numParams; i++) { length = getInt(&attributes); for (j = 0; j < length; j++) { skipAnnotationElementValue(&attributes); } } break; } count--; } }
static Method* getMethod(Env* env, Class* clazz, const char* name, const char* desc) { if (!strcmp("<init>", name) || !strcmp("<clinit>", name)) { // Constructors and static initializers are not inherited so we shouldn't check with the superclasses. return findMethod(env, clazz, name, desc); } Class* c = clazz; for (c = clazz; c != NULL; c = c->superclass) { Method* method = findMethod(env, c, name, desc); if (rvmExceptionCheck(env)) return NULL; if (method) return method; } /* * Check with interfaces. * TODO: Should we really do this? Does the JNI GetMethodID() function do this? */ for (c = clazz; c != NULL; c = c->superclass) { Interface* interfaze = rvmGetInterfaces(env, c); if (rvmExceptionCheck(env)) return NULL; for (; interfaze != NULL; interfaze = interfaze->next) { Method* method = getMethod(env, interfaze->interfaze, name, desc); if (rvmExceptionCheck(env)) return NULL; if (method) return method; } } if (CLASS_IS_INTERFACE(clazz)) { /* * Class is an interface so check with java.lang.Object. * TODO: Should we really do this? Does the JNI GetMethodID() function do this? */ return getMethod(env, java_lang_Object, name, desc); } return NULL; }
Method* rvmFindMethodAtAddress(Env* env, void* address) { Class* clazz = env->vm->options->findClassAt(env, address); if (!clazz) return NULL; Method* method = rvmGetMethods(env, clazz); if (rvmExceptionCheck(env)) return NULL; for (; method != NULL; method = method->next) { void* start = method->impl; void* end = start + method->size; if (start && address >= start && address < end) { return method; } } // TODO: We should never end up here return NULL; }
ObjectArray* Java_java_lang_Class_getInterfaces(Env* env, Class* thiz) { Interface* interfaces = rvmGetInterfaces(env, thiz); if (rvmExceptionCheck(env)) return NULL; Interface* interfaze; jint length = 0; LL_FOREACH(interfaces, interfaze) { length++; } ObjectArray* result = rvmNewObjectArray(env, length, java_lang_Class, NULL, NULL); if (!result) return NULL; jint i = 0; LL_FOREACH(interfaces, interfaze) { result->values[i++] = (Object*) interfaze->interfaze; } return result; }
jboolean rvmIsAssignableFrom(Env* env, Class* s, Class* t) { // TODO: What if s or t are NULL? if (s == t || t == java_lang_Object) { return TRUE; } if (CLASS_IS_ARRAY(s)) { if (t == java_io_Serializable) { return TRUE; } if (t == java_lang_Cloneable) { return TRUE; } if (!CLASS_IS_ARRAY(t)) { return FALSE; } Class* componentType = s->componentType; if (CLASS_IS_PRIMITIVE(componentType)) { // s is a primitive array and can only be assigned to // class t if s == t or t == (Object|Serializable|Cloneable). But we // already know that s != t and t != (Object|Serializable|Cloneable) return FALSE; } return rvmIsAssignableFrom(env, componentType, t->componentType); } if (CLASS_IS_INTERFACE(t)) { // s or any of its parents must implement the interface t for (; s; s = s->superclass) { Interface* interface = rvmGetInterfaces(env, s); if (rvmExceptionCheck(env)) return FALSE; for (; interface != NULL; interface = interface->next) { if (rvmIsAssignableFrom(env, interface->interface, t)) { return TRUE; } } } return FALSE; } while (s && s != t) { s = s->superclass; } return s ? TRUE : FALSE; }
static void loadInterfaces(Env* env, Class* clazz) { ClassInfoHeader* header = lookupClassInfo(env, clazz->name, !clazz->classLoader || !clazz->classLoader->parent ? _bcBootClassesHash : _bcClassesHash); if (!header) return; ClassInfo ci; jint i; void* p = header; readClassInfo(&p, &ci); for (i = 0; i < ci.interfaceCount; i++) { const char* interfaceName = readInterfaceName(&p); Class* interface = rvmFindClassUsingLoader(env, interfaceName, clazz->classLoader); if (!interface) return; rvmAddInterface(env, clazz, interface); if (rvmExceptionCheck(env)) return; } }
static void signalHandler_npe_so(int signum, siginfo_t* info, void* context) { // SIGSEGV/SIGBUS are synchronous signals so we shouldn't have to worry about only calling // async-signal-safe functions here. Env* env = rvmGetEnv(); if (env && rvmIsNonNativeFrame(env)) { // We now know the fault occurred in non-native code. void* faultAddr = info->si_addr; void* stackAddr = env->currentThread->stackAddr; Class* exClass = NULL; if (faultAddr < stackAddr && faultAddr >= (void*) (stackAddr - THREAD_STACK_GUARD_SIZE)) { // StackOverflowError exClass = java_lang_StackOverflowError; } else { // At least on Linux x86 it seems like si_addr isn't always 0x0 even // if a read of address 0x0 triggered SIGSEGV so we assume // everything that isn't a stack overflow is a read of address 0x0 // and throw NullPointerException. exClass = java_lang_NullPointerException; } if (exClass) { Frame fakeFrame; fakeFrame.prev = (Frame*) getFramePointer((ucontext_t*) context); fakeFrame.returnAddress = getPC((ucontext_t*) context); Object* throwable = NULL; CallStack* callStack = captureCallStackFromFrame(env, &fakeFrame); if (callStack) { throwable = rvmAllocateObject(env, exClass); if (throwable) { rvmCallVoidClassMethod(env, exClass, throwableInitMethod, throwable, PTR_TO_LONG(callStack)); if (rvmExceptionCheck(env)) { throwable = NULL; } } } if (!throwable) { throwable = rvmExceptionClear(env); } rvmRaiseException(env, throwable); // Never returns! } } }
Env* rvmStartup(Options* options) { // TODO: Error handling TRACE("Initializing logging"); if (!rvmInitLog(options)) return NULL; #if defined(IOS) && (defined(RVM_ARMV7) || defined(RVM_THUMBV7)) // Enable IEEE-754 denormal support. // Without this the VFP treats numbers that are close to zero as zero itself. // See http://developer.apple.com/library/ios/#technotes/tn2293/_index.html. fenv_t fenv; fegetenv(&fenv); fenv.__fpscr &= ~__fpscr_flush_to_zero; fesetenv(&fenv); #endif // print PID if it was requested if(options->printPID) { pid_t pid = getpid(); if(options->pidFile) { FILE* f = fopen(options->pidFile, "w"); if (!f) return NULL; fprintf(f, "%d", pid); fclose(f); } else { fprintf(stderr, "[DEBUG] %s: pid=%d\n", LOG_TAG, pid); } } // setup the TCP channel socket and wait // for the debugger to connect if(options->enableHooks) { if(!rvmHookSetupTCPChannel(options)) return NULL; if(!rvmHookHandshake(options)) return NULL; } TRACE("Initializing GC"); if (!initGC(options)) return NULL; // Ignore SIGPIPE signals. SIGPIPE interrupts write() calls which we don't // want. Dalvik does this too in dalvikvm/Main.cpp. if (!ignoreSignal(SIGPIPE)) return NULL; // Ignore SIGXFSZ signals. SIGXFSZ is raised when writing beyond the RLIMIT_FSIZE // of the current process (at least on Darwin) using pwrite(). if (!ignoreSignal(SIGXFSZ)) return NULL; VM* vm = rvmCreateVM(options); if (!vm) return NULL; Env* env = rvmCreateEnv(vm); if (!env) return NULL; // TODO: What if we can't allocate Env? if (!initClasspathEntries(env, options->resourcesPath, options->rawBootclasspath, &options->bootclasspath)) return NULL; if (!initClasspathEntries(env, options->resourcesPath, options->rawClasspath, &options->classpath)) return NULL; // Call init on modules TRACE("Initializing classes"); if (!rvmInitClasses(env)) return NULL; TRACE("Initializing memory"); if (!rvmInitMemory(env)) return NULL; TRACE("Initializing methods"); if (!rvmInitMethods(env)) return NULL; TRACE("Initializing strings"); if (!rvmInitStrings(env)) return NULL; TRACE("Initializing monitors"); if (!rvmInitMonitors(env)) return NULL; TRACE("Initializing proxy"); if (!rvmInitProxy(env)) return NULL; TRACE("Initializing threads"); if (!rvmInitThreads(env)) return NULL; TRACE("Initializing attributes"); if (!rvmInitAttributes(env)) return NULL; TRACE("Initializing primitive wrapper classes"); if (!rvmInitPrimitiveWrapperClasses(env)) return NULL; TRACE("Initializing exceptions"); if (!rvmInitExceptions(env)) return NULL; TRACE("Initializing signals"); if (!rvmInitSignals(env)) return NULL; TRACE("Initializing JNI"); if (!rvmInitJNI(env)) return NULL; // Initialize the RoboVM rt JNI code // RT_JNI_OnLoad(&vm->javaVM, NULL); // Initialize the dalvik rt JNI code TRACE("Initializing dalvik's runtime JNI code"); registerCoreLibrariesJni((JNIEnv*) env); #ifdef DARWIN TRACE("Initializing JAR NSURLProtocol"); registerJARURLProtocol(); #endif TRACE("Creating system ClassLoader"); systemClassLoader = rvmGetSystemClassLoader(env); if (rvmExceptionOccurred(env)) goto error_system_ClassLoader; env->currentThread->threadObj->contextClassLoader = systemClassLoader; TRACE("Initialization done"); env->vm->initialized = TRUE; // Start Daemons TRACE("Starting Daemons"); java_lang_Daemons = rvmFindClassUsingLoader(env, "java/lang/Daemons", NULL); if (!java_lang_Daemons) goto error_daemons; java_lang_Daemons_start = rvmGetClassMethod(env, java_lang_Daemons, "start", "()V"); if (!java_lang_Daemons_start) goto error_daemons; rvmCallVoidClassMethod(env, java_lang_Daemons, java_lang_Daemons_start); if (rvmExceptionCheck(env)) goto error_daemons; TRACE("Daemons started"); jboolean errorDuringSetup = FALSE; //If our options has any properties, let's set them before we call our main. if (options->properties) { //First, find java.lang.System, which has the setProperty method. Class* clazz = rvmFindClassUsingLoader(env, "java/lang/System", NULL); if (clazz) { //Get the setProperty method. Method* method = rvmGetClassMethod(env, clazz, "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); if (method) { SystemProperty* property = options->properties; //Go through all of our properties and add each one in turn by calling setProperty. while (property != NULL) { Object* key = NULL; Object* value = NULL; //The key is not allowed to be an empty string, so don't set it if we don't get a key. if(property->key && strlen(property->key) > 0) { key = rvmNewStringUTF(env, property->key, -1); } else { FATAL("Cannot have empty key in system property."); errorDuringSetup = TRUE; break; } if (property->value) { value = rvmNewStringUTF(env, property->value, -1); } else { value = rvmNewStringUTF(env, "", -1); } if (key && value) { rvmCallObjectClassMethod(env, clazz, method, key, value); } else { if (!key) { FATALF("Error creating string from system property key: %s", property->key); } if (!value) { FATALF("Error creating string from system property value: %s", property->value); } errorDuringSetup = TRUE; break; } property = property->next; //Advance to the next property. } } } } return (errorDuringSetup) ? NULL : env; error_daemons: error_system_ClassLoader: rvmDetachCurrentThread(env->vm, TRUE, FALSE); return NULL; }
jboolean rvmHasMethod(Env* env, Class* clazz, const char* name, const char* desc) { Method* method = getMethod(env, clazz, name, desc); if (rvmExceptionCheck(env)) return FALSE; return method ? TRUE : FALSE; }
static jboolean ExceptionCheck(JNIEnv* env) { return rvmExceptionCheck((Env*) env); }
Env* rvmStartup(Options* options) { // TODO: Error handling TRACE("Initializing GC"); if (!initGC(options)) return NULL; VM* vm = rvmCreateVM(options); if (!vm) return NULL; Env* env = rvmCreateEnv(vm); if (!env) return NULL; // TODO: What if we can't allocate Env? if (!initClasspathEntries(env, options->basePath, options->rawBootclasspath, &options->bootclasspath)) return NULL; if (!initClasspathEntries(env, options->basePath, options->rawClasspath, &options->classpath)) return NULL; // Call init on modules TRACE("Initializing logging"); if (!rvmInitLog(env)) return NULL; TRACE("Initializing classes"); if (!rvmInitClasses(env)) return NULL; TRACE("Initializing methods"); if (!rvmInitMethods(env)) return NULL; TRACE("Initializing strings"); if (!rvmInitStrings(env)) return NULL; TRACE("Initializing monitors"); if (!rvmInitMonitors(env)) return NULL; TRACE("Initializing threads"); if (!rvmInitThreads(env)) return NULL; TRACE("Initializing attributes"); if (!rvmInitAttributes(env)) return NULL; TRACE("Initializing primitive wrapper classes"); if (!rvmInitPrimitiveWrapperClasses(env)) return NULL; TRACE("Initializing exceptions"); if (!rvmInitExceptions(env)) return NULL; TRACE("Initializing signals"); if (!rvmInitSignals(env)) return NULL; TRACE("Initializing memory"); if (!rvmInitMemory(env)) return NULL; // Initialize the RoboVM rt JNI code // RT_JNI_OnLoad(&vm->javaVM, NULL); // Initialize dalvik's JNIHelp code in libnativehelper TRACE("Initializing dalvik's libnativehelper"); registerJniHelp((JNIEnv*) env); // Initialize the dalvik rt JNI code TRACE("Initializing dalvik's runtime JNI code"); registerCoreLibrariesJni((JNIEnv*) env); TRACE("Creating system ClassLoader"); systemClassLoader = rvmGetSystemClassLoader(env); if (rvmExceptionOccurred(env)) goto error_system_ClassLoader; env->currentThread->threadObj->contextClassLoader = systemClassLoader; TRACE("Initialization done"); // Start Daemons TRACE("Starting Daemons"); java_lang_Daemons = rvmFindClassUsingLoader(env, "java/lang/Daemons", NULL); if (!java_lang_Daemons) goto error_daemons; java_lang_Daemons_start = rvmGetClassMethod(env, java_lang_Daemons, "start", "()V"); if (!java_lang_Daemons_start) goto error_daemons; rvmCallVoidClassMethod(env, java_lang_Daemons, java_lang_Daemons_start); if (rvmExceptionCheck(env)) goto error_daemons; TRACE("Daemons started"); return env; error_daemons: error_system_ClassLoader: rvmDetachCurrentThread(env->vm, TRUE); return NULL; }
static jboolean getAnnotationValue(Env* env, void** attributes, Class* expectedAnnotationClass, Object* classLoader, jvalue* result, jboolean ignoreClassNotFound) { char* annotationTypeName = getString(attributes); if (expectedAnnotationClass && strncmp(&annotationTypeName[1], expectedAnnotationClass->name, strlen(expectedAnnotationClass->name))) { return throwFormatError(env, rvmFromBinaryClassName(env, expectedAnnotationClass->name)); } Class* annotationClass = expectedAnnotationClass; if (!annotationClass) { annotationClass = rvmFindClassByDescriptor(env, annotationTypeName, classLoader); if (!annotationClass) { if (ignoreClassNotFound && rvmExceptionOccurred(env)->clazz == java_lang_ClassNotFoundException) { rvmExceptionClear(env); jint length = getInt(attributes); for (jint i = 0; i < length; i++) { getString(attributes); skipElementValue(attributes); } } return FALSE; } } // Find the annotation impl class Class* annotationImplClass = findAnnotationImplClass(env, annotationClass, classLoader); if (rvmExceptionCheck(env)) return FALSE; jint length = getInt(attributes); if (length == 0) { // No member values specified. Use a singleton instance. Method* factoryMethod = rvmGetClassMethod(env, annotationImplClass, "$createSingleton", "()Ljava/lang/Object;"); if (rvmExceptionCheck(env)) return FALSE; Object* annotationObject = rvmCallObjectClassMethod(env, annotationImplClass, factoryMethod); if (rvmExceptionCheck(env)) return FALSE; result->l = (jobject) annotationObject; return TRUE; } // Call the annotation impl $create() method Method* factoryMethod = rvmGetClassMethod(env, annotationImplClass, "$create", "()Ljava/lang/Object;"); if (rvmExceptionCheck(env)) return FALSE; Object* annotationObject = rvmCallObjectClassMethod(env, annotationImplClass, factoryMethod); if (rvmExceptionCheck(env)) return FALSE; jint i = 0; for (i = 0; i < length; i++) { char* name = getString(attributes); Method* method = getAnnotationValueMethod(env, annotationClass, name); if (rvmExceptionCheck(env)) return FALSE; if (!method) { skipElementValue(attributes); } else { const char* memberDesc = rvmGetReturnType(method->desc); Class* type = findType(env, memberDesc, method->clazz->classLoader); Object* value = NULL; if (!type) { value = rvmExceptionClear(env); } else { jvalue v = {0}; if (!parseElementValue(env, attributes, type, classLoader, &v)) { value = rvmExceptionClear(env); } else { value = rvmBox(env, type, &v); } } InstanceField* field = getAnnotationMemberField(env, annotationImplClass, method->name); if (!field) return FALSE; rvmSetObjectInstanceFieldValue(env, annotationObject, field, value); if (rvmExceptionCheck(env)) return FALSE; } } result->l = (jobject) annotationObject; return TRUE; }