emscripten/tests/nbody-java/xmlvm.c

566 строки
18 KiB
C

/*
* 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, &currentThreadId, 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);
}