diff --git a/jtransc-gen-common-tests/src/threading/ThreadingTest.java b/jtransc-gen-common-tests/src/threading/ThreadingTest.java index 3229addd..9ef56c0e 100644 --- a/jtransc-gen-common-tests/src/threading/ThreadingTest.java +++ b/jtransc-gen-common-tests/src/threading/ThreadingTest.java @@ -5,6 +5,7 @@ public class ThreadingTest { static public void main(String[] args) { + testCurrentThread(); ArrayList logs = new ArrayList(); long start = System.currentTimeMillis(); for (int n = 0; n < 3; n++) { @@ -34,4 +35,32 @@ public void run() { System.out.println(log); } } + + private static void testCurrentThread() { + System.out.println(Thread.currentThread() == null); + System.out.println(Thread.currentThread().getName() == null); + System.out.println(Thread.currentThread().isDaemon()); + Thread t1 = new Thread() { + public void run() { + System.out.println(Thread.currentThread().getName()); + Thread t2 = new Thread() { + public void run() { + System.out.println(Thread.currentThread().getName()); + } + }; + t2.start(); + try { + t2.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + t1.start(); + try { + t1.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt b/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt index 4b6ab659..470952ef 100644 --- a/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt +++ b/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt @@ -424,6 +424,7 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { line("try") { line("N::startup();") line(genStaticConstructorsSorted()) + line("initMainThread();") val callMain = buildMethod(program[AstMethodRef(program.entrypoint, "main", AstType.METHOD(AstType.VOID, listOf(ARRAY(AstType.STRING))))]!!, static = true) line("$callMain(N::strEmptyArray());") @@ -434,14 +435,14 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { line("catch (std::wstring s)") { line("""std::wcout << L"ERROR std::wstring " << s << L"\n";""") } - //line("catch (java_lang_Throwable *s)") { + //line("catch (const java_lang_Throwable& s)") { // line("""std::wcout << L"${"java.lang.Throwable".fqname.targetName}:" << L"\n";""") - // line("""printf("Exception: %p\n", (void*)s);""") - //} - //line("catch (p_java_lang_Object s)") { - // val toStringMethod = program["java.lang.Object".fqname].getMethodWithoutOverrides("toString")!!.targetName - // line("""std::wcout << L"ERROR p_java_lang_Object " << N::istr2(s->$toStringMethod()) << L"\n";""") + //line("""printf("Exception: %p\n", (void*)s);""") //} + line("catch (${"java.lang.Object".fqname.targetName}* s)") { + val toStringMethod = program["java.lang.Object".fqname].getMethodWithoutOverrides("toString")!!.targetName + line("""std::wcout << L"ERROR p_java_lang_Object " << N::istr2(s->$toStringMethod()) << L"\n";""") + } //line("catch (...)") { // line("""std::wcout << L"ERROR unhandled unknown exception\n";""") //} @@ -666,7 +667,7 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { line(defaultBody) } line("#endif") - } else if (method.isNative && bodies.isEmpty() && method.name.startsWith("dooFoo")) { + } else if (method.isNative && bodies.isEmpty()) { line(genJniMethod(method)) } else { line(defaultBody) @@ -721,7 +722,22 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { val arg = method.methodType.args[i].type sb2.append(", ${genJavaToJniCast(arg)}p${i}") } - line("return ${genJniToJavaCast(method.actualRetType)}fptr(N::getJniEnv(), NULL $sb2);") + //line("N::enterNativeFunction(N::getThreadEnv()->currentThread)") + if (method.actualRetType != AstType.VOID) line("${method.actualRetType.targetNameRef} result;") + line("try") { + if (method.actualRetType == AstType.VOID) { + line("fptr((JNIEnv*)N::getThreadEnv(), NULL $sb2);") + } else { + line("result = ${genJniToJavaCast(method.actualRetType)}fptr((JNIEnv*)N::getThreadEnv(), NULL $sb2);") + } + } + line("catch(${"java.lang.Object".fqname.targetName}* e)"){ + line("abort();") + } + line("N::exitNative(N::getThreadEnv());") + if (method.actualRetType == AstType.VOID) line("return;") + else line("return result;") + //line("JNI: \"Empty BODY : ${method.containingClass.name}::${method.name}::${method.desc}\";") } @@ -1184,4 +1200,9 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { override fun genStmMonitorEnter(stm: AstStm.MONITOR_ENTER) = Indenter("N::monitorEnter(" + stm.expr.genExpr() + ");") override fun genStmMonitorExit(stm: AstStm.MONITOR_EXIT) = Indenter("N::monitorExit(" + stm.expr.genExpr() + ");") + + //override fun genStmThrow(stm: AstStm.THROW, last: Boolean) = Indenter("N::throwException(${stm.exception.genExpr()});") + //override fun genStmRethrow(stm: AstStm.RETHROW, last: Boolean) = Indenter("""N::throwException(J__i__exception__);""") + override open fun genStmThrow(stm: AstStm.THROW, last: Boolean) = Indenter("N::throwJavaException(${stm.exception.genExpr()});") + override open fun genStmRethrow(stm: AstStm.RETHROW, last: Boolean) = Indenter("""N::throwJavaException(J__i__exception__);""") } diff --git a/jtransc-gen-haxe/test/HaxeTest.kt b/jtransc-gen-haxe/test/HaxeTest.kt index 1e6ec4a8..4f304c14 100644 --- a/jtransc-gen-haxe/test/HaxeTest.kt +++ b/jtransc-gen-haxe/test/HaxeTest.kt @@ -32,6 +32,7 @@ import jtransc.jtransc.nativ.JTranscHaxeNativeMixedTest import jtransc.micro.MicroHelloWorld import org.junit.Ignore import org.junit.Test +import threading.ThreadingTest class HaxeTest : _Base() { override val DEFAULT_TARGET = HaxeTarget() @@ -59,6 +60,8 @@ class HaxeTest : _Base() { @Test fun testThreadCpp() = testClass(Params(clazz = ThreadTest::class.java, minimize = false, lang = "cpp", log = null, debug = true)) + @Test fun testThreading() = testClass(Params(clazz = ThreadingTest::class.java, minimize = false, lang = "cpp", log = false, debug = true)) + @Test fun haxeNativeCallTest() = testNativeClass(""" true true diff --git a/jtransc-rt-core/src/com/jtransc/JTranscSystem.java b/jtransc-rt-core/src/com/jtransc/JTranscSystem.java index cf52dc20..e1bd3c75 100644 --- a/jtransc-rt-core/src/com/jtransc/JTranscSystem.java +++ b/jtransc-rt-core/src/com/jtransc/JTranscSystem.java @@ -165,6 +165,11 @@ public static boolean isSys() { return TRUE; } + @HaxeMethodBody(target = "cpp", value = "return true;") + public static boolean isHaxeCpp() { + return FALSE; + } + @JTranscMethodBody(target = "cpp", value = "return true;") public static boolean isCpp() { return FALSE; diff --git a/jtransc-rt-core/src/com/jtransc/io/JTranscConsole.java b/jtransc-rt-core/src/com/jtransc/io/JTranscConsole.java index 5bc0f890..a78053f0 100644 --- a/jtransc-rt-core/src/com/jtransc/io/JTranscConsole.java +++ b/jtransc-rt-core/src/com/jtransc/io/JTranscConsole.java @@ -170,6 +170,7 @@ static public synchronized void log(double v) { @JTranscMethodBodyList({ @JTranscMethodBody(target = "php", value = "echo \"$p0\\n\";"), @JTranscMethodBody(target = "js", value = "console.error('' + p0);"), + @JTranscMethodBody(target = "cpp", value = "std::wcerr << N::istr2({% SMETHOD java.lang.String:valueOf:(Ljava/lang/Object;)Ljava/lang/String; %}(p0)) << L\"\\n\";"), @JTranscMethodBody(target = "cs", value = "Console.Error.WriteLine(p0);"), @JTranscMethodBody(target = "as3", value = "trace(p0);"), @JTranscMethodBody(target = "dart", value = "print(p0);"), diff --git a/jtransc-rt-core/src/j/ProgramReflection.java b/jtransc-rt-core/src/j/ProgramReflection.java index 8fe326aa..bbb22fed 100644 --- a/jtransc-rt-core/src/j/ProgramReflection.java +++ b/jtransc-rt-core/src/j/ProgramReflection.java @@ -1,5 +1,6 @@ package j; +import com.jtransc.JTranscSystem; import com.jtransc.ds.FastIntMap; import com.jtransc.ds.FastStringMap; @@ -327,7 +328,10 @@ static public MemberInfo getMethodInfo(int classId, int methodId) { //native static public Class getClassByInfo(ClassInfo info); - native static public Method getMethodByInfo(Class clazz, MemberInfo info); + static public Method getMethodByInfo(Class clazz, MemberInfo info){ + JTranscSystem.checkInJVM("ProgramReflection::getMethodByInfo should've been replaced by plugin!"); + return null; + } static public Class getClassById(int classId) { ProgramReflection._ensure(); diff --git a/jtransc-rt/resources/cpp/Base.cpp b/jtransc-rt/resources/cpp/Base.cpp index 1540132a..ec086322 100644 --- a/jtransc-rt/resources/cpp/Base.cpp +++ b/jtransc-rt/resources/cpp/Base.cpp @@ -191,6 +191,9 @@ typedef java_lang_Object* JAVA_OBJECT; #define GET_OBJECT(type, obj) (dynamic_cast(obj)) #define GET_OBJECT2(ptype, obj) (dynamic_cast(obj)) #define GET_OBJECT_NPE(type, obj) GET_OBJECT(type, N::ensureNpe(obj)) +#define ENTER_JAVA(env) try{ +#define LEAVE_JAVA(env) } catch( {% CLASS java.lang.Object %}* e){ N::leaveJava(env, e);} +//#define LEAVE_JAVA_VOID(env) #ifdef DEBUG #define CHECK_NPE 1 @@ -205,8 +208,10 @@ struct N; // Headers struct Env { - JNIEnv jni; - // + const struct JNINativeInterface_ *functions; + //JNIEnv jni; + JAVA_OBJECT throwable; + {% CLASS java.lang.Thread %}* currentThread; }; //template p_java_lang_Object CC_GET_OBJECT(T t) { @@ -242,8 +247,11 @@ template TTo CC_CHECK_CAST1(void* i, int typeId, const char *from // return res; //} + +enum class ThreadState { thread_in_java, thread_in_native }; +static __thread Env env; struct N { public: - static Env env; + static int64_t NAN_LONG; static double NAN_DOUBLE; @@ -260,6 +268,7 @@ struct N { public: static const int64_t MAX_INT64 = (int64_t)0x7FFFFFFFFFFFFFFF; static JAVA_OBJECT resolveClass(std::wstring str); + static JAVA_OBJECT resolveClass(JAVA_OBJECT str); inline static int64_t lnew(int32_t high, int32_t low); static JT_BOOL is(JAVA_OBJECT obj, int32_t type); template inline static bool is(T* obj, int32_t type) { return is((JAVA_OBJECT)obj, type); } @@ -392,7 +401,13 @@ struct N { public: static JAVA_OBJECT newFloatArray(); static JAVA_OBJECT newDoubleArray(); - static JNIEnv* getJniEnv(); + static Env* getThreadEnv(); + [[noreturn]] static void throwJavaException(JAVA_OBJECT throwable); + static void throwJniException(JAVA_OBJECT throwable); + static JAVA_OBJECT newExceptionInternal(JAVA_OBJECT clazz); + static JAVA_OBJECT newExceptionInternal(JAVA_OBJECT clazz, const char* message); + static void exitNative(Env* env); + static void leaveJava(Env* env, JAVA_OBJECT newThrowable); static void monitorEnter(JAVA_OBJECT obj); static void monitorExit(JAVA_OBJECT obj); @@ -735,6 +750,10 @@ JAVA_OBJECT N::resolveClass(std::wstring str) { return {% SMETHOD java.lang.Class:forName0 %}(N::str(str)); }; +JAVA_OBJECT N::resolveClass(JAVA_OBJECT str) { + return {% SMETHOD java.lang.Class:forName0 %}(str); +}; + int64_t N::lnew(int32_t high, int32_t low) { return (((int64_t)high) << 32) | (((int64_t)low) << 0); }; @@ -1271,6 +1290,74 @@ void N::monitorExit(JAVA_OBJECT obj){ //std::wcout << L"N::monitorExit[2]\n"; } + +extern JAVA_OBJECT jtvmNewObjectA(Env* env, JAVA_OBJECT clazz, JAVA_OBJECT method, const jvalue *args); +extern JAVA_OBJECT jtvmGetInstanceMethod(Env* env, JAVA_OBJECT clazz_, const char *name, const char *sig); +extern JAVA_OBJECT jtvmFindClass(Env* env, const char* sig); + +JAVA_OBJECT N::newExceptionInternal(JAVA_OBJECT clazz){ + Env* env = N::getThreadEnv(); + JAVA_OBJECT ctor = jtvmGetInstanceMethod(env, clazz, "", "()V"); + if (!ctor) return nullptr; + jvalue args[0]; + jobject e = (jobject)jtvmNewObjectA(env, clazz, ctor, args); + return (JAVA_OBJECT)e; +} + +JAVA_OBJECT N::newExceptionInternal(JAVA_OBJECT clazz, const char* message){ + Env* env = N::getThreadEnv(); + JAVA_OBJECT ctor = jtvmGetInstanceMethod(env, clazz, "", "(Ljava/lang/String;)V"); + if (!ctor) return nullptr; + //Object* string = NULL; + // TODO: Check that clazz != NULL? + //if (message) { + // string = rvmNewStringUTF(env, message, -1); + // if (!string) return FALSE; + //} + jvalue args[1]; + args[0].l = (jobject) N::str(message); + jobject e = (jobject)jtvmNewObjectA(env, clazz, ctor, args); + return (JAVA_OBJECT)e; +} + +void N::throwJavaException(JAVA_OBJECT throwable){ + Env* env = N::getThreadEnv(); + if(throwable == nullptr) { + JAVA_OBJECT newThrowable = N::newExceptionInternal(jtvmFindClass(env, "java/lang/NullPointerException")); + if(newThrowable != nullptr) N::throwJavaException(newThrowable); + else abort(); + } + throw throwable; +} + +void N::throwJniException(JAVA_OBJECT throwable){ + + //std::cerr << "Inside throwException"; + Env* env = N::getThreadEnv(); + //assert(env != NULL, "ENV should never be null!"); + //assert(env->currentThread != NULL, "Current Thread should never be null!"); + //if(env->currentThread->state == ThreadState::thread_in_java) { + //std::cerr << "Inside java code"; + //throw throwable; + // env->throwable = throwable; + //} else if(env->currentThread) { + //std::cerr << "Inside jni code"; + // env->throwable = throwable; + //} else { + // throw throwable; + //abort(/*"N::throwException should not reach this branch!"**/); + //} + env->throwable = throwable; +} + +void N::exitNative(Env* env){ + if(env->throwable) throw env->throwable; +} + +void N::leaveJava(Env* env, JAVA_OBJECT newThrowable){ + env->throwable = newThrowable; +} + void SIGSEGV_handler(int signal) { std::wcout << L"invalid memory access (segmentation fault)\n"; throw L"invalid memory access (segmentation fault)"; @@ -1362,7 +1449,9 @@ void SIGFPE_handler(int signal) { JAVA_OBJECT nativeLibsRaw = GET_OBJECT({% CLASS java.lang.ClassLoader %}, classLoader)->{% METHOD java.lang.ClassLoader:getNativeLibs %}(); auto nativeLibs = GET_OBJECT({% CLASS java.util.ArrayList %}, nativeLibsRaw); jint size = nativeLibs->{% METHOD java.util.ArrayList:size %}(); + //__asm__("int3"); for(jint i = 0; i < size; i++){ + auto nativeLib = GET_OBJECT({% CLASS java.lang.ClassLoader$NativeLib %}, nativeLibs->{% METHOD java.util.ArrayList:get %}(i)); auto handle = jlong_to_ptr(nativeLib->{% FIELD java.lang.ClassLoader$NativeLib:handle %}); @@ -1384,39 +1473,141 @@ void SIGFPE_handler(int signal) { return nullptr; } jmethodID JNICALL FromReflectedMethod(JNIEnv *env, jobject method){ - return (jmethodID)method; // TODO check requirements and other stuff + jmethodID result; + ENTER_JAVA((Env*) env); + result = (jmethodID)method; // TODO check requirements and other stuff + LEAVE_JAVA((Env*) env); + return result; } jfieldID JNICALL FromReflectedField(JNIEnv *env, jobject field){ - return (jfieldID)field; // TODO check requirements and other stuff + jfieldID result; + ENTER_JAVA((Env*) env); + result = (jfieldID)field; // TODO check requirements and other stuff + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL ToReflectedMethod(JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic){ - return (jobject)methodID; // TODO check requirements and other stuff + jobject result; + ENTER_JAVA((Env*) env); + result = (jobject)methodID; // TODO check requirements and other stuff + LEAVE_JAVA((Env*) env); + return result; } jclass JNICALL GetSuperclass(JNIEnv *env, jclass clazz){ - return (jclass)GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz)->{% METHOD java.lang.Class:getSuperclass%}(); + jclass result; + ENTER_JAVA((Env*) env); + result = (jclass)GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz)->{% METHOD java.lang.Class:getSuperclass%}(); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL IsAssignableFrom(JNIEnv *env, jclass clazz1, jclass clazz2){ - return GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz2)->{% METHOD java.lang.Class:isAssignableFrom %}(GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz1)); + jboolean result; + ENTER_JAVA((Env*) env); + result = GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz2)->{% METHOD java.lang.Class:isAssignableFrom %}(GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz1)); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL ToReflectedField(JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic){ - return (jobject)fieldID; // TODO check requirements and other stuff + jobject result; + ENTER_JAVA((Env*) env); + result = (jobject)fieldID; // TODO check requirements and other stuff + LEAVE_JAVA((Env*) env); + return result; +} + +void jtvmThrow(Env* env, JAVA_OBJECT e){ + N::throwJniException(e); +} + +jint JNICALL Throw(JNIEnv *env, jthrowable obj){ + jint result; + ENTER_JAVA((Env*) env); + jtvmThrow((Env*) env, (JAVA_OBJECT) obj); + result = 0; + LEAVE_JAVA((Env*) env); + return result; +} + +jboolean jtvmThrowNew(Env* env, JAVA_OBJECT clazz, const char* message) { + JAVA_OBJECT ctor = jtvmGetInstanceMethod(env, clazz, "", "(Ljava/lang/String;)V"); + if (!ctor) return false; + //Object* string = NULL; + // TODO: Check that clazz != NULL? + //if (message) { + // string = rvmNewStringUTF(env, message, -1); + // if (!string) return FALSE; + //} + jvalue args[1]; + args[0].l = (jobject) N::str(message); + jobject e = (jobject)jtvmNewObjectA(env, clazz, ctor, args); + if (!e) return false; + jtvmThrow(env, (JAVA_OBJECT) e); + return true; +} + +jint JNICALL ThrowNew(JNIEnv *env, jclass clazz, const char *message){ + jint result; + ENTER_JAVA((Env*) env); + result = jtvmThrowNew((Env*) env, (JAVA_OBJECT) clazz, message) ? 0 : -1; + LEAVE_JAVA((Env*) env); + return result; +} + +jthrowable JNICALL ExceptionOccurred(JNIEnv *env){ + jthrowable result; + ENTER_JAVA((Env*) env); + result = (jthrowable)((Env*) env)->throwable; + LEAVE_JAVA((Env*) env); + return result; +} + +void JNICALL ExceptionDescribe(JNIEnv *env){ + ENTER_JAVA((Env*) env); + std::cerr << "Unimplemented ExceptionDescribe"; + abort(); + LEAVE_JAVA((Env*) env); +} + +void JNICALL ExceptionClear(JNIEnv *env){ + ENTER_JAVA((Env*) env); + ((Env*) env)->throwable = NULL; + LEAVE_JAVA((Env*) env); +} + +void JNICALL FatalError(JNIEnv *env, const char *msg){ + ENTER_JAVA((Env*) env); + std::cerr << msg; + abort(); + LEAVE_JAVA((Env*) env); } jint JNICALL PushLocalFrame(JNIEnv* env, jint cap) { - return 0; + jint result; + ENTER_JAVA((Env*) env); + result = 0; + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL PopLocalFrame(JNIEnv* env, jobject res) { - return res; + jobject result; + ENTER_JAVA((Env*) env); + result = res; + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL EnsureLocalCapacity(JNIEnv* env, jint capacity) { - return 0; + jint result; + ENTER_JAVA((Env*) env); + result = 0; + LEAVE_JAVA((Env*) env); + return result; } static JA_L* jvalueToJavaArray(int32_t count, {% CLASS java.lang.reflect.MethodConstructor %}* ctor, const jvalue *args){ @@ -1479,24 +1670,32 @@ static JA_L* va_listToJavaArray(int32_t count, {% CLASS java.lang.reflect.Method return array; } -JAVA_OBJECT jtvmNewObjectA(JAVA_OBJECT clazz, JAVA_OBJECT method, const jvalue *args) { +JAVA_OBJECT jtvmNewObjectA(Env* env, JAVA_OBJECT clazz, JAVA_OBJECT method, const jvalue *args) { {% CLASS java.lang.reflect.Constructor %}* ctor = GET_OBJECT({% CLASS java.lang.reflect.Constructor %}, method); int32_t parameterCount = ctor->{% METHOD java.lang.reflect.Constructor:getParameterCount %}(); return ctor->{% METHOD java.lang.reflect.Constructor:newInstance %}(jvalueToJavaArray(parameterCount, ctor, args)); } -JAVA_OBJECT jtvmNewObjectV(JAVA_OBJECT clazz, JAVA_OBJECT method, va_list args) { +JAVA_OBJECT jtvmNewObjectV(Env* env, JAVA_OBJECT clazz, JAVA_OBJECT method, va_list args) { {% CLASS java.lang.reflect.Constructor %}* ctor = GET_OBJECT({% CLASS java.lang.reflect.Constructor %}, method); int32_t parameterCount = ctor->{% METHOD java.lang.reflect.Constructor:getParameterCount %}(); return ctor->{% METHOD java.lang.reflect.Constructor:newInstance %}(va_listToJavaArray(parameterCount, ctor, args)); } jobject JNICALL NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { - return (jobject) jtvmNewObjectV((JAVA_OBJECT) clazz, (JAVA_OBJECT) methodID, args); + jobject result; + ENTER_JAVA((Env*) env); + result = (jobject) jtvmNewObjectV((Env*) env, (JAVA_OBJECT) clazz, (JAVA_OBJECT) methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { - return (jobject) jtvmNewObjectA((JAVA_OBJECT) clazz, (JAVA_OBJECT) methodID, args); + jobject result; + ENTER_JAVA((Env*) env); + result = (jobject) jtvmNewObjectA((Env*) env, (JAVA_OBJECT) clazz, (JAVA_OBJECT) methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL NewObject(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -1508,23 +1707,26 @@ jobject JNICALL NewObject(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { } jobject JNICALL AllocObject(JNIEnv *env, jclass clazz){ + jobject result; + ENTER_JAVA((Env*) env); int32_t classId = GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz)->{% FIELD java.lang.Class:id %}; - return (jobject)CTOR_TABLE[classId](); + result = (jobject)CTOR_TABLE[classId](); + LEAVE_JAVA((Env*) env); + return result; } -inline static jboolean isMethodStatic(jmethodID method_){ - JAVA_OBJECT method = (JAVA_OBJECT)method_; +inline static jboolean isMethodStatic(JAVA_OBJECT method){ jboolean result = GET_OBJECT({% CLASS java.lang.reflect.MethodConstructor %}, method)->{% METHOD java.lang.reflect.MethodConstructor:isStatic %}(); return result; } -JAVA_OBJECT jtvmGetMethodForClass(JNIEnv *env, JAVA_OBJECT clazz, const char *name, const char *sig){ - if(strcmp(sig, "") == 0){ +JAVA_OBJECT jtvmGetMethodForClass(Env *env, JAVA_OBJECT clazz, const char* name, const char* sig){ + if(strcmp(name, "") == 0){ // for ctors // TODO check correctness etc. JAVA_OBJECT ctor_ = GET_OBJECT({% CLASS java.lang.Class %}, clazz)->{% METHOD java.lang.Class:getDeclaredConstructorBySig %}(N::str(sig)); - auto ctor = GET_OBJECT({% CLASS java.lang.reflect.Method %}, ctor_); + auto ctor = GET_OBJECT({% CLASS java.lang.reflect.Constructor %}, ctor_); if(ctor){ return ctor; @@ -1543,25 +1745,29 @@ JAVA_OBJECT jtvmGetMethodForClass(JNIEnv *env, JAVA_OBJECT clazz, const char *na return nullptr; } -jmethodID jtvmGetInstanceMethod(JNIEnv *env, JAVA_OBJECT clazz_, const char *name, const char *sig){ +JAVA_OBJECT jtvmGetInstanceMethod(Env* env, JAVA_OBJECT clazz_, const char *name, const char *sig){ auto clazz = GET_OBJECT({% CLASS java.lang.Class %}, clazz_); - if(strcmp(sig, "") == 0 || strcmp(sig, "") == 0 ){ + if(strcmp(name, "") == 0 || strcmp(name, "") == 0 ){ // ctors and static initializers are not inherited, so only check this class and not the superclass - jmethodID method = (jmethodID)jtvmGetMethodForClass(env, reinterpret_cast(clazz), name, sig); + JAVA_OBJECT method = jtvmGetMethodForClass(env, reinterpret_cast(clazz), name, sig); if(method && !isMethodStatic(method)) return method; //TODO throw exception? } java_lang_Class* c = NULL; for (c = clazz; c != NULL; c = GET_OBJECT({% CLASS java.lang.Class %}, GET_OBJECT({% CLASS java.lang.Class %}, c)->{% METHOD java.lang.Class:getSuperclass %}())) { - jmethodID method = (jmethodID)jtvmGetMethodForClass(env, clazz, name, sig); + JAVA_OBJECT method = jtvmGetMethodForClass(env, clazz, name, sig); if(method && !isMethodStatic(method)) return method; //TODO throw exception? } return NULL; } jmethodID JNICALL GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig){ + jmethodID result; + ENTER_JAVA((Env*) env); // TODO force init of clazz if not already initialised - return jtvmGetInstanceMethod(env, reinterpret_cast(clazz), name, sig); + result = (jmethodID)jtvmGetInstanceMethod((Env*) env, reinterpret_cast(clazz), name, sig); + LEAVE_JAVA((Env*) env); + return result; } /** @@ -1595,11 +1801,19 @@ jobject jtvmCallObjectInstanceMethodA(JNIEnv* env, jobject obj, jmethodID method } jobject JNICALL CallObjectMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallObjectInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jobject result; + ENTER_JAVA((Env*) env); + result = jtvmCallObjectInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL CallObjectMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallObjectInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jobject result; + ENTER_JAVA((Env*) env); + result = jtvmCallObjectInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL CallObjectMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1623,11 +1837,19 @@ jboolean jtvmCallBooleanInstanceMethodA(JNIEnv* env, jobject obj, jmethodID meth } jboolean JNICALL CallBooleanMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallBooleanInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jboolean result; + ENTER_JAVA((Env*) env); + result = jtvmCallBooleanInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL CallBooleanMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallBooleanInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jboolean result; + ENTER_JAVA((Env*) env); + result = jtvmCallBooleanInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL CallBooleanMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1651,11 +1873,19 @@ jbyte jtvmCallByteInstanceMethodA(JNIEnv* env, jobject obj, jmethodID methodID, } jbyte JNICALL CallByteMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallByteInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jbyte result; + ENTER_JAVA((Env*) env); + result = jtvmCallByteInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jbyte JNICALL CallByteMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallByteInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jbyte result; + ENTER_JAVA((Env*) env); + result = jtvmCallByteInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jbyte JNICALL CallByteMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1679,11 +1909,19 @@ jchar jtvmCallCharInstanceMethodA(JNIEnv* env, jobject obj, jmethodID methodID, } jchar JNICALL CallCharMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallCharInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jchar result; + ENTER_JAVA((Env*) env); + result = jtvmCallCharInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jchar JNICALL CallCharMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallCharInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jchar result; + ENTER_JAVA((Env*) env); + result = jtvmCallCharInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jchar JNICALL CallCharMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1707,11 +1945,19 @@ jshort jtvmCallShortInstanceMethodA(JNIEnv* env, jobject obj, jmethodID methodID } jshort JNICALL CallShortMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallShortInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jshort result; + ENTER_JAVA((Env*) env); + result = jtvmCallShortInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jshort JNICALL CallShortMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallShortInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jshort result; + ENTER_JAVA((Env*) env); + result = jtvmCallShortInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jshort JNICALL CallShortMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1735,11 +1981,19 @@ jint jtvmCallIntInstanceMethodA(JNIEnv* env, jobject obj, jmethodID methodID, co } jint JNICALL CallIntMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallIntInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jint result; + ENTER_JAVA((Env*) env); + result = jtvmCallIntInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL CallIntMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallIntInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jint result; + ENTER_JAVA((Env*) env); + result = jtvmCallIntInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL CallIntMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1763,11 +2017,19 @@ jlong jtvmCallLongInstanceMethodA(JNIEnv* env, jobject obj, jmethodID methodID, } jlong JNICALL CallLongMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallLongInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jlong result; + ENTER_JAVA((Env*) env); + result = jtvmCallLongInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jlong JNICALL CallLongMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallLongInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jlong result; + ENTER_JAVA((Env*) env); + result = jtvmCallLongInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jlong JNICALL CallLongMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1791,11 +2053,19 @@ jfloat jtvmCallFloatInstanceMethodA(JNIEnv* env, jobject obj, jmethodID methodID } jfloat JNICALL CallFloatMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallFloatInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jfloat result; + ENTER_JAVA((Env*) env); + result = jtvmCallFloatInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jfloat JNICALL CallFloatMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallFloatInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jfloat result; + ENTER_JAVA((Env*) env); + result = jtvmCallFloatInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jfloat JNICALL CallFloatMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1819,11 +2089,19 @@ jdouble jtvmCallDoubleInstanceMethodA(JNIEnv* env, jobject obj, jmethodID method } jdouble JNICALL CallDoubleMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { - return jtvmCallDoubleInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + jdouble result; + ENTER_JAVA((Env*) env); + result = jtvmCallDoubleInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jdouble JNICALL CallDoubleMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { - return jtvmCallDoubleInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + jdouble result; + ENTER_JAVA((Env*) env); + result = jtvmCallDoubleInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); + return result; } jdouble JNICALL CallDoubleMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1847,11 +2125,15 @@ void jtvmCallVoidInstanceMethodA(JNIEnv* env, jobject obj, jmethodID methodID, c } void JNICALL CallVoidMethodV(JNIEnv* env, jobject obj, jmethodID methodID, va_list args) { + ENTER_JAVA((Env*) env); jtvmCallVoidInstanceMethodV(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); } void JNICALL CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) { + ENTER_JAVA((Env*) env); jtvmCallVoidInstanceMethodA(env, obj, getRealMethod(env, obj, methodID), args); + LEAVE_JAVA((Env*) env); } void JNICALL CallVoidMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) { @@ -1865,11 +2147,19 @@ void JNICALL CallVoidMethod(JNIEnv* env, jobject obj, jmethodID methodID, ...) // jobject JNICALL CallNonvirtualObjectMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallObjectInstanceMethodV(env, obj, methodID, args); + jobject result; + ENTER_JAVA((Env*) env); + result = jtvmCallObjectInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL CallNonvirtualObjectMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallObjectInstanceMethodA(env, obj, methodID, args); + jobject result; + ENTER_JAVA((Env*) env); + result = jtvmCallObjectInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL CallNonvirtualObjectMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -1881,11 +2171,19 @@ jobject JNICALL CallNonvirtualObjectMethod(JNIEnv* env, jobject obj, jclass claz } jboolean JNICALL CallNonvirtualBooleanMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallBooleanInstanceMethodV(env, obj, methodID, args); + jboolean result; + ENTER_JAVA((Env*) env); + result = jtvmCallBooleanInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL CallNonvirtualBooleanMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallBooleanInstanceMethodA(env, obj, methodID, args); + jboolean result; + ENTER_JAVA((Env*) env); + result = jtvmCallBooleanInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL CallNonvirtualBooleanMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -1897,11 +2195,19 @@ jboolean JNICALL CallNonvirtualBooleanMethod(JNIEnv* env, jobject obj, jclass cl } jbyte JNICALL CallNonvirtualByteMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallByteInstanceMethodV(env, obj, methodID, args); + jbyte result; + ENTER_JAVA((Env*) env); + result = jtvmCallByteInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jbyte JNICALL CallNonvirtualByteMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallByteInstanceMethodA(env, obj, methodID, args); + jbyte result; + ENTER_JAVA((Env*) env); + result = jtvmCallByteInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jbyte JNICALL CallNonvirtualByteMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -1913,11 +2219,19 @@ jbyte JNICALL CallNonvirtualByteMethod(JNIEnv* env, jobject obj, jclass clazz, j } jchar JNICALL CallNonvirtualCharMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallCharInstanceMethodV(env, obj, methodID, args); + jchar result; + ENTER_JAVA((Env*) env); + result = jtvmCallCharInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jchar JNICALL CallNonvirtualCharMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallCharInstanceMethodA(env, obj, methodID, args); + jchar result; + ENTER_JAVA((Env*) env); + result = jtvmCallCharInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jchar JNICALL CallNonvirtualCharMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -1929,11 +2243,19 @@ jchar JNICALL CallNonvirtualCharMethod(JNIEnv* env, jobject obj, jclass clazz, j } jshort JNICALL CallNonvirtualShortMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallShortInstanceMethodV(env, obj, methodID, args); + jshort result; + ENTER_JAVA((Env*) env); + result = jtvmCallShortInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jshort JNICALL CallNonvirtualShortMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallShortInstanceMethodA(env, obj, methodID, args); + jshort result; + ENTER_JAVA((Env*) env); + result = jtvmCallShortInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jshort JNICALL CallNonvirtualShortMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -1945,11 +2267,19 @@ jshort JNICALL CallNonvirtualShortMethod(JNIEnv* env, jobject obj, jclass clazz, } jint JNICALL CallNonvirtualIntMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallIntInstanceMethodV(env, obj, methodID, args); + jint result; + ENTER_JAVA((Env*) env); + result = jtvmCallIntInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL CallNonvirtualIntMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallIntInstanceMethodA(env, obj, methodID, args); + jint result; + ENTER_JAVA((Env*) env); + result = jtvmCallIntInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -1961,11 +2291,19 @@ jint JNICALL CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass clazz, jme } jlong JNICALL CallNonvirtualLongMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallLongInstanceMethodV(env, obj, methodID, args); + jlong result; + ENTER_JAVA((Env*) env); + result = jtvmCallLongInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jlong JNICALL CallNonvirtualLongMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallLongInstanceMethodA(env, obj, methodID, args); + jlong result; + ENTER_JAVA((Env*) env); + result = jtvmCallLongInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jlong JNICALL CallNonvirtualLongMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -1977,11 +2315,19 @@ jlong JNICALL CallNonvirtualLongMethod(JNIEnv* env, jobject obj, jclass clazz, j } jfloat JNICALL CallNonvirtualFloatMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallFloatInstanceMethodV(env, obj, methodID, args); + jfloat result; + ENTER_JAVA((Env*) env); + result = jtvmCallFloatInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jfloat JNICALL CallNonvirtualFloatMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallFloatInstanceMethodA(env, obj, methodID, args); + jfloat result; + ENTER_JAVA((Env*) env); + result = jtvmCallFloatInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jfloat JNICALL CallNonvirtualFloatMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -1993,11 +2339,19 @@ jfloat JNICALL CallNonvirtualFloatMethod(JNIEnv* env, jobject obj, jclass clazz, } jdouble JNICALL CallNonvirtualDoubleMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { - return jtvmCallDoubleInstanceMethodV(env, obj, methodID, args); + jdouble result; + ENTER_JAVA((Env*) env); + result = jtvmCallDoubleInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jdouble JNICALL CallNonvirtualDoubleMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { - return jtvmCallDoubleInstanceMethodA(env, obj, methodID, args); + jdouble result; + ENTER_JAVA((Env*) env); + result = jtvmCallDoubleInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); + return result; } jdouble JNICALL CallNonvirtualDoubleMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -2009,11 +2363,15 @@ jdouble JNICALL CallNonvirtualDoubleMethod(JNIEnv* env, jobject obj, jclass claz } void JNICALL CallNonvirtualVoidMethodV(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, va_list args) { + ENTER_JAVA((Env*) env); jtvmCallVoidInstanceMethodV(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); } void JNICALL CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { + ENTER_JAVA((Env*) env); jtvmCallVoidInstanceMethodA(env, obj, methodID, args); + LEAVE_JAVA((Env*) env); } void JNICALL CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID, ...) { @@ -2041,111 +2399,181 @@ jfieldID jtvmGetFieldID({% CLASS java.lang.Class %}* clazz, JAVA_OBJECT name, JA } jfieldID JNICALL GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig){ - return jtvmGetFieldID(GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz), N::str(name), N::str(sig)); + jfieldID result; + ENTER_JAVA((Env*) env); + result = jtvmGetFieldID(GET_OBJECT({% CLASS java.lang.Class %}, (JAVA_OBJECT)clazz), N::str(name), N::str(sig)); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jobject result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return (jobject) field->{% METHOD java.lang.reflect.Field:get %}(reinterpret_cast(obj)); + result = (jobject) field->{% METHOD java.lang.reflect.Field:get %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL GetBooleanField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jboolean result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getBoolean %}(reinterpret_cast(obj)); + result = field->{% METHOD java.lang.reflect.Field:getBoolean %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } jbyte JNICALL GetByteField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jbyte result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getByte %}(reinterpret_cast(obj)); + result = field->{% METHOD java.lang.reflect.Field:getByte %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } jchar JNICALL GetCharField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jchar result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getChar %}(reinterpret_cast(obj)); + result = field->{% METHOD java.lang.reflect.Field:getChar %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } jshort JNICALL GetShortField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jshort result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getShort %}(reinterpret_cast(obj)); + result = field->{% METHOD java.lang.reflect.Field:getShort %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL GetIntField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jint result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getInt %}(reinterpret_cast(obj)); + result = field->{% METHOD java.lang.reflect.Field:getInt %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } jlong JNICALL GetLongField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jlong result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getLong %}(reinterpret_cast(obj)); + result = field->{% METHOD java.lang.reflect.Field:getLong %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } jfloat JNICALL GetFloatField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jfloat result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getFloat %}(reinterpret_cast(obj)); + result = field->{% METHOD java.lang.reflect.Field:getFloat %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } jdouble JNICALL GetDoubleField(JNIEnv *env, jobject obj, jfieldID fieldID){ + jdouble result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getDouble %}(reinterpret_cast(obj)); + result = field->{% METHOD java.lang.reflect.Field:getDouble %}(reinterpret_cast(obj)); + LEAVE_JAVA((Env*) env); + return result; } void JNICALL SetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID, jobject value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:set %}(reinterpret_cast(obj), reinterpret_cast(value)); + LEAVE_JAVA((Env*) env); } void JNICALL SetBooleanField(JNIEnv *env, jobject obj, jfieldID fieldID, jboolean value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setBoolean %}(reinterpret_cast(obj), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetByteField(JNIEnv *env, jobject obj, jfieldID fieldID, jbyte value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setByte %}(reinterpret_cast(obj), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetCharField(JNIEnv *env, jobject obj, jfieldID fieldID, jchar value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setChar %}(reinterpret_cast(obj), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetShortField(JNIEnv *env, jobject obj, jfieldID fieldID, jshort value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setShort %}(reinterpret_cast(obj), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetIntField(JNIEnv *env, jobject obj, jfieldID fieldID, jint value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setInt %}(reinterpret_cast(obj), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetLongField(JNIEnv *env, jobject obj, jfieldID fieldID, jlong value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setLong %}(reinterpret_cast(obj), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetFloatField(JNIEnv *env, jobject obj, jfieldID fieldID, jfloat value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setFloat %}(reinterpret_cast(obj), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetDoubleField(JNIEnv *env, jobject obj, jfieldID fieldID, jdouble value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setDouble %}(reinterpret_cast(obj), value); + LEAVE_JAVA((Env*) env); } jobject JNICALL CallStaticObjectMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jobject result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return (jobject)method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args)); + result = (jobject)method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args)); + LEAVE_JAVA((Env*) env); + return result; } jmethodID JNICALL GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig){ - return (jmethodID)jtvmGetMethodForClass(env, (JAVA_OBJECT)clazz, name, sig); // TODO verify jni specs + jmethodID result; + ENTER_JAVA((Env*) env); + result = (jmethodID)jtvmGetMethodForClass((Env*) env, (JAVA_OBJECT) clazz, name, sig); // TODO verify jni specs + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL CallStaticObjectMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + jobject result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return (jobject)method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args)); + result = (jobject)method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args)); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL CallStaticObjectMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2157,15 +2585,23 @@ jobject JNICALL CallStaticObjectMethod(JNIEnv* env, jclass clazz, jmethodID meth } jboolean JNICALL CallStaticBooleanMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jboolean result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxBool(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + result = N::unboxBool(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL CallStaticBooleanMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + jboolean result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxBool(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + result = N::unboxBool(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL CallStaticBooleanMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2177,15 +2613,23 @@ jboolean JNICALL CallStaticBooleanMethod(JNIEnv* env, jclass clazz, jmethodID me } jbyte JNICALL CallStaticByteMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jbyte result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxByte(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + result = N::unboxByte(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jbyte JNICALL CallStaticByteMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + jbyte result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxByte(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + result = N::unboxByte(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jbyte JNICALL CallStaticByteMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2197,15 +2641,23 @@ jbyte JNICALL CallStaticByteMethod(JNIEnv* env, jclass clazz, jmethodID methodID } jchar JNICALL CallStaticCharMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jchar result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxChar(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + result = N::unboxChar(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jchar JNICALL CallStaticCharMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + jchar result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxChar(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + result = N::unboxChar(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jchar JNICALL CallStaticCharMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2217,15 +2669,23 @@ jchar JNICALL CallStaticCharMethod(JNIEnv* env, jclass clazz, jmethodID methodID } jshort JNICALL CallStaticShortMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jshort result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxShort(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + result = N::unboxShort(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jshort JNICALL CallStaticShortMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + jshort result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxShort(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + result = N::unboxShort(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jshort JNICALL CallStaticShortMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2237,15 +2697,23 @@ jshort JNICALL CallStaticShortMethod(JNIEnv* env, jclass clazz, jmethodID method } jint JNICALL CallStaticIntMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jint result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxInt(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + result = N::unboxInt(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL CallStaticIntMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + jint result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxInt(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + result = N::unboxInt(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL CallStaticIntMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2257,15 +2725,23 @@ jint JNICALL CallStaticIntMethod(JNIEnv* env, jclass clazz, jmethodID methodID, } jlong JNICALL CallStaticLongMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jlong result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxLong(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + result = N::unboxLong(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jlong JNICALL CallStaticLongMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + jlong result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxLong(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + result = N::unboxLong(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jlong JNICALL CallStaticLongMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2277,15 +2753,23 @@ jlong JNICALL CallStaticLongMethod(JNIEnv* env, jclass clazz, jmethodID methodID } jfloat JNICALL CallStaticFloatMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jfloat result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxFloat(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + result = N::unboxFloat(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jfloat JNICALL CallStaticFloatMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + jfloat result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxFloat(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + result = N::unboxFloat(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jfloat JNICALL CallStaticFloatMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2297,15 +2781,23 @@ jfloat JNICALL CallStaticFloatMethod(JNIEnv* env, jclass clazz, jmethodID method } jdouble JNICALL CallStaticDoubleMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + jdouble result; + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxDouble(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + result = N::unboxDouble(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jdouble JNICALL CallStaticDoubleMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { - {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); - int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); - return N::unboxDouble(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + jdouble result; + ENTER_JAVA((Env*) env); + {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); + int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); + result = N::unboxDouble(method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args))); + LEAVE_JAVA((Env*) env); + return result; } jdouble JNICALL CallStaticDoubleMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2317,15 +2809,19 @@ jdouble JNICALL CallStaticDoubleMethod(JNIEnv* env, jclass clazz, jmethodID meth } void JNICALL CallStaticVoidMethodV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) { + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, va_listToJavaArray(parameterCount, method, args)); + LEAVE_JAVA((Env*) env); } void JNICALL CallStaticVoidMethodA(JNIEnv* env, jclass clazz, jmethodID methodID, const jvalue* args) { + ENTER_JAVA((Env*) env); {% CLASS java.lang.reflect.Method %}* method = GET_OBJECT({% CLASS java.lang.reflect.Method %}, (JAVA_OBJECT)methodID); int32_t parameterCount = method->{% METHOD java.lang.reflect.MethodConstructor:getParameterCount %}(); method->{% METHOD java.lang.reflect.Method:invoke %}(NULL, jvalueToJavaArray(parameterCount, method, args)); + LEAVE_JAVA((Env*) env); } void JNICALL CallStaticVoidMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) { @@ -2352,100 +2848,158 @@ jfieldID jtvmGetStaticFieldID(JNIEnv *env, JAVA_OBJECT clazz, const char *name, } jfieldID JNICALL GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig){ + jfieldID result; + ENTER_JAVA((Env*) env); // TODO force init of clazz if not already initialised - return jtvmGetStaticFieldID(env, reinterpret_cast(clazz), name, sig); + result = jtvmGetStaticFieldID(env, reinterpret_cast(clazz), name, sig); + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL GetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jobject result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return (jobject) field->{% METHOD java.lang.reflect.Field:get %}(reinterpret_cast(clazz)); + result = (jobject) field->{% METHOD java.lang.reflect.Field:get %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } jboolean JNICALL GetStaticBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jboolean result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getBoolean %}(reinterpret_cast(clazz)); + result = field->{% METHOD java.lang.reflect.Field:getBoolean %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } jbyte JNICALL GetStaticByteField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jbyte result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getByte %}(reinterpret_cast(clazz)); + result = field->{% METHOD java.lang.reflect.Field:getByte %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } jchar JNICALL GetStaticCharField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jchar result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getChar %}(reinterpret_cast(clazz)); + result = field->{% METHOD java.lang.reflect.Field:getChar %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } jshort JNICALL GetStaticShortField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jshort result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getShort %}(reinterpret_cast(clazz)); + result = field->{% METHOD java.lang.reflect.Field:getShort %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } jint JNICALL GetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jint result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getInt %}(reinterpret_cast(clazz)); + result = field->{% METHOD java.lang.reflect.Field:getInt %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } jlong JNICALL GetStaticLongField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jlong result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getLong %}(reinterpret_cast(clazz)); + result = field->{% METHOD java.lang.reflect.Field:getLong %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } jfloat JNICALL GetStaticFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jfloat result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getFloat %}(reinterpret_cast(clazz)); + result = field->{% METHOD java.lang.reflect.Field:getFloat %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } jdouble JNICALL GetStaticDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID){ + jdouble result; + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (JAVA_OBJECT)fieldID); - return field->{% METHOD java.lang.reflect.Field:getDouble %}(reinterpret_cast(clazz)); + result = field->{% METHOD java.lang.reflect.Field:getDouble %}(reinterpret_cast(clazz)); + LEAVE_JAVA((Env*) env); + return result; } void JNICALL SetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:set %}(reinterpret_cast(clazz), reinterpret_cast(value)); + LEAVE_JAVA((Env*) env); } void JNICALL SetStaticBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setBoolean %}(reinterpret_cast(clazz), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetStaticByteField(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setByte %}(reinterpret_cast(clazz), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetStaticCharField(JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setChar %}(reinterpret_cast(clazz), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetStaticShortField(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setShort %}(reinterpret_cast(clazz), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID, jint value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setInt %}(reinterpret_cast(clazz), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetStaticLongField(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setLong %}(reinterpret_cast(clazz), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetStaticFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setFloat %}(reinterpret_cast(clazz), value); + LEAVE_JAVA((Env*) env); } void JNICALL SetStaticDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value){ + ENTER_JAVA((Env*) env); auto field = GET_OBJECT({% CLASS java.lang.reflect.Field %}, (reinterpret_cast(fieldID))); field->{% METHOD java.lang.reflect.Field:setDouble %}(reinterpret_cast(clazz), value); + LEAVE_JAVA((Env*) env); } @@ -2459,7 +3013,11 @@ JAVA_OBJECT jtvmNewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity){ } jobject JNICALL NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity){ - return (jobject) jtvmNewDirectByteBuffer(env, address, capacity); + jobject result; + ENTER_JAVA((Env*) env); + result = (jobject) jtvmNewDirectByteBuffer(env, address, capacity); + LEAVE_JAVA((Env*) env); + return result; } void* jtvmGetDirectBufferAddress(JNIEnv* env, JAVA_OBJECT buf){ @@ -2469,7 +3027,11 @@ void* jtvmGetDirectBufferAddress(JNIEnv* env, JAVA_OBJECT buf){ } void* JNICALL GetDirectBufferAddress(JNIEnv* env, jobject buf){ - return jtvmGetDirectBufferAddress(env, (JAVA_OBJECT) buf); + void* result; + ENTER_JAVA((Env*) env); + result = jtvmGetDirectBufferAddress(env, (JAVA_OBJECT) buf); + LEAVE_JAVA((Env*) env); + return result; } jlong jtvmGetDirectBufferCapacity(JNIEnv* env, JAVA_OBJECT buf){ @@ -2478,7 +3040,11 @@ jlong jtvmGetDirectBufferCapacity(JNIEnv* env, JAVA_OBJECT buf){ } jlong JNICALL GetDirectBufferCapacity(JNIEnv* env, jobject buf){ - return jtvmGetDirectBufferCapacity(env, (JAVA_OBJECT) buf); + jlong result; + ENTER_JAVA((Env*) env); + result = jtvmGetDirectBufferCapacity(env, (JAVA_OBJECT) buf); + LEAVE_JAVA((Env*) env); + return result; } jsize jtvmGetArrayLength(JNIEnv* env, jarray array){ @@ -2486,25 +3052,39 @@ jsize jtvmGetArrayLength(JNIEnv* env, jarray array){ } jsize JNICALL GetArrayLength(JNIEnv* env, jarray array){ - return jtvmGetArrayLength(env, array); + jsize result; + ENTER_JAVA((Env*) env); + result = jtvmGetArrayLength(env, array); + LEAVE_JAVA((Env*) env); + return result; } jobjectArray JNICALL NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement){ + jobjectArray result; + ENTER_JAVA((Env*) env); JA_L* array = new JA_L(length, L"[Ljava/lang/Object"); // TODO fix me! for(int32_t i = 0; i < length; i++){ array->fastSet(i, (JAVA_OBJECT)initialElement); } - return (jobjectArray)array; + result = (jobjectArray)array; + LEAVE_JAVA((Env*) env); + return result; } jobject JNICALL GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index){ + jobject result; + ENTER_JAVA((Env*) env); JA_L* arr = (JA_L*)array; - return (jobject)arr->fastGet(index); // TODO indexoutofbounds check + result = (jobject)arr->fastGet(index); // TODO indexoutofbounds check + LEAVE_JAVA((Env*) env); + return result; } void JNICALL SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value){ + ENTER_JAVA((Env*) env); JA_L* arr = (JA_L*)array; arr->fastSet(index, (JAVA_OBJECT)value); // TODO indexoutofbounds check + LEAVE_JAVA((Env*) env); } void* jtvmGetUniversalArrayElements(JNIEnv *env, JA_0* array, jboolean *isCopy){ @@ -2513,35 +3093,67 @@ void* jtvmGetUniversalArrayElements(JNIEnv *env, JA_0* array, jboolean *isCopy){ } jboolean* JNICALL GetBooleanArrayElements(JNIEnv *env, jbooleanArray array, jboolean *isCopy){ - return (jboolean*) jtvmGetUniversalArrayElements(env, (JA_Z*) array, isCopy); + jboolean* result; + ENTER_JAVA((Env*) env); + result = (jboolean*) jtvmGetUniversalArrayElements(env, (JA_Z*) array, isCopy); + LEAVE_JAVA((Env*) env); + return result; } jbyte* JNICALL GetByteArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy){ - return (jbyte*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + jbyte* result; + ENTER_JAVA((Env*) env); + result = (jbyte*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + LEAVE_JAVA((Env*) env); + return result; } jchar* JNICALL GetCharArrayElements(JNIEnv *env, jcharArray array, jboolean *isCopy){ - return (jchar*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + jchar* result; + ENTER_JAVA((Env*) env); + result = (jchar*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + LEAVE_JAVA((Env*) env); + return result; } jshort* JNICALL GetShortArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy){ - return (jshort*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + jshort* result; + ENTER_JAVA((Env*) env); + result = (jshort*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + LEAVE_JAVA((Env*) env); + return result; } jint* JNICALL GetIntArrayElements(JNIEnv *env, jintArray array, jboolean *isCopy){ - return (jint*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + jint* result; + ENTER_JAVA((Env*) env); + result = (jint*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + LEAVE_JAVA((Env*) env); + return result; } jlong* JNICALL GetLongArrayElements(JNIEnv *env, jlongArray array, jboolean *isCopy){ - return (jlong*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + jlong* result; + ENTER_JAVA((Env*) env); + result = (jlong*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + LEAVE_JAVA((Env*) env); + return result; } jfloat* JNICALL GetFloatArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy){ - return (jfloat*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + jfloat* result; + ENTER_JAVA((Env*) env); + result = (jfloat*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + LEAVE_JAVA((Env*) env); + return result; } jdouble* JNICALL GetDoubleArrayElements(JNIEnv *env, jdoubleArray array, jboolean *isCopy){ - return (jdouble*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + double* result; + ENTER_JAVA((Env*) env); + result = (jdouble*) jtvmGetUniversalArrayElements(env, (JA_0*) array, isCopy); + LEAVE_JAVA((Env*) env); + return result; } @@ -2588,83 +3200,115 @@ static jboolean checkBounds(JNIEnv* env, JA_0* array, jint start, jint len){ } void JNICALL GetBooleanArrayRegion(JNIEnv *env, jbooleanArray array, jsize start, jsize len, jboolean *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(buf, ((jboolean* )((JA_Z*) array)->_data) + start, sizeof(jboolean) * len); + LEAVE_JAVA((Env*) env); } void JNICALL GetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(buf, ((jbyte* )((JA_B*) array)->_data) + start, sizeof(jbyte) * len); + LEAVE_JAVA((Env*) env); } void JNICALL GetCharArrayRegion(JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(buf, ((jchar* )((JA_C*) array)->_data) + start, sizeof(jchar) * len); + LEAVE_JAVA((Env*) env); } void JNICALL GetShortArrayRegion(JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(buf, ((jshort* )((JA_S*) array)->_data) + start, sizeof(jshort) * len); + LEAVE_JAVA((Env*) env); } void JNICALL GetIntArrayRegion(JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(buf, ((jint* )((JA_I*) array)->_data) + start, sizeof(jint) * len); + LEAVE_JAVA((Env*) env); } void JNICALL GetLongArrayRegion(JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(buf, ((jlong* )((JA_J*) array)->_data) + start, sizeof(jlong) * len); + LEAVE_JAVA((Env*) env); } void JNICALL GetFloatArrayRegion(JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(buf, ((jfloat* )((JA_F*) array)->_data) + start, sizeof(jfloat) * len); + LEAVE_JAVA((Env*) env); } void JNICALL GetDoubleArrayRegion(JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(buf, ((jdouble* )((JA_D*) array)->_data) + start, sizeof(jdouble) * len); + LEAVE_JAVA((Env*) env); } void JNICALL SetBooleanArrayRegion(JNIEnv *env, jbooleanArray array, jsize start, jsize len, const jboolean *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(((jboolean* )((JA_Z*) array)->_data) + start, buf, sizeof(jboolean) * len); + LEAVE_JAVA((Env*) env); } void JNICALL SetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(((jbyte* )((JA_B*) array)->_data) + start, buf, sizeof(jbyte) * len); + LEAVE_JAVA((Env*) env); } void JNICALL SetCharArrayRegion(JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(((jchar* )((JA_C*) array)->_data) + start, buf, sizeof(jchar) * len); + LEAVE_JAVA((Env*) env); } void JNICALL SetShortArrayRegion(JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(((jshort* )((JA_S*) array)->_data) + start, buf, sizeof(jshort) * len); + LEAVE_JAVA((Env*) env); } void JNICALL SetIntArrayRegion(JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(((jint* )((JA_I*) array)->_data) + start, buf, sizeof(jint) * len); + LEAVE_JAVA((Env*) env); } void JNICALL SetLongArrayRegion(JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(((jlong* )((JA_J*) array)->_data) + start, buf, sizeof(jlong) * len); + LEAVE_JAVA((Env*) env); } void JNICALL SetFloatArrayRegion(JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(((jfloat* )((JA_F*) array)->_data) + start, buf, sizeof(jfloat) * len); + LEAVE_JAVA((Env*) env); } void JNICALL SetDoubleArrayRegion(JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf){ + ENTER_JAVA((Env*) env); if(!checkBounds(env, (JA_0*) array, start, len)) return; memcpy(((jdouble* )((JA_D*) array)->_data) + start, buf, sizeof(jdouble) * len); + LEAVE_JAVA((Env*) env); } @@ -2683,7 +3327,11 @@ JA_Z* jtvmNewBooleanArray(JNIEnv* env, jsize length){ } jbooleanArray JNICALL NewBooleanArray(JNIEnv* env, jsize length){ - return (jbooleanArray) jtvmNewBooleanArray(env, length); + jbooleanArray result; + ENTER_JAVA((Env*) env); + result = (jbooleanArray) jtvmNewBooleanArray(env, length); + LEAVE_JAVA((Env*) env); + return result; } @@ -2693,7 +3341,11 @@ JA_B* jtvmNewByteArray(JNIEnv* env, jsize length){ } jbyteArray JNICALL NewByteArray(JNIEnv* env, jsize length){ - return (jbyteArray) jtvmNewByteArray(env, length); + jbyteArray result; + ENTER_JAVA((Env*) env); + result = (jbyteArray) jtvmNewByteArray(env, length); + LEAVE_JAVA((Env*) env); + return result; } @@ -2703,7 +3355,11 @@ JA_C* jtvmNewCharArray(JNIEnv* env, jsize length){ } jcharArray JNICALL NewCharArray(JNIEnv* env, jsize length){ - return (jcharArray) jtvmNewCharArray(env, length); + jcharArray result; + ENTER_JAVA((Env*) env); + result = (jcharArray) jtvmNewCharArray(env, length); + LEAVE_JAVA((Env*) env); + return result; } @@ -2713,7 +3369,11 @@ JA_S* jtvmNewShortArray(JNIEnv* env, jsize length){ } jshortArray JNICALL NewShortArray(JNIEnv* env, jsize length){ - return (jshortArray) jtvmNewShortArray(env, length); + jshortArray result; + ENTER_JAVA((Env*) env); + result = (jshortArray) jtvmNewShortArray(env, length); + LEAVE_JAVA((Env*) env); + return result; } JA_I* jtvmNewIntArray(JNIEnv* env, jsize length){ @@ -2722,7 +3382,11 @@ JA_I* jtvmNewIntArray(JNIEnv* env, jsize length){ } jintArray JNICALL NewIntArray(JNIEnv* env, jsize length){ - return (jintArray) jtvmNewIntArray(env, length); + jintArray result; + ENTER_JAVA((Env*) env); + result = (jintArray) jtvmNewIntArray(env, length); + LEAVE_JAVA((Env*) env); + return result; } JA_J* jtvmNewLongArray(JNIEnv* env, jsize length){ @@ -2731,7 +3395,11 @@ JA_J* jtvmNewLongArray(JNIEnv* env, jsize length){ } jlongArray JNICALL NewLongArray(JNIEnv* env, jsize length){ - return (jlongArray) jtvmNewLongArray(env, length); + jlongArray result; + ENTER_JAVA((Env*) env); + result = (jlongArray) jtvmNewLongArray(env, length); + LEAVE_JAVA((Env*) env); + return result; } JA_F* jtvmNewFloatArray(JNIEnv* env, jsize length){ @@ -2740,7 +3408,11 @@ JA_F* jtvmNewFloatArray(JNIEnv* env, jsize length){ } jfloatArray JNICALL NewFloatArray(JNIEnv* env, jsize length){ - return (jfloatArray) jtvmNewFloatArray(env, length); + jfloatArray result; + ENTER_JAVA((Env*) env); + result = (jfloatArray) jtvmNewFloatArray(env, length); + LEAVE_JAVA((Env*) env); + return result; } JA_D* jtvmNewDoubleArray(JNIEnv* env, jsize length){ @@ -2749,11 +3421,15 @@ JA_D* jtvmNewDoubleArray(JNIEnv* env, jsize length){ } jdoubleArray JNICALL NewDoubleArray(JNIEnv* env, jsize length){ - return (jdoubleArray) jtvmNewDoubleArray(env, length); + jdoubleArray result; + ENTER_JAVA((Env*) env); + result = (jdoubleArray) jtvmNewDoubleArray(env, length); + LEAVE_JAVA((Env*) env); + return result; } -JNIEnv* N::getJniEnv(){ - return &N::env.jni; +Env* N::getThreadEnv(){ + return &env; } jint JNICALL GetVersion(JNIEnv* env){ @@ -2761,18 +3437,27 @@ jint JNICALL GetVersion(JNIEnv* env){ } jclass JNICALL DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize bufLen){ + ENTER_JAVA((Env*) env); throw "Unsupported Operation"; // TODO add proper exception + LEAVE_JAVA((Env*) env); return NULL; } -JAVA_OBJECT jtvmFindClass(JNIEnv* env, const char *name){ - return N::resolveClass(N::istr2(N::str(name))); +JAVA_OBJECT jtvmFindClass(Env* env, const char *name){ + JAVA_OBJECT string = N::str(name); + string = GET_OBJECT({% CLASS java.lang.String %}, string)->{% METHOD java.lang.String:replace:(CC)Ljava/lang/String; %}('/', '.'); + JAVA_OBJECT clazz = N::resolveClass(string); + return clazz; // FIXME horribly inefficient // FIXME semantics are probably wrong } jclass JNICALL FindClass(JNIEnv* env, const char *name){ - return (jclass) jtvmFindClass(env, name); + jclass result; + ENTER_JAVA((Env*) env); + result = (jclass) jtvmFindClass((Env*) env, name); + LEAVE_JAVA((Env*) env); + return result; } JAVA_OBJECT jtvmGetObjectClass(JNIEnv *env, JAVA_OBJECT obj){ @@ -2780,7 +3465,11 @@ JAVA_OBJECT jtvmGetObjectClass(JNIEnv *env, JAVA_OBJECT obj){ } jclass JNICALL GetObjectClass(JNIEnv *env, jobject obj){ - return (jclass) jtvmGetObjectClass(env, (JAVA_OBJECT) obj); + jclass result; + ENTER_JAVA((Env*) env); + result = (jclass) jtvmGetObjectClass(env, (JAVA_OBJECT) obj); + LEAVE_JAVA((Env*) env); + return result; } bool jtvmIsInstanceOf(JNIEnv *env, JAVA_OBJECT obj, JAVA_OBJECT clazz){ @@ -2788,7 +3477,11 @@ bool jtvmIsInstanceOf(JNIEnv *env, JAVA_OBJECT obj, JAVA_OBJECT clazz){ } jboolean JNICALL IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz){ - return jtvmIsInstanceOf(env, (JAVA_OBJECT) obj, (JAVA_OBJECT) clazz); + jboolean result; + ENTER_JAVA((Env*) env); + result = jtvmIsInstanceOf(env, (JAVA_OBJECT) obj, (JAVA_OBJECT) clazz); + LEAVE_JAVA((Env*) env); + return result; } const struct JNINativeInterface_ jni = { @@ -2811,12 +3504,12 @@ const struct JNINativeInterface_ jni = { &ToReflectedField, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, + &Throw, + &ThrowNew, + &ExceptionOccurred, + &ExceptionDescribe, + &ExceptionClear, + &FatalError, &PushLocalFrame, &PopLocalFrame, @@ -3072,7 +3765,11 @@ const struct JNINativeInterface_ jni = { -Env N::env; +//Env N::env; + +static void initMainThread(){ + {% SMETHOD java.lang.Thread:initMainThread %}(); +} void N::startup() { /* @@ -3096,13 +3793,14 @@ void N::startup() { //std::setlocale(LC_COLLATE, "en_US.UTF-8"); //std::setlocale(LC_CTYPE, "en_US.UTF-8"); - N::env.jni.functions = &jni; + env.functions = &jni; setvbuf(stdout, nullptr, _IONBF, 0); setvbuf(stderr, nullptr, _IONBF, 0); std::signal(SIGSEGV, SIGSEGV_handler); std::signal(SIGFPE, SIGFPE_handler); N::initStringPool(); + //{% SMETHOD java.lang.Thread:initMainThread %}(); }; // Type Table Footer diff --git a/jtransc-rt/resources/d/Base.d b/jtransc-rt/resources/d/Base.d index 7e41ea30..d2c22590 100644 --- a/jtransc-rt/resources/d/Base.d +++ b/jtransc-rt/resources/d/Base.d @@ -446,4 +446,8 @@ TOut checkCast(TOut, TIn)(TIn i) { return o; } +private static void initMainThread(){ + {% SMETHOD java.lang.Thread:initMainThread %}(); +} + /* ## BODY ## */ diff --git a/jtransc-rt/src/java/lang/ClassLoader.java b/jtransc-rt/src/java/lang/ClassLoader.java index c227e38e..d2c24f19 100644 --- a/jtransc-rt/src/java/lang/ClassLoader.java +++ b/jtransc-rt/src/java/lang/ClassLoader.java @@ -176,6 +176,7 @@ public InputStream getResourceAsStream(String name) { void loadLibrary(Class fromClass, String name, boolean isAbsolute) { if (JTranscSystem.isCpp()) { + if (!isAbsolute) throw new UnsupportedOperationException("Only absolute paths are currently supported!"); NativeLib nativeLib = new NativeLib(loadLibrarayCpp(name), name); if (!nativeLibs.contains(nativeLib)) { nativeLibs.add(nativeLib); diff --git a/jtransc-rt/src/java/lang/Runtime.java b/jtransc-rt/src/java/lang/Runtime.java index bf8f8ab3..961d8576 100644 --- a/jtransc-rt/src/java/lang/Runtime.java +++ b/jtransc-rt/src/java/lang/Runtime.java @@ -166,19 +166,19 @@ public void traceMethodCalls(boolean on) { } public void load(String filename) { - + load0(/*Reflection.getCallerClass()*/null, filename); } synchronized void load0(Class fromClass, String filename) { - + ClassLoader.getSystemClassLoader().loadLibrary(fromClass, filename, true); } public void loadLibrary(String libname) { - + loadLibrary0(/*Reflection.getCallerClass()*/null, libname); } synchronized void loadLibrary0(Class fromClass, String libname) { - + ClassLoader.getSystemClassLoader().loadLibrary(fromClass, libname, false); } @Deprecated diff --git a/jtransc-rt/src/java/lang/Thread.java b/jtransc-rt/src/java/lang/Thread.java index 17329e84..0d492a51 100644 --- a/jtransc-rt/src/java/lang/Thread.java +++ b/jtransc-rt/src/java/lang/Thread.java @@ -23,9 +23,7 @@ import com.jtransc.annotation.haxe.HaxeAddMembers; import com.jtransc.annotation.haxe.HaxeMethodBody; -import java.util.Collection; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.Map; @JTranscAddMembers(target = "d", value = "static {% CLASS java.lang.Thread %} _dCurrentThread; Thread thread;") @@ -34,34 +32,37 @@ @JTranscAddIncludes(target = "cpp", value = {"thread", "map"}) @JTranscAddMembers(target = "cpp", cond = "USE_BOOST", value = "boost::thread t_;") @JTranscAddMembers(target = "cpp", cond = "!USE_BOOST", value = "std::thread t_;") -@JTranscAddMembers(target = "cpp", cond = "USE_BOOST", value = "static std::map ###_cpp_threads;") -@JTranscAddMembers(target = "cpp", cond = "!USE_BOOST", value = "static std::map ###_cpp_threads;") +//ThreadState is defined in Base.cpp, because it is used by other code before. +@JTranscAddMembers(target = "cpp", value = "ThreadState state = ThreadState::thread_in_java;") +//@JTranscAddMembers(target = "cpp", cond = "USE_BOOST", value = "static std::map ###_cpp_threads;") +//@JTranscAddMembers(target = "cpp", cond = "!USE_BOOST", value = "static std::map ###_cpp_threads;") @HaxeAddMembers({ "private static var threadsMap = new haxe.ds.ObjectMap();", - "#if cpp var _cpp_thread: cpp.vm.Thread; #end", + "#if cpp var _cpp_thread: cpp.vm.Thread; static var currentThread : cpp.vm.Tls<{% CLASS java.lang.Thread %}> = new Tls(); #end", }) public class Thread implements Runnable { + public final static int MIN_PRIORITY = 1; public final static int NORM_PRIORITY = 5; public final static int MAX_PRIORITY = 10; public static Thread currentThread() { - lazyPrepareThread(); + //lazyPrepareThread(); Thread out = _getCurrentThreadOrNull(); - return (out != null) ? out : _mainThread; + //return (out != null) ? out : _mainThread; + return out; } @JTranscMethodBody(target = "d", value = { - "if (_dCurrentThread is null) {", - " _dCurrentThread = new {% CLASS java.lang.Thread %}();", - "}", "return _dCurrentThread;", }) - @JTranscMethodBody(target = "cpp", cond = "USE_BOOST", value = "return _cpp_threads[boost::this_thread::get_id()];") - @JTranscMethodBody(target = "cpp", value = "return _cpp_threads[std::this_thread::get_id()];") - @HaxeMethodBody(target = "cpp", value = "return threadsMap.get(cpp.vm.Thread.current().handle);") + //@JTranscMethodBody(target = "cpp", cond = "USE_BOOST", value = "return _cpp_threads[boost::this_thread::get_id()];") + //@JTranscMethodBody(target = "cpp", value = "return _cpp_threads[std::this_thread::get_id()];") + @JTranscMethodBody(target = "cpp", value = "return N::getThreadEnv()->currentThread;") + //@HaxeMethodBody(target = "cpp", value = "return threadsMap.get(cpp.vm.Thread.current().handle);") + @HaxeMethodBody(target = "cpp", value = "return currentThread.get_value();") private static Thread _getCurrentThreadOrNull() { - for (Thread t : getThreadsCopy()) return t; // Just valid for programs with just once thread + //for (Thread t : getThreadsCopy()) return t; // Just valid for programs with just once thread return null; } @@ -100,16 +101,45 @@ public Thread() { this(null, null, null, 1024); } - static private LinkedHashMap _threadsById; + //static private LinkedHashMap _threadsById; private ThreadGroup group; public String name; private long stackSize; private Runnable target; private int priority = MIN_PRIORITY; - private int id; - static private int lastId = 0; + + private long threadID; + + // Used for generating the unique thread ids for getId() + private static long nextThreadID; + + private static synchronized long nextThreadID() { + return ++nextThreadID; + } + + + // Used for naming anonymous threads + private static long threadNameNumber; + + private static synchronized long nextThreadNumber() { + return threadNameNumber++; + } + + private UncaughtExceptionHandler uncaughtExceptionHandler = defaultUncaughtExceptionHandler; + private boolean _isAlive; + + static private final Object staticLock = new Object(); + static private ThreadGroup _mainThreadGroup = null; + static private Thread _mainThread = null; + + static { + //_threadsById = new LinkedHashMap<>(); + // _mainThread will be set by `initMainThread + //_threadsById.put(_mainThread.getId(), _mainThread); + } + public Thread(Runnable target) { this(null, target, null, 1024); } @@ -137,59 +167,65 @@ public Thread(ThreadGroup group, Runnable target, String name) { public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { this.group = (group != null) ? group : currentThread().getThreadGroup(); this.target = target; - this.id = lastId++; - this.name = (name != null) ? name : ("thread-" + id++); + this.threadID = nextThreadID(); + this.name = (name != null) ? name : ("Thread-" + nextThreadNumber()); this.stackSize = stackSize; - _init(); + //_init(); } + @JTranscMethodBody(target = "cpp", value = { + //"asm(\"int $3\");", + "N::getThreadEnv()->currentThread = this;", + }) @JTranscMethodBody(target = "d", value = { - "this.thread = new Thread(delegate () {", - " {% METHOD java.lang.Thread:runInternal:()V %}();", - "});", + "this.thread = Thread.getThis();", + "_dCurrentThread = this;" }) - private void _init() { + @HaxeMethodBody(target = "cpp", value = "currentThread.set_value(this);" + + "_cpp_thread = cpp.vm.Thread.current();") + private void _initThreadEnv() { } - private boolean _isAlive; - - static private final Object staticLock = new Object(); - static private ThreadGroup _mainThreadGroup = null; - static private Thread _mainThread = null; - - synchronized static private Thread[] getThreadsCopy() { + /*synchronized static private Thread[] getThreadsCopy() { Collection threads = getThreadSetInternal().values(); synchronized (staticLock) { return threads.toArray(new Thread[0]); } - } + }*/ - static private void lazyPrepareThread() { + static private void initMainThread() { synchronized (staticLock) { + if (JTranscSystem.isCpp() || JTranscSystem.isD() || JTranscSystem.isHaxeCpp()) { + ThreadGroup mainThreadGroup = new ThreadGroup(); + new Thread(mainThreadGroup, "main")._initThreadEnv(); + return; + } if (_mainThreadGroup == null) { - _mainThreadGroup = new ThreadGroup("main"); + _mainThreadGroup = new ThreadGroup(); } if (_mainThread == null) { _mainThread = new Thread(_mainThreadGroup, "main"); - } - if (_threadsById == null) { - _threadsById = new LinkedHashMap<>(); - _threadsById.put(_mainThread.getId(), _mainThread); + _mainThread._initThreadEnv(); } } } - static private LinkedHashMap getThreadSetInternal() { - lazyPrepareThread(); - return _threadsById; - } + //static private LinkedHashMap getThreadSetInternal() { + //lazyPrepareThread(); + // return _threadsById; + //} public synchronized void start() { runInternalPreInit(); + _initThreadEnv(); _start(); } - @JTranscMethodBody(target = "d", value = "this.thread.start();") + @JTranscMethodBody(target = "d", value = { + "this.thread = new Thread(delegate () {", + " {% METHOD java.lang.Thread:runInternal:()V %}();", + "});", + "this.thread.start();"}) @JTranscMethodBody(target = "cs", value = { "_cs_thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate() { this{% IMETHOD java.lang.Thread:runInternal:()V %}(); }));", "_cs_thread.Start();", @@ -203,7 +239,7 @@ public synchronized void start() { @HaxeMethodBody(target = "cpp", value = "" + "var that = this;" + "cpp.vm.Thread.create(function():Void {" + - " that._cpp_thread = cpp.vm.Thread.current();" + + //" that._cpp_thread = cpp.vm.Thread.current();" + " that{% IMETHOD java.lang.Thread:runInternal:()V %}();" + "});" ) @@ -216,6 +252,7 @@ private void _start() { private void runInternal() { try { runInternalInit(); + _initThreadEnv(); run(); } catch (Throwable t) { uncaughtExceptionHandler.uncaughtException(this, t); @@ -236,31 +273,30 @@ private void runInternalPreInitNative() { private void runInternalPreInit() { runInternalPreInitNative(); - final LinkedHashMap set = getThreadSetInternal(); - synchronized (staticLock) { - set.put(getId(), this); - _isAlive = true; - } + //final LinkedHashMap set = getThreadSetInternal(); + //synchronized (staticLock) { + //set.put(getId(), this); + _isAlive = true; + //} } - @JTranscMethodBody(target = "d", value = "_dCurrentThread = this;") - @JTranscMethodBody(target = "cpp", value = "GC_init_thread(); _cpp_threads[t_.get_id()] = this;") + @JTranscMethodBody(target = "cpp", value = "GC_init_thread();") @HaxeMethodBody(target = "cpp", value = "threadsMap.set(_cpp_thread.handle, this);") private void runInternalInit() { } - @JTranscMethodBody(target = "cpp", value = "_cpp_threads.erase(t_.get_id()); GC_finish_thread();") - @HaxeMethodBody(target = "cpp", value = "threadsMap.remove(_cpp_thread.handle);") + @JTranscMethodBody(target = "cpp", value = "GC_finish_thread();") + @HaxeMethodBody(target = "cpp", value = "threadsMap.remove(_cpp_thread.handle); currentThread.set_value(null);") private void runInternalExit() { } private void runExit() { - final LinkedHashMap set = getThreadSetInternal(); - synchronized (this) { - runInternalExit(); - set.remove(getId()); - _isAlive = false; - } + //final LinkedHashMap set = getThreadSetInternal(); + //synchronized (this) { + runInternalExit(); + // set.remove(getId()); + _isAlive = false; + //} } @Override @@ -291,10 +327,10 @@ public boolean isInterrupted() { @Deprecated public void destroy() { + throw new NoSuchMethodError(); } public final boolean isAlive() { - //System.out.println("isAlive: " + _isAlive); return _isAlive; } @@ -305,7 +341,18 @@ public final boolean isAlive() { native public final void resume(); public final void setPriority(int newPriority) { - this.priority = newPriority; + ThreadGroup group; + if (newPriority > Thread.MAX_PRIORITY || newPriority < Thread.MIN_PRIORITY) { + throw new IllegalArgumentException(); + } + if ((group = getThreadGroup()) != null) { + this.priority = Math.min(group.getMaxPriority(), newPriority); + setPriorityNative(priority); + } + } + + public final void setPriorityNative(int newPriority) { + // TODO implement me } public final int getPriority() { @@ -325,16 +372,11 @@ public final ThreadGroup getThreadGroup() { } public static int activeCount() { - return getThreadsCopy().length; + return currentThread().getThreadGroup().activeCount(); } public static int enumerate(Thread tarray[]) { - int n = 0; - for (Thread thread : getThreadsCopy()) { - if (n >= tarray.length) break; - tarray[n++] = thread; - } - return n; + return currentThread().getThreadGroup().enumerate(tarray); } @Deprecated @@ -367,11 +409,18 @@ public final void join() throws InterruptedException { private boolean _isDaemon = false; @JTranscMethodBody(target = "d", value = "this.thread.isDaemon = p0;") + public final void setDaemonNative(boolean on) { + } + public final void setDaemon(boolean on) { + if (isAlive()) { + throw new IllegalThreadStateException(); + } + setDaemonNative(on); _isDaemon = on; } - @JTranscMethodBody(target = "d", value = "return this.thread.isDaemon;") + //@JTranscMethodBody(target = "d", value = "return this.thread.isDaemon;") public final boolean isDaemon() { return _isDaemon; } @@ -408,9 +457,8 @@ public static Map getAllStackTraces() { return new HashMap(); } - @JTranscMethodBody(target = "d", value = "return this.thread.id;") public long getId() { - return id; + return threadID; } public enum State { diff --git a/jtransc-rt/src/java/lang/ThreadGroup.java b/jtransc-rt/src/java/lang/ThreadGroup.java index 6eca694a..76bb455f 100644 --- a/jtransc-rt/src/java/lang/ThreadGroup.java +++ b/jtransc-rt/src/java/lang/ThreadGroup.java @@ -1,11 +1,12 @@ /* - * Copyright 2016 Carlos Ballesteros Velasco + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,179 +14,690 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package java.lang; +import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashSet; +import java.util.Iterator; +import java.util.List; + +import libcore.util.CollectionUtils; +/** + * {@code ThreadGroup} is a means of organizing threads into a hierarchical structure. + * This class is obsolete. See Effective Java Item 73, "Avoid thread groups" for details. + * + * @see Thread + */ public class ThreadGroup implements Thread.UncaughtExceptionHandler { - private ThreadGroup parent; + // Name of this ThreadGroup + // VM needs this field name for debugging. private String name; - private LinkedHashSet threads; - private LinkedHashSet children; + // Maximum priority for Threads inside this ThreadGroup private int maxPriority = Thread.MAX_PRIORITY; - private boolean isDaemon = false; - private boolean isDestroyed = false; + // The ThreadGroup to which this ThreadGroup belongs + // VM needs this field name for debugging. + final ThreadGroup parent; + /** + * Weak references to the threads in this group. + * Access is guarded by synchronizing on this field. + */ + private final List> threadRefs = new ArrayList>(5); + /** + * View of the threads. + * Access is guarded by synchronizing on threadRefs. + */ + private final Iterable threads = CollectionUtils.dereferenceIterable(threadRefs, true); + /** + * Thread groups. Access is guarded by synchronizing on this field. + */ + private final List groups = new ArrayList(3); + // Whether this ThreadGroup is a daemon ThreadGroup or not + private boolean isDaemon; + // Whether this ThreadGroup has already been destroyed or not + private boolean isDestroyed; + /* the VM uses these directly; do not rename */ + //static final ThreadGroup mSystem = new ThreadGroup(); + //static final ThreadGroup mMain = new ThreadGroup(mSystem, "main"); + + /** + * Constructs a new {@code ThreadGroup} with the given name. The new {@code ThreadGroup} + * will be child of the {@code ThreadGroup} to which the calling thread belongs. + * + * @param name the name + * @see Thread#currentThread + */ + public ThreadGroup(String name) { + this(Thread.currentThread().getThreadGroup(), name); + } + + /** + * Constructs a new {@code ThreadGroup} with the given name, as a child of the + * given {@code ThreadGroup}. + * + * @param parent the parent + * @param name the name + * @throws NullPointerException if {@code parent == null} + * @throws IllegalThreadStateException if {@code parent} has been + * destroyed already + */ + public ThreadGroup(ThreadGroup parent, String name) { + if (parent == null) { + throw new NullPointerException("parent == null"); + } + this.name = name; + this.parent = parent; + if (parent != null) { + parent.add(this); + this.setMaxPriority(parent.getMaxPriority()); + if (parent.isDaemon()) { + this.setDaemon(true); + } + } + } - synchronized private Thread[] getThreadsCopy() { - return (threads != null) ? threads.toArray(new Thread[0]) : new Thread[0]; + /** + * Initialize the special "main" ThreadGroup. + */ + public ThreadGroup() { + this.name = "main"; + this.parent = null; } - synchronized private ArrayList getAllThreads(ArrayList out) { - for (Thread thread : getThreadsCopy()) out.add(thread); - for (ThreadGroup group : getChildrenCopy()) group.getAllThreads(out); - return out; + /** + * Returns the number of running {@code Thread}s which are children of this thread group, + * directly or indirectly. + * + * @return the number of children + */ + public int activeCount() { + int count = 0; + synchronized (threadRefs) { + for (Thread thread : threads) { + if (thread.isAlive()) { + count++; + } + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + count += group.activeCount(); + } + } + return count; } - synchronized private ArrayList getAllThreads() { - return getAllThreads(new ArrayList<>()); + /** + * Returns the number of {@code ThreadGroup}s which are children of this group, + * directly or indirectly. + * + * @return the number of children + */ + public int activeGroupCount() { + int count = 0; + synchronized (groups) { + for (ThreadGroup group : groups) { + // One for this group & the subgroups + count += 1 + group.activeGroupCount(); + } + } + return count; } - synchronized private ThreadGroup[] getChildrenCopy() { - return (children != null) ? children.toArray(new ThreadGroup[0]) : new ThreadGroup[0]; + /** + * Adds a {@code ThreadGroup} to this thread group. + * + * @param g ThreadGroup to add + * @throws IllegalThreadStateException if this group has been destroyed already + */ + private void add(ThreadGroup g) throws IllegalThreadStateException { + synchronized (groups) { + if (isDestroyed) { + throw new IllegalThreadStateException(); + } + groups.add(g); + } } - synchronized private ArrayList getAllChildren(ArrayList out) { - out.add(this); - for (ThreadGroup group : getChildrenCopy()) group.getAllChildren(out); - return out; + /** + * Does nothing. The definition of this method depends on the deprecated + * method {@link #suspend()}. The exact behavior of this call was never + * specified. + * + * @param b Used to control low memory implicit suspension + * @return {@code true} (always) + * @deprecated Required deprecated method suspend(). + */ + @Deprecated + public boolean allowThreadSuspension(boolean b) { + // Does not apply to this VM, no-op + return true; } - synchronized private ArrayList getAllChildren() { - return getAllChildren(new ArrayList<>()); + /** + * Does nothing. + */ + public final void checkAccess() { } - private ThreadGroup() { - this("ThreadGroup"); + /** + * Destroys this thread group and recursively all its subgroups. It is only legal + * to destroy a {@code ThreadGroup} that has no threads in it. Any daemon + * {@code ThreadGroup} is destroyed automatically when it becomes empty (no threads + * or thread groups in it). + * + * @throws IllegalThreadStateException if this thread group or any of its + * subgroups has been destroyed already or if it still contains + * threads. + */ + public final void destroy() { + synchronized (threadRefs) { + synchronized (groups) { + if (isDestroyed) { + throw new IllegalThreadStateException( + "Thread group was already destroyed: " + + (this.name != null ? this.name : "n/a")); + } + if (threads.iterator().hasNext()) { + throw new IllegalThreadStateException( + "Thread group still contains threads: " + + (this.name != null ? this.name : "n/a")); + } + // Call recursively for subgroups + while (!groups.isEmpty()) { + // We always get the first element - remember, when the + // child dies it removes itself from our collection. See + // below. + groups.get(0).destroy(); + } + if (parent != null) { + parent.remove(this); + } + // Now that the ThreadGroup is really destroyed it can be tagged as so + this.isDestroyed = true; + } + } } - public ThreadGroup(String name) { - this(null, name); + /* + * Auxiliary method that destroys this thread group and recursively all its + * subgroups if this is a daemon ThreadGroup. + * + * @see #destroy + * @see #setDaemon + * @see #isDaemon + */ + private void destroyIfEmptyDaemon() { + // Has to be non-destroyed daemon to make sense + synchronized (threadRefs) { + if (isDaemon && !isDestroyed && !threads.iterator().hasNext()) { + synchronized (groups) { + if (groups.isEmpty()) { + destroy(); + } + } + } + } } - public ThreadGroup(ThreadGroup parent, String name) { - this.parent = parent; - this.name = (name != null) ? name : "ThreadGroup"; + /** + * Iterates over all active threads in this group (and its sub-groups) and + * stores the threads in the given array. Returns when the array is full or + * no more threads remain, whichever happens first. + *

+ *

Note that this method will silently ignore any threads that don't fit in the + * supplied array. + * + * @param threads the array into which the {@code Thread}s will be copied + * @return the number of {@code Thread}s that were copied + */ + public int enumerate(Thread[] threads) { + return enumerate(threads, true); + } + + /** + * Iterates over all active threads in this group (and, optionally, its + * sub-groups) and stores the threads in the given array. Returns when the + * array is full or no more threads remain, whichever happens first. + *

+ *

Note that this method will silently ignore any threads that don't fit in the + * supplied array. + * + * @param threads the array into which the {@code Thread}s will be copied + * @param recurse indicates whether {@code Thread}s in subgroups should be + * recursively copied as well + * @return the number of {@code Thread}s that were copied + */ + public int enumerate(Thread[] threads, boolean recurse) { + return enumerateGeneric(threads, recurse, 0, true); + } + + /** + * Iterates over all thread groups in this group (and its sub-groups) and + * and stores the groups in the given array. Returns when the array is full + * or no more groups remain, whichever happens first. + *

+ *

Note that this method will silently ignore any thread groups that don't fit in the + * supplied array. + * + * @param groups the array into which the {@code ThreadGroup}s will be copied + * @return the number of {@code ThreadGroup}s that were copied + */ + public int enumerate(ThreadGroup[] groups) { + return enumerate(groups, true); + } + + /** + * Iterates over all thread groups in this group (and, optionally, its + * sub-groups) and stores the groups in the given array. Returns when + * the array is full or no more groups remain, whichever happens first. + *

+ *

Note that this method will silently ignore any thread groups that don't fit in the + * supplied array. + * + * @param groups the array into which the {@code ThreadGroup}s will be copied + * @param recurse indicates whether {@code ThreadGroup}s in subgroups should be + * recursively copied as well or not + * @return the number of {@code ThreadGroup}s that were copied + */ + public int enumerate(ThreadGroup[] groups, boolean recurse) { + return enumerateGeneric(groups, recurse, 0, false); + } + + /** + * Copies into enumeration starting at + * enumerationIndex all Threads or ThreadGroups in the + * receiver. If recurse is true, recursively enumerate the + * elements in subgroups. + *

+ * If the array passed as parameter is too small no exception is thrown - + * the extra elements are simply not copied. + * + * @param enumeration array into which the elements will be copied + * @param recurse Indicates whether subgroups should be enumerated or not + * @param enumerationIndex Indicates in which position of the enumeration + * array we are + * @param enumeratingThreads Indicates whether we are enumerating Threads or + * ThreadGroups + * @return How many elements were enumerated/copied over + */ + private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, + boolean enumeratingThreads) { + if (enumeratingThreads) { + synchronized (threadRefs) { + // walk the references directly so we can iterate in reverse order + for (int i = threadRefs.size() - 1; i >= 0; --i) { + Thread thread = threadRefs.get(i).get(); + if (thread != null && thread.isAlive()) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumeration[enumerationIndex++] = thread; + } + } + } + } else { + synchronized (groups) { + for (int i = groups.size() - 1; i >= 0; --i) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumeration[enumerationIndex++] = groups.get(i); + } + } + } + if (recurse) { + synchronized (groups) { + for (ThreadGroup group : groups) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumerationIndex = group.enumerateGeneric(enumeration, recurse, + enumerationIndex, enumeratingThreads); + } + } + } + return enumerationIndex; + } + + /** + * Returns the maximum allowed priority for a {@code Thread} in this thread group. + * + * @return the maximum priority + * @see #setMaxPriority + */ + public final int getMaxPriority() { + return maxPriority; } + /** + * Returns the name of this thread group. + * + * @return the group's name + */ public final String getName() { return name; } + /** + * Returns this thread group's parent {@code ThreadGroup}. It can be null if this + * is the the root ThreadGroup. + * + * @return the parent + */ public final ThreadGroup getParent() { return parent; } - public final int getMaxPriority() { - return maxPriority; + /** + * Interrupts every {@code Thread} in this group and recursively in all its + * subgroups. + * + * @see Thread#interrupt + */ + public final void interrupt() { + synchronized (threadRefs) { + for (Thread thread : threads) { + thread.interrupt(); + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + group.interrupt(); + } + } } + /** + * Checks whether this thread group is a daemon {@code ThreadGroup}. + * + * @return true if this thread group is a daemon {@code ThreadGroup} + * @see #setDaemon + * @see #destroy + */ public final boolean isDaemon() { - return this.isDaemon; + return isDaemon; } + /** + * Checks whether this thread group has already been destroyed. + * + * @return true if this thread group has already been destroyed + * @see #destroy + */ public synchronized boolean isDestroyed() { return isDestroyed; } - public final void setDaemon(boolean daemon) { - this.isDaemon = daemon; + /** + * Outputs to {@code System.out} a text representation of the + * hierarchy of {@code Thread}s and {@code ThreadGroup}s in this thread group (and recursively). + * Proper indentation is used to show the nesting of groups inside groups + * and threads inside groups. + */ + public void list() { + // We start in a fresh line + System.out.println(); + list(0); + } + + /* + * Outputs to {@code System.out}a text representation of the + * hierarchy of Threads and ThreadGroups in this thread group (and recursively). + * The indentation will be four spaces per level of nesting. + * + * @param levels How many levels of nesting, so that proper indentation can + * be output. + */ + private void list(int levels) { + indent(levels); + System.out.println(this.toString()); + ++levels; + synchronized (threadRefs) { + for (Thread thread : threads) { + indent(levels); + System.out.println(thread); + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + group.list(levels); + } + } } - public final void setMaxPriority(int priority) { - this.maxPriority = priority; + private void indent(int levels) { + for (int i = 0; i < levels; i++) { + System.out.print(" "); // 4 spaces for each level + } } + /** + * Checks whether this thread group is a direct or indirect parent group of a + * given {@code ThreadGroup}. + * + * @param g the potential child {@code ThreadGroup} + * @return true if this thread group is parent of {@code g} + */ public final boolean parentOf(ThreadGroup g) { - return (g == this.parent) || ((this.parent != null) && this.parent.parentOf(g)); - } - - public final void checkAccess() { - } - - public int activeGroupCount() { - int count = 1; - for (ThreadGroup child : getChildrenCopy()) { - count += child.activeGroupCount(); + while (g != null) { + if (this == g) { + return true; + } + g = g.parent; } - return count; - } - - public int activeCount() { - return getAllThreads().size(); - } - - public int enumerate(Thread list[]) { - return enumerate(list, true); + return false; } - public int enumerate(ThreadGroup list[]) { - return enumerate(list, true); + /** + * Removes an immediate subgroup. + * + * @param g ThreadGroup to remove + * @see #add(Thread) + * @see #add(ThreadGroup) + */ + private void remove(ThreadGroup g) { + synchronized (groups) { + for (Iterator i = groups.iterator(); i.hasNext(); ) { + ThreadGroup threadGroup = i.next(); + if (threadGroup.equals(g)) { + i.remove(); + break; + } + } + } + destroyIfEmptyDaemon(); } - public int enumerate(Thread list[], boolean recurse) { - int n = 0; - for (Thread item : recurse ? getAllThreads() : Arrays.asList(getThreadsCopy())) { - if (n >= list.length) break; - list[n++] = item; + /** + * Resumes every thread in this group and recursively in all its + * subgroups. + * + * @see Thread#resume + * @see #suspend + * @deprecated Requires deprecated method Thread.resume(). + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void resume() { + synchronized (threadRefs) { + for (Thread thread : threads) { + thread.resume(); + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + group.resume(); + } } - return n; } - public int enumerate(ThreadGroup list[], boolean recurse) { - int n = 0; - for (ThreadGroup item : recurse ? getAllChildren() : Arrays.asList(getChildrenCopy())) { - if (n >= list.length) break; - list[n++] = item; + /** + * Sets whether this is a daemon {@code ThreadGroup} or not. Daemon + * thread groups are automatically destroyed when they become empty. + * + * @param isDaemon the new value + * @see #isDaemon + * @see #destroy + */ + public final void setDaemon(boolean isDaemon) { + this.isDaemon = isDaemon; + } + + /** + * Configures the maximum allowed priority for a {@code Thread} in this group and + * recursively in all its subgroups. + *

+ *

A caller can never increase the maximum priority of a thread group. + * Such an attempt will not result in an exception, it will + * simply leave the thread group with its current maximum priority. + * + * @param newMax the new maximum priority to be set + * @throws IllegalArgumentException if the new priority is greater than + * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY + * @see #getMaxPriority + */ + public final void setMaxPriority(int newMax) { + if (newMax <= this.maxPriority) { + if (newMax < Thread.MIN_PRIORITY) { + newMax = Thread.MIN_PRIORITY; + } + int parentPriority = parent == null ? newMax : parent.getMaxPriority(); + this.maxPriority = parentPriority <= newMax ? parentPriority : newMax; + synchronized (groups) { + for (ThreadGroup group : groups) { + group.setMaxPriority(newMax); + } + } } - return n; } + /** + * Stops every thread in this group and recursively in all its subgroups. + * + * @see Thread#stop() + * @see Thread#stop(Throwable) + * @see ThreadDeath + * @deprecated Requires deprecated method Thread.stop(). + */ + @SuppressWarnings("deprecation") @Deprecated public final void stop() { - for (Thread thread : getThreadsCopy()) thread.stop(); - for (ThreadGroup group : getChildrenCopy()) group.stop(); + if (stopHelper()) { + Thread.currentThread().stop(); + } } - public final void interrupt() { - for (Thread thread : getThreadsCopy()) thread.interrupt(); - for (ThreadGroup group : getChildrenCopy()) group.interrupt(); + @SuppressWarnings("deprecation") + private boolean stopHelper() { + boolean stopCurrent = false; + synchronized (threadRefs) { + Thread current = Thread.currentThread(); + for (Thread thread : threads) { + if (thread == current) { + stopCurrent = true; + } else { + thread.stop(); + } + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + stopCurrent |= group.stopHelper(); + } + } + return stopCurrent; } - @Deprecated + /** + * Suspends every thread in this group and recursively in all its + * subgroups. + * + * @see Thread#suspend + * @see #resume + * @deprecated Requires deprecated method Thread.suspend(). + */ @SuppressWarnings("deprecation") + @Deprecated public final void suspend() { - for (Thread thread : getThreadsCopy()) thread.suspend(); - for (ThreadGroup group : getChildrenCopy()) group.suspend(); + if (suspendHelper()) { + Thread.currentThread().suspend(); + } } - @Deprecated @SuppressWarnings("deprecation") - public final void resume() { - for (Thread thread : getThreadsCopy()) thread.resume(); - for (ThreadGroup group : getChildrenCopy()) group.resume(); - } - - synchronized public final void destroy() { - isDestroyed = true; - for (Thread thread : getThreadsCopy()) thread.destroy(); - for (ThreadGroup group : getChildrenCopy()) group.destroy(); + private boolean suspendHelper() { + boolean suspendCurrent = false; + synchronized (threadRefs) { + Thread current = Thread.currentThread(); + for (Thread thread : threads) { + if (thread == current) { + suspendCurrent = true; + } else { + thread.suspend(); + } + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + suspendCurrent |= group.suspendHelper(); + } + } + return suspendCurrent; } - public void list() { - System.out.println("Unimplemented ThreadGroup.list()"); + @Override + public String toString() { + return getClass().getName() + "[name=" + getName() + + ",maxPriority=" + getMaxPriority() + "]"; + } + + /** + * Handles uncaught exceptions. Any uncaught exception in any {@code Thread} + * is forwarded to the thread's {@code ThreadGroup} by invoking this + * method. + *

+ *

New code should use {@link Thread#setUncaughtExceptionHandler} instead of thread groups. + * + * @param t the Thread that terminated with an uncaught exception + * @param e the uncaught exception itself + */ + public void uncaughtException(Thread t, Throwable e) { + if (parent != null) { + parent.uncaughtException(t, e); + } else if (Thread.getDefaultUncaughtExceptionHandler() != null) { + // TODO The spec is unclear regarding this. What do we do? + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e); + } else if (!(e instanceof ThreadDeath)) { + // No parent group, has to be 'system' Thread Group + e.printStackTrace(System.err); + } } - native public void uncaughtException(Thread t, Throwable e); - - @Deprecated - public boolean allowThreadSuspension(boolean b) { - return false; + /** + * Called by the Thread constructor. + */ + final void addThread(Thread thread) throws IllegalThreadStateException { + synchronized (threadRefs) { + if (isDestroyed) { + throw new IllegalThreadStateException(); + } + threadRefs.add(new WeakReference(thread)); + } } - public String toString() { - return getClass().getName() + "[name=" + getName() + ",maxpri=" + getMaxPriority() + "]"; + /** + * Called by the VM when a Thread dies. + */ + final void removeThread(Thread thread) throws IllegalThreadStateException { + synchronized (threadRefs) { + for (Iterator i = threads.iterator(); i.hasNext(); ) { + if (i.next().equals(thread)) { + i.remove(); + break; + } + } + } + destroyIfEmptyDaemon(); } -} +} \ No newline at end of file diff --git a/jtransc-rt/src/libcore/util/CollectionUtils.java b/jtransc-rt/src/libcore/util/CollectionUtils.java new file mode 100644 index 00000000..ddd8fbc5 --- /dev/null +++ b/jtransc-rt/src/libcore/util/CollectionUtils.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package libcore.util; + +import java.lang.ref.Reference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +public final class CollectionUtils { + private CollectionUtils() { + } + + /** + * Returns an iterator over the values referenced by the elements of {@code + * iterable}. + * + * @param trim true to remove reference objects from the iterable after + * their referenced values have been cleared. + */ + public static Iterable dereferenceIterable( + final Iterable> iterable, final boolean trim) { + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + private final Iterator> delegate = iterable.iterator(); + private boolean removeIsOkay; + private T next; + + private void computeNext() { + removeIsOkay = false; + while (next == null && delegate.hasNext()) { + next = delegate.next().get(); + if (trim && next == null) { + delegate.remove(); + } + } + } + + @Override + public boolean hasNext() { + computeNext(); + return next != null; + } + + @Override + public T next() { + if (!hasNext()) { + throw new IllegalStateException(); + } + T result = next; + removeIsOkay = true; + next = null; + return result; + } + + public void remove() { + if (!removeIsOkay) { + throw new IllegalStateException(); + } + delegate.remove(); + } + }; + } + }; + } + + /** + * Sorts and removes duplicate elements from {@code list}. This method does + * not use {@link Object#equals}: only the comparator defines equality. + */ + public static void removeDuplicates(List list, Comparator comparator) { + Collections.sort(list, comparator); + int j = 1; + for (int i = 1; i < list.size(); i++) { + if (comparator.compare(list.get(j - 1), list.get(i)) != 0) { + T object = list.get(i); + list.set(j++, object); + } + } + if (j < list.size()) { + list.subList(j, list.size()).clear(); + } + } +}