| /* |
| * Copyright (c) 2002-2011 by XMLVM.org |
| * |
| * Project Info: http://www.xmlvm.org |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU Lesser General Public License as published by |
| * the Free Software Foundation; either version 2.1 of the License, or |
| * (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
| * License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
| * USA. |
| */ |
| |
| #include "xmlvm.h" |
| #include "xmlvm-util.h" |
| #include "java_lang_System.h" |
| #include "java_lang_Class.h" |
| #include "java_lang_String.h" |
| #include "java_lang_Throwable.h" |
| #include "org_xmlvm_runtime_FinalizerNotifier.h" |
| #include "org_xmlvm_runtime_XMLVMUtil.h" |
| #include <stdio.h> |
| #include <string.h> |
| |
| |
| XMLVM_STATIC_INITIALIZER_CONTROLLER* staticInitializerController; |
| |
| // This exception value is only used for the main thread. |
| // Since a call to Thread.currentThread() contains try-catch blocks, this must |
| // be defined before the "main" java.lang.Thread is defined. |
| XMLVM_JMP_BUF xmlvm_exception_env_main_thread; |
| |
| |
| #ifdef XMLVM_ENABLE_STACK_TRACES |
| |
| |
| #include "uthash.h" |
| |
| #define HASH_ADD_JAVA_LONG(head,javalongfield,add) \ |
| HASH_ADD(hh,head,javalongfield,sizeof(JAVA_LONG),add) |
| #define HASH_FIND_JAVA_LONG(head,findjavalong,out) \ |
| HASH_FIND(hh,head,findjavalong,sizeof(JAVA_LONG),out) |
| |
| // A map of type UTHash with a key of JAVA_LONG and value of JAVA_OBJECT |
| struct hash_struct { |
| JAVA_LONG key; |
| JAVA_OBJECT value; |
| UT_hash_handle hh; // makes this structure hashable |
| }; |
| |
| |
| // Map of thread id to its stack trace |
| struct hash_struct** threadToStackTraceMapPtr; |
| |
| |
| #endif |
| |
| |
| void xmlvm_init() |
| { |
| #ifdef XMLVM_ENABLE_STACK_TRACES |
| threadToStackTraceMapPtr = malloc(sizeof(struct hash_struct**)); |
| struct hash_struct* map = NULL; // This must be set to NULL according to the UTHash documentation |
| *threadToStackTraceMapPtr = map; |
| |
| JAVA_LONG nativeThreadId = (JAVA_LONG) pthread_self(); |
| createStackForNewThread(nativeThreadId); |
| #endif |
| |
| #ifndef XMLVM_NO_GC |
| //#ifdef DEBUG |
| // setenv("GC_PRINT_STATS", "1", 1); |
| //#endif |
| GC_INIT(); |
| GC_enable_incremental(); |
| #endif |
| |
| staticInitializerController = XMLVM_MALLOC(sizeof(XMLVM_STATIC_INITIALIZER_CONTROLLER)); |
| staticInitializerController->initMutex = XMLVM_MALLOC(sizeof(pthread_mutex_t)); |
| if (0 != pthread_mutex_init(staticInitializerController->initMutex, NULL)) { |
| XMLVM_ERROR("Error initializing static initializer mutex", __FILE__, __FUNCTION__, __LINE__); |
| } |
| |
| __INIT_org_xmlvm_runtime_XMLVMArray(); |
| java_lang_Class_initNativeLayer__(); |
| __INIT_java_lang_System(); |
| org_xmlvm_runtime_XMLVMUtil_init__(); |
| /* |
| * The implementation of the constant pool makes use of cross-compiled Java data structures. |
| * During initialization of the VM done up to this point there are some circular dependencies |
| * between class initializers of various classes and the constant pool that lead to some |
| * inconsistencies. The easiest way to fix this is to clear the constant pool cache. |
| */ |
| xmlvm_clear_constant_pool_cache(); |
| |
| #ifndef XMLVM_NO_GC |
| #ifndef __EMSCRIPTEN__ |
| GC_finalize_on_demand = 1; |
| GC_java_finalization = 1; |
| java_lang_Thread* finalizerThread = (java_lang_Thread*) org_xmlvm_runtime_FinalizerNotifier_startFinalizerThread__(); |
| GC_finalizer_notifier = org_xmlvm_runtime_FinalizerNotifier_finalizerNotifier__; |
| #endif |
| #endif |
| |
| reference_array = XMLVMUtil_NEW_ArrayList(); |
| } |
| |
| void xmlvm_destroy(java_lang_Thread* mainThread) |
| { |
| #ifdef __EMSCRIPTEN__ |
| return; // Let the JS engine handle clean up |
| #endif |
| |
| java_lang_Thread_threadTerminating__(mainThread); |
| |
| #ifdef XMLVM_ENABLE_STACK_TRACES |
| JAVA_LONG nativeThreadId = (JAVA_LONG) pthread_self(); |
| destroyStackForExitingThread(nativeThreadId); |
| #endif |
| |
| // Unregister the current thread. Only an explicitly registered |
| // thread (i.e. for which GC_register_my_thread() returns GC_SUCCESS) |
| // is allowed (and required) to call this function. (As a special |
| // exception, it is also allowed to once unregister the main thread.) |
| #ifndef XMLVM_NO_GC |
| GC_unregister_my_thread(); |
| #endif |
| |
| // Call pthread_exit(0) so that the main thread does not terminate until |
| // the other threads have finished |
| pthread_exit(0); |
| } |
| |
| /** |
| * Lock a mutex. If an error occurs, terminate the program. |
| */ |
| static void lockOrExit(char* className, pthread_mutex_t* mut) |
| { |
| int result = pthread_mutex_lock(mut); |
| if (0 != result) { |
| printf("Error locking mutex in %s: %i\n", className, result); |
| exit(1); |
| } |
| // else { printf("SUCCESSFUL mutex lock in %s\n", className); } |
| } |
| |
| /** |
| * Unlock a mutex. If an error occurs, terminate the program. |
| */ |
| static void unlockOrExit(char* className, pthread_mutex_t* mut) |
| { |
| int result = pthread_mutex_unlock(mut); |
| if (0 != result) { |
| printf("Error unlocking mutex in %s: %i\n", className, result); |
| exit(1); |
| } |
| // else { printf("SUCCESSFUL mutex unlock in %s\n", className); } |
| } |
| |
| /** |
| * Lock the static initializer mutex. |
| */ |
| void staticInitializerLock(void* tibDefinition) |
| { |
| char* className = ((struct __TIB_DEFINITION_TEMPLATE*)tibDefinition)->className; |
| lockOrExit(className, staticInitializerController->initMutex); |
| } |
| |
| /** |
| * Unlock the static initializer mutex. |
| */ |
| void staticInitializerUnlock(void* tibDefinition) |
| { |
| char* className = ((struct __TIB_DEFINITION_TEMPLATE*)tibDefinition)->className; |
| unlockOrExit(className, staticInitializerController->initMutex); |
| } |
| |
| int xmlvm_java_string_cmp(JAVA_OBJECT s1, const char* s2) |
| { |
| java_lang_String* str = (java_lang_String*) s1; |
| JAVA_INT len = str->fields.java_lang_String.count_; |
| if (len != strlen(s2)) { |
| return 0; |
| } |
| JAVA_INT offset = str->fields.java_lang_String.offset_; |
| org_xmlvm_runtime_XMLVMArray* value = (org_xmlvm_runtime_XMLVMArray*) str->fields.java_lang_String.value_; |
| JAVA_ARRAY_CHAR* valueArray = (JAVA_ARRAY_CHAR*) value->fields.org_xmlvm_runtime_XMLVMArray.array_; |
| for (int i = 0; i < len; i++) { |
| if (valueArray[i + offset] != s2[i]) { |
| return 0; |
| } |
| } |
| return 1; |
| } |
| |
| const char* xmlvm_java_string_to_const_char(JAVA_OBJECT s) |
| { |
| if (s == JAVA_NULL) { |
| return "null"; |
| } |
| java_lang_String* str = (java_lang_String*) s; |
| JAVA_INT len = str->fields.java_lang_String.count_; |
| char* cs = XMLVM_ATOMIC_MALLOC(len + 1); |
| JAVA_INT offset = str->fields.java_lang_String.offset_; |
| org_xmlvm_runtime_XMLVMArray* value = (org_xmlvm_runtime_XMLVMArray*) str->fields.java_lang_String.value_; |
| JAVA_ARRAY_CHAR* valueArray = (JAVA_ARRAY_CHAR*) value->fields.org_xmlvm_runtime_XMLVMArray.array_; |
| int i = 0; |
| for (i = 0; i < len; i++) { |
| cs[i] = valueArray[i + offset]; |
| } |
| cs[i] = '\0'; |
| return cs; |
| } |
| |
| JAVA_OBJECT xmlvm_create_java_string(const char* s) |
| { |
| java_lang_String* str = __NEW_java_lang_String(); |
| org_xmlvm_runtime_XMLVMArray* charArray = XMLVMArray_createFromString(s); |
| java_lang_String___INIT____char_1ARRAY(str, charArray); |
| return XMLVMUtil_getFromStringPool(str); |
| } |
| |
| JAVA_OBJECT xmlvm_create_java_string_array(int count, const char **s) |
| { |
| JAVA_OBJECT javaStrings[count]; |
| for (int i = 0; i < count; i++) { |
| javaStrings[i] = xmlvm_create_java_string(s[i]); |
| } |
| JAVA_OBJECT javaStringArray = XMLVMArray_createSingleDimension(__CLASS_java_lang_String, count); |
| XMLVMArray_fillArray(javaStringArray, javaStrings); |
| return javaStringArray; |
| } |
| |
| static JAVA_OBJECT* stringConstants = JAVA_NULL; |
| |
| JAVA_OBJECT xmlvm_create_java_string_from_pool(int pool_id) |
| { |
| if (stringConstants == JAVA_NULL) { |
| // TODO: use XMLVM_ATOMIC_MALLOC? |
| stringConstants = XMLVM_MALLOC(xmlvm_constant_pool_size * sizeof(JAVA_OBJECT)); |
| XMLVM_BZERO(stringConstants, xmlvm_constant_pool_size * sizeof(JAVA_OBJECT)); |
| } |
| if (stringConstants[pool_id] != JAVA_NULL) { |
| return stringConstants[pool_id]; |
| } |
| java_lang_String* str = __NEW_java_lang_String(); |
| org_xmlvm_runtime_XMLVMArray* charArray = XMLVMArray_createSingleDimensionWithData(__CLASS_char, |
| xmlvm_constant_pool_length[pool_id], |
| (JAVA_OBJECT) xmlvm_constant_pool_data[pool_id]); |
| java_lang_String___INIT____char_1ARRAY(str, charArray); |
| JAVA_OBJECT poolStr = XMLVMUtil_getFromStringPool(str); |
| stringConstants[pool_id] = poolStr; |
| return poolStr; |
| } |
| |
| void xmlvm_clear_constant_pool_cache() |
| { |
| XMLVM_BZERO(stringConstants, xmlvm_constant_pool_size * sizeof(JAVA_OBJECT)); |
| } |
| |
| |
| //--------------------------------------------------------------------------------------------- |
| // XMLVMClass |
| |
| JAVA_OBJECT XMLVM_CREATE_CLASS_OBJECT(void* tib) |
| { |
| JAVA_OBJECT clazz = __NEW_java_lang_Class(); |
| java_lang_Class___INIT____java_lang_Object(clazz, tib); |
| return clazz; |
| } |
| |
| |
| JAVA_OBJECT XMLVM_CREATE_ARRAY_CLASS_OBJECT(JAVA_OBJECT baseType) |
| { |
| __TIB_DEFINITION_org_xmlvm_runtime_XMLVMArray* tib = XMLVM_MALLOC(sizeof(__TIB_DEFINITION_org_xmlvm_runtime_XMLVMArray)); |
| XMLVM_MEMCPY(tib, &__TIB_org_xmlvm_runtime_XMLVMArray, sizeof(__TIB_DEFINITION_org_xmlvm_runtime_XMLVMArray)); |
| tib->flags = XMLVM_TYPE_ARRAY; |
| tib->baseType = baseType; |
| tib->arrayType = JAVA_NULL; |
| JAVA_OBJECT clazz = __NEW_java_lang_Class(); |
| java_lang_Class___INIT____java_lang_Object(clazz, tib); |
| tib->clazz = clazz; |
| // Set the arrayType in in baseType to this newly created array type class |
| java_lang_Class* baseTypeClass = (java_lang_Class*) baseType; |
| __TIB_DEFINITION_TEMPLATE* baseTypeTIB = (__TIB_DEFINITION_TEMPLATE*) baseTypeClass->fields.java_lang_Class.tib_; |
| baseTypeTIB->arrayType = clazz; |
| return clazz; |
| } |
| |
| |
| int XMLVM_ISA(JAVA_OBJECT obj, JAVA_OBJECT clazz) |
| { |
| if (obj == JAVA_NULL) { |
| return 0; |
| } |
| |
| int dimension_tib1 = 0; |
| int dimension_tib2 = 0; |
| __TIB_DEFINITION_TEMPLATE* tib1 = (__TIB_DEFINITION_TEMPLATE*) ((java_lang_Object*) obj)->tib; |
| __TIB_DEFINITION_TEMPLATE* tib2 = (__TIB_DEFINITION_TEMPLATE*) ((java_lang_Class*) clazz)->fields.java_lang_Class.tib_; |
| |
| if (tib1 == &__TIB_org_xmlvm_runtime_XMLVMArray) { |
| java_lang_Class* clazz = ((org_xmlvm_runtime_XMLVMArray*) obj)->fields.org_xmlvm_runtime_XMLVMArray.type_; |
| tib1 = clazz->fields.java_lang_Class.tib_; |
| } |
| |
| while (tib1->baseType != JAVA_NULL) { |
| tib1 = ((java_lang_Class*) tib1->baseType)->fields.java_lang_Class.tib_; |
| dimension_tib1++; |
| } |
| |
| while (tib2->baseType != JAVA_NULL) { |
| tib2 = ((java_lang_Class*) tib2->baseType)->fields.java_lang_Class.tib_; |
| dimension_tib2++; |
| } |
| |
| if (dimension_tib1 < dimension_tib2) { |
| return 0; |
| } |
| |
| while (tib1 != JAVA_NULL) { |
| if (tib1 == tib2) { |
| return 1; |
| } |
| // Check all implemented interfaces |
| int i; |
| for (i = 0; i < tib1->numImplementedInterfaces; i++) { |
| if (tib1->implementedInterfaces[0][i] == tib2) { |
| return 1; |
| } |
| } |
| tib1 = tib1->extends; |
| } |
| return 0; |
| } |
| |
| //--------------------------------------------------------------------------------------------- |
| // Stack traces |
| |
| #ifdef XMLVM_ENABLE_STACK_TRACES |
| |
| void createStackForNewThread(JAVA_LONG threadId) |
| { |
| struct hash_struct *s = malloc(sizeof(struct hash_struct)); |
| s->key = threadId; |
| |
| XMLVM_STACK_TRACE_CURRENT* newStack = malloc(sizeof(XMLVM_STACK_TRACE_CURRENT)); |
| newStack->stackSize = 0; |
| newStack->topOfStack = NULL; |
| |
| s->value = newStack; |
| HASH_ADD_JAVA_LONG((struct hash_struct *)*threadToStackTraceMapPtr, key, s); |
| } |
| |
| void destroyStackForExitingThread(JAVA_LONG threadId) |
| { |
| struct hash_struct *s; |
| HASH_FIND_JAVA_LONG((struct hash_struct *)*threadToStackTraceMapPtr, &threadId, s); |
| if (s == NULL) { |
| printf("ERROR: Unable to destroy stack trace for exiting thread!\n"); |
| exit(-1); |
| } else { |
| HASH_DEL((struct hash_struct *)*threadToStackTraceMapPtr, s); |
| free(s->value); |
| free(s); |
| } |
| } |
| |
| XMLVM_STACK_TRACE_CURRENT* getCurrentStackTrace() |
| { |
| JAVA_LONG currentThreadId = (JAVA_LONG)pthread_self(); |
| struct hash_struct *s; |
| HASH_FIND_JAVA_LONG((struct hash_struct *)*threadToStackTraceMapPtr, ¤tThreadId, s); |
| if (s == NULL) { |
| printf("ERROR: Unable to find stack trace for current thread!\n"); |
| exit(-1); |
| } |
| return (XMLVM_STACK_TRACE_CURRENT*)s->value; |
| } |
| |
| void xmlvmEnterMethod(XMLVM_STACK_TRACE_CURRENT* threadStack, const char* className, const char* methodName, const char* fileName) |
| { |
| //printf("Entering method %s\n", className); |
| |
| XMLVM_STACK_TRACE_ELEMENT* newLocationElement = malloc(sizeof(XMLVM_STACK_TRACE_ELEMENT)); |
| newLocationElement->className = className; |
| newLocationElement->methodName = methodName; |
| newLocationElement->fileName = fileName; |
| newLocationElement->lineNumber = -2; |
| |
| XMLVM_STACK_TRACE_LINK* link = malloc(sizeof(XMLVM_STACK_TRACE_LINK)); |
| link->nextLink = threadStack->topOfStack; |
| if (threadStack->topOfStack != NULL) { |
| link->element = threadStack->topOfStack->currentLocation; |
| } |
| link->currentLocation = newLocationElement; |
| |
| // Push what was the current location onto the stack and set the new current location |
| threadStack->stackSize++; |
| threadStack->topOfStack = link; |
| } |
| |
| void xmlvmSourcePosition(XMLVM_STACK_TRACE_CURRENT* threadStack, const char* fileName, int lineNumber) |
| { |
| //printf("Source position update %i\n", lineNumber); |
| |
| threadStack->topOfStack->currentLocation->fileName = fileName; |
| threadStack->topOfStack->currentLocation->lineNumber = lineNumber; |
| } |
| |
| void xmlvmExitMethod(XMLVM_STACK_TRACE_CURRENT* threadStack) |
| { |
| //printf("Exiting method\n"); |
| |
| XMLVM_STACK_TRACE_LINK* linkToDestroy = threadStack->topOfStack; |
| threadStack->topOfStack = linkToDestroy->nextLink; |
| threadStack->stackSize--; |
| |
| free(linkToDestroy->currentLocation); |
| free(linkToDestroy); |
| } |
| |
| void xmlvmUnwindException(XMLVM_STACK_TRACE_CURRENT* threadStack, int unwindToStackSize) |
| { |
| while (unwindToStackSize + 1 < threadStack->stackSize) { |
| //printf("Unwinding stack after catching an exception: %i > %i\n", unwindToStackSize, threadStack->stackSize); |
| xmlvmExitMethod(threadStack); |
| } |
| } |
| |
| #endif |
| |
| |
| #ifdef XMLVM_ENABLE_CLASS_LOGGING |
| //--------------------------------------------------------------------------------------------- |
| // Reflection/Class Usage logging |
| |
| FILE *logFile = 0; |
| int useLogging = 1; |
| |
| void xmlvmClassUsed(const char *prefix, const char *className) { |
| if (useLogging && (logFile == 0)) { |
| logFile = fopen("touched_classes.txt", "w"); |
| } |
| if (useLogging && (logFile != 0)) { |
| fprintf(logFile, "%s:%s\n", prefix, className); |
| } else { |
| useLogging = 0; // Failed to open file, so stop |
| } |
| } |
| |
| #endif |
| |
| //--------------------------------------------------------------------------------------------- |
| // XMLVMArray |
| |
| |
| JAVA_OBJECT XMLVMArray_createSingleDimension(JAVA_OBJECT type, JAVA_INT size) |
| { |
| return org_xmlvm_runtime_XMLVMArray_createSingleDimension___java_lang_Class_int(type, size); |
| } |
| |
| JAVA_OBJECT XMLVMArray_createSingleDimensionWithData(JAVA_OBJECT type, JAVA_INT size, JAVA_OBJECT data) |
| { |
| return org_xmlvm_runtime_XMLVMArray_createSingleDimensionWithData___java_lang_Class_int_java_lang_Object(type, size, data); |
| } |
| |
| |
| JAVA_OBJECT XMLVMArray_createMultiDimensions(JAVA_OBJECT type, JAVA_OBJECT dimensions) |
| { |
| return org_xmlvm_runtime_XMLVMArray_createMultiDimensions___java_lang_Class_org_xmlvm_runtime_XMLVMArray(type, dimensions); |
| } |
| |
| JAVA_OBJECT XMLVMArray_createFromString(const char* str) |
| { |
| int len = strlen(str); |
| int size = len * sizeof(JAVA_ARRAY_CHAR); |
| int i; |
| JAVA_ARRAY_CHAR* data = XMLVM_ATOMIC_MALLOC(size); |
| for (i = 0; i < len; i++) { |
| data[i] = *str++; |
| } |
| return XMLVMArray_createSingleDimensionWithData(__CLASS_char, len, data); |
| } |
| |
| void XMLVMArray_fillArray(JAVA_OBJECT array, void* data) |
| { |
| org_xmlvm_runtime_XMLVMArray_fillArray___org_xmlvm_runtime_XMLVMArray_java_lang_Object(array, data); |
| } |
| |
| int XMLVMArray_count(JAVA_OBJECT array) |
| { |
| org_xmlvm_runtime_XMLVMArray* a = (org_xmlvm_runtime_XMLVMArray*) array; |
| return a->fields.org_xmlvm_runtime_XMLVMArray.length_; |
| } |
| |
| void xmlvm_unhandled_exception() |
| { |
| java_lang_Thread* curThread; |
| curThread = (java_lang_Thread*) java_lang_Thread_currentThread__(); |
| JAVA_OBJECT exception = curThread->fields.java_lang_Thread.xmlvmException_; |
| |
| JAVA_OBJECT thread_name; |
| #ifdef XMLVM_VTABLE_IDX_java_lang_Thread_getName__ |
| thread_name = ((Func_OO) ((java_lang_Thread*) curThread)->tib->vtable[XMLVM_VTABLE_IDX_java_lang_Thread_getName__])(curThread); |
| #else |
| thread_name = java_lang_Thread_getName__(curThread); |
| #endif |
| |
| #ifdef XMLVM_ENABLE_STACK_TRACES |
| |
| printf("Exception in thread \"%s\" ", |
| xmlvm_java_string_to_const_char(thread_name)); |
| java_lang_Throwable_printStackTrace__(exception); |
| |
| #else |
| |
| JAVA_OBJECT message; |
| #ifdef XMLVM_VTABLE_IDX_java_lang_Throwable_getMessage__ |
| message = ((Func_OO) ((java_lang_Throwable*) exception)->tib->vtable[XMLVM_VTABLE_IDX_java_lang_Throwable_getMessage__])(exception); |
| #else |
| message = java_lang_Throwable_getMessage__(exception); |
| #endif |
| |
| JAVA_OBJECT exception_class; |
| #ifdef XMLVM_VTABLE_IDX_java_lang_Object_getClass__ |
| exception_class = ((Func_OO) ((java_lang_Object*) exception)->tib->vtable[XMLVM_VTABLE_IDX_java_lang_Object_getClass__])(exception); |
| #else |
| exception_class = java_lang_Object_getClass__(exception); |
| #endif |
| |
| JAVA_OBJECT class_name; |
| #ifdef XMLVM_VTABLE_IDX_java_lang_Class_getName__ |
| class_name = ((Func_OO) ((java_lang_Class*) exception_class)->tib->vtable[XMLVM_VTABLE_IDX_java_lang_Class_getName__])(exception_class); |
| #else |
| class_name = java_lang_Class_getName__(exception_class); |
| #endif |
| |
| printf("Exception in thread \"%s\" %s: %s\n", |
| xmlvm_java_string_to_const_char(thread_name), |
| xmlvm_java_string_to_const_char(class_name), |
| xmlvm_java_string_to_const_char(message)); |
| |
| #endif |
| } |
| |
| void xmlvm_unimplemented_native_method() |
| { |
| XMLVM_ERROR("Unimplemented native method", __FILE__, __FUNCTION__, __LINE__); |
| } |
| |
| void XMLVM_ERROR(const char* msg, const char* file, const char* function, int line) |
| { |
| printf("XMLVM Error: %s: (%s):%s:%d\n", msg, function, file, line); |
| exit(-1); |
| } |
| |