From 7f4a100938d98301a26a722f3ad7711900614952 Mon Sep 17 00:00:00 2001 From: facboy Date: Sat, 30 Aug 2014 01:02:38 +0100 Subject: [PATCH 1/2] Fix https://github.com/joeferner/node-java/issues/143 (threading bug on Windows). Fix https://github.com/joeferner/node-java/pull/150 breaking gyp on Windows. --- binding.gyp | 8 ++- src/java.cpp | 116 ++++++++++++++++++++++---------------- src/java.h | 4 +- src/utils.h | 2 - test/RunInterface$1.class | Bin 0 -> 1007 bytes test/RunInterface.class | Bin 798 -> 1667 bytes test/RunInterface.java | 16 ++++++ test/dynamicProxy-test.js | 15 +++++ 8 files changed, 106 insertions(+), 55 deletions(-) create mode 100644 test/RunInterface$1.class diff --git a/binding.gyp b/binding.gyp index 06bbba48..4ee80ce2 100644 --- a/binding.gyp +++ b/binding.gyp @@ -1,14 +1,18 @@ { 'variables': { 'arch%': 'amd64', # linux JVM architecture. See $(JAVA_HOME)/jre/lib/<@(arch)/server/ + 'uname_m': '', 'conditions': [ ['target_arch=="ia32"', { 'arch%': 'i386' }], - ['"result = NULL; - JNIEnv* env = dynamicProxyData->env; - - NanScope(); - v8::Array* v8Args; - v8::Function* fn; - v8::Handle* argv; - int argc; - int i; - v8::Local v8Result; - jobject javaResult; - - v8::Local dynamicProxyDataFunctions = NanNew(dynamicProxyData->functions); - v8::Local fnObj = dynamicProxyDataFunctions->Get(NanNew(dynamicProxyData->methodName.c_str())); - if(fnObj->IsUndefined() || fnObj->IsNull()) { - printf("ERROR: Could not find method %s\n", dynamicProxyData->methodName.c_str()); - goto CleanUp; - } - if(!fnObj->IsFunction()) { - printf("ERROR: %s is not a function.\n", dynamicProxyData->methodName.c_str()); - goto CleanUp; - } - - fn = v8::Function::Cast(*fnObj); - - if(dynamicProxyData->args) { - v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, dynamicProxyData->args)); - argc = v8Args->Length(); - } else { - argc = 0; - } - argv = new v8::Handle[argc]; - for(i=0; iGet(i); - } - v8Result = fn->Call(dynamicProxyDataFunctions, argc, argv); - delete[] argv; - if(!dynamicProxyDataVerify(dynamicProxyData)) { - return; + JNIEnv* env; + int ret = dynamicProxyData->java->getJvm()->GetEnv((void**)&env, JNI_VERSION_1_6); + if (ret != JNI_OK) { + printf("ERROR: jvm->GetEnv returned %d\n", ret); + goto CleanUp; } - javaResult = v8ToJava(env, v8Result); - if(javaResult == NULL) { - dynamicProxyData->result = NULL; - dynamicProxyData->resultGlobalRef = NULL; - } else { - dynamicProxyData->result = javaResult; - dynamicProxyData->resultGlobalRef = env->NewGlobalRef(javaResult); + { + NanScope(); + v8::Array* v8Args; + v8::Function* fn; + v8::Handle* argv; + int argc; + int i; + v8::Local v8Result; + jobject javaResult; + + v8::Local dynamicProxyDataFunctions = NanNew(dynamicProxyData->functions); + v8::Local fnObj = dynamicProxyDataFunctions->Get(NanNew(dynamicProxyData->methodName.c_str())); + if(fnObj->IsUndefined() || fnObj->IsNull()) { + printf("ERROR: Could not find method %s\n", dynamicProxyData->methodName.c_str()); + goto CleanUp; + } + if(!fnObj->IsFunction()) { + printf("ERROR: %s is not a function.\n", dynamicProxyData->methodName.c_str()); + goto CleanUp; + } + + fn = v8::Function::Cast(*fnObj); + + if(dynamicProxyData->args) { + v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, dynamicProxyData->args)); + argc = v8Args->Length(); + } else { + argc = 0; + } + argv = new v8::Handle[argc]; + for(i=0; iGet(i); + } + v8Result = fn->Call(dynamicProxyDataFunctions, argc, argv); + delete[] argv; + if(!dynamicProxyDataVerify(dynamicProxyData)) { + return; + } + + javaResult = v8ToJava(env, v8Result); + if(javaResult == NULL) { + dynamicProxyData->result = NULL; + } else { + dynamicProxyData->result = env->NewGlobalRef(javaResult); + } } CleanUp: @@ -1061,13 +1066,13 @@ void EIO_AfterCallJs(uv_work_t* req) { JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jobject src, jlong ptr, jobject method, jobjectArray args) { long myThreadId = my_getThreadId(); + bool hasArgsGlobalRef = false; + // args needs to be global, you can't send env across thread boundaries DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr; - dynamicProxyData->env = env; dynamicProxyData->args = args; dynamicProxyData->done = false; dynamicProxyData->result = NULL; - dynamicProxyData->resultGlobalRef = NULL; jclass methodClazz = env->FindClass("java/lang/reflect/Method"); jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;"); @@ -1083,6 +1088,12 @@ JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jo EIO_AfterCallJs(req); #endif } else { + if (args) { + // if args is not null and we have to kick this across the thread boundary, make it a global ref + dynamicProxyData->args = (jobjectArray) env->NewGlobalRef(args); + hasArgsGlobalRef = true; + } + uv_queue_work(uv_default_loop(), req, EIO_CallJs, (uv_after_work_cb)EIO_AfterCallJs); while(!dynamicProxyData->done) { @@ -1093,10 +1104,17 @@ JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jo if(!dynamicProxyDataVerify(dynamicProxyData)) { return NULL; } + if(hasArgsGlobalRef) { + env->DeleteGlobalRef(dynamicProxyData->args); + } + + jobject result = NULL; if(dynamicProxyData->result) { - env->DeleteGlobalRef(dynamicProxyData->resultGlobalRef); + // need to retain a local ref so that we can return it, otherwise the returned object gets corrupted + result = env->NewLocalRef(dynamicProxyData->result); + env->DeleteGlobalRef(dynamicProxyData->result); } - return dynamicProxyData->result; + return result; } JNIEXPORT void JNICALL Java_node_NodeDynamicProxyClass_unref(JNIEnv *env, jobject src, jlong ptr) { diff --git a/src/java.h b/src/java.h index f5f6f4d0..2e6ca61c 100644 --- a/src/java.h +++ b/src/java.h @@ -12,7 +12,7 @@ class Java : public node::ObjectWrap { public: static void Init(v8::Handle target); JavaVM* getJvm() { return m_jvm; } - JNIEnv* getJavaEnv() { return m_env; } + JNIEnv* getJavaEnv() { return m_env; } // can only be used safely by the main thread as this is the thread it belongs to jobject getClassLoader() { return m_classLoader; } private: @@ -47,7 +47,7 @@ class Java : public node::ObjectWrap { static v8::Persistent s_ct; JavaVM* m_jvm; - JNIEnv* m_env; + JNIEnv* m_env; // can only be used safely by the main thread as this is the thread it belongs to jobject m_classLoader; std::string m_classPath; static std::string s_nativeBindingLocation; diff --git a/src/utils.h b/src/utils.h index 0430dba9..60402bd6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,7 +32,6 @@ typedef enum _jvalueType { struct DynamicProxyData { unsigned int markerStart; - JNIEnv* env; Java* java; std::string interfaceName; v8::Persistent functions; @@ -40,7 +39,6 @@ struct DynamicProxyData { std::string methodName; jobjectArray args; jobject result; - jobject resultGlobalRef; int done; unsigned int markerEnd; }; diff --git a/test/RunInterface$1.class b/test/RunInterface$1.class new file mode 100644 index 0000000000000000000000000000000000000000..7210ed1172d017e9d371ef7de22839fd5d585c94 GIT binary patch literal 1007 zcma)5ZEw<06n-u(R9Iz<=@g8z7nsm(AijRkm>8xB8Mzsp24 z`oSOIk20QHaX`X{HR*Y~=bZcWIj6t>{QL#rKAz-p1r05>wYaNAvw%EWODMuAUyAD#5){DojHb$jcW!R$pN&agZP1b>r!8glv09flMs4hkg||^6J^m`ttyX5aYinNU!k5Aeqc{+!uetK0 zk%g{JgMDD*8m`+|f^Fj}R&129YGVyG8+Y)~!Xq1xQ6su$d)B)IDCIkJ#cAhoc*Z@N zba@)0Z1L~A*5?Wr43cI)n?h}*9{mduDWBN!C3ivdR=IlDC}GKXdy01!_U;A^-pY literal 0 HcmV?d00001 diff --git a/test/RunInterface.class b/test/RunInterface.class index 77d3d2e14bcd294d40bebe25288280b86d4cb031..292ae6386379f93ec6bbf3a597626ae918b90861 100644 GIT binary patch literal 1667 zcma)6+foxj5IvJ@vPlR?f*{}>K|=yW@NT>z-l74j#KJ4QFs-NOV_~MHc zV)+4nl;xS-m@Jm4`!KyTedhF;?wcb!|+qjZJ z5?5_hY+TDAjq4etaKj8Yjpvq)MH{zmEC~!%rFPW|r|v%WbmOtBeI*65RViJy)O6Zy zw{7E&Kz|oEzo=f?xEozM%OwE_B(HhW(>Da-h3RzxYpJ#63LL0;(p~XiZ@TJk4X5Lw}bi->44AdU`vKy@;kW#)h5$HQ!*yEh+!i>LSLJB2tN~A>vSwh+rQpVVh;2IZWjSP}BXjqC z-F>6ImSnx}alY2_Ro%VknR4Z#6`3=UvKYl!7WXkm4ccw%c{6!n2C6#sA7Tu{LxI5n z8qvZAh(?$f1};4ZVq_?l+SxKT!`d)O)F1^U1b%jrVET=&pzARG?nMoe|N`!)!UFCib~*l zrMYoIYbV;;QA||AdFOZmRS2l2NfiqY|AE01#4cE0GaK>v>hdm<8`d`Lm0hGZc9GsF&Xi`i zk=aJyRcp-pjO^@Z^v}fSdNIeD8!(|MrZkjWKoQfJ<7jl84ZbJY7 delta 389 zcmYjNJ5Iwu6r5ehyYVkLKoSVyV-g5*WC>HzrbOZr2sv~qf_sFhQ&`(kP*TxQp-^xD zZo>r-v$%jQX5MG!y{CPMo>cnz{r&;q3@1J`b^=5gG_e~Xz+QkR_B9SP5{<*^ay%Le zL>Ke2Sj?`ji+sAc(HQ+Now8Itrqx$wrXn*-ac*WZ>8~LUafDdFEsNVSx5=@Ev1vH> z=GhtEA%Wrn_B9|t2eHj8Aq%pbzCpY)5LhSrkVHvwz{&vAbs diff --git a/test/RunInterface.java b/test/RunInterface.java index 7681e3c4..042664da 100644 --- a/test/RunInterface.java +++ b/test/RunInterface.java @@ -1,3 +1,4 @@ +import java.util.concurrent.CountDownLatch; public class RunInterface { public static interface Interface0Arg { @@ -25,4 +26,19 @@ public void run1Args(Interface1Arg r) { public int runWithReturn(InterfaceWithReturn r) { return r.run(42); } + + public int runInAnotherThread(final InterfaceWithReturn r) throws InterruptedException { + final int[] result = {0}; + final CountDownLatch latch = new CountDownLatch(1); + Thread t = new Thread(new Runnable() { + @Override + public void run() { + result[0] = r.run(46); + latch.countDown(); + } + }); + t.start(); + latch.await(); + return result[0]; + } } diff --git a/test/dynamicProxy-test.js b/test/dynamicProxy-test.js index 81d62dff..7bdc76b9 100644 --- a/test/dynamicProxy-test.js +++ b/test/dynamicProxy-test.js @@ -80,5 +80,20 @@ exports['Dynamic Proxy'] = nodeunit.testCase({ } waitForThread(); + }, + + "thread issue #143": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', { + run: function (i) { + return i - 1; + } + }); + + var runInterface = java.newInstanceSync("RunInterface"); + runInterface.runInAnotherThread(myProxy, function(err, result) { + test.equals(result, 45); + + test.done(); + }); } }); From c3f157c6ae73146fd22872ecdb73cc8f525a6274 Mon Sep 17 00:00:00 2001 From: facboy Date: Sat, 30 Aug 2014 20:54:23 +0100 Subject: [PATCH 2/2] Make dynamic java proxy equals/hashCode methods equivalent to java.lang.Object if they are not defined on the js proxy. --- .gitignore | 1 + src-java/node/NodeDynamicProxyClass.class | Bin 1144 -> 2644 bytes src-java/node/NodeDynamicProxyClass.java | 42 ++++++-- src/java.cpp | 119 +++++++++++++--------- test/RunInterface.class | Bin 1667 -> 2447 bytes test/RunInterface.java | 22 ++++ test/dynamicProxy-test.js | 107 +++++++++++++++++++ 7 files changed, 234 insertions(+), 57 deletions(-) diff --git a/.gitignore b/.gitignore index ef47a1f1..05ed4abf 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules npm-debug.log .idea node-java.cbp +*.iml diff --git a/src-java/node/NodeDynamicProxyClass.class b/src-java/node/NodeDynamicProxyClass.class index 9584b6eed16702afa4602f1ccbdcd767ba991816..9d891fc63894013248d6c831c7929de1bbe41f9e 100644 GIT binary patch literal 2644 zcma)8S##4?7(KF`$WfHUh9qPmKmruUEN#DUPrOiezLt5V|jP z-(Gp{OnC*`hDj8FOvO;*5d_&MG*ETpNaPUg6_{jCrwmMZv4MDEuxdxQy4t<8=jZ;7xJ*mO^(; z!2UlSHT6c@~;<$w9%<PI<-k#P;VQ$9kFMX6 zpiJM)>m}E)t+EVTLQBOW3<;6M?C3m`O&FHp9+l9W2zcjQ$FK_Ho<(HRUeqOYWeiK7 zt1K<(&UtOYY$I;bAg!$#ixE?Ed`zua+LDn! zCQYqe7FH$AEsE$3-F!F_I&!X-zjj(HdD%RzODr#G7}m0Vjk$9{8on-pNRL(E|BBe^ z=;ey(Hly;ROBf0yn6q=0e6d>KX~(f0GHNLaek|@1*=bIJ8f@MmGkcN$*$D|znCL{_ z^h-1Rr1-VUj6~40^7=_b6lY*Vc@GFpRU|PbmHJwqu8kcU4@$dn(??2QofX@ew{&@d<9r_*BJb z_*}&>cBuFQU&{DO?0l^vfl&#uM(nE2tg(K*qL~!0t8wNvjf!vZEfp(jK=B zkktf}u)ASVC!Ds@oCQNOja$0o6;DX@9mRZd!%EoMh*ayYG@CK6iJ6-TI;@5u9^>Tg;7VYl|Gd-~jl8Bl;TXKu2d!~xc9bBOL>QBIU|)kuO_}OoJciOA zq|r-EN~T^-8NDfK?RMC*KBjkjNKlP(;~tUF9n_)fEfRDy!3O0y611-235;D0|2kI+ z6{y$=+ctHL)a7N(tms+Zv%QH+!Fu@?bc-2_I_vAM%+%YqsdhCK>81>6%hd~_p?VYP zU|fHnaofnSCiAf%Ex&HTo5$B>1ETexsJ>u`dh3pl&U12y_=7278^Rb6j~F2hVkI)X7;2*7_4t^k}(CVxhB z-~NYa8;kTu?g_;0`&Oae!N~sb!Bupuq4N&n{9Z#>@<+&#yNHC>5WDvP@v32aat)i- zu=!qpq0Hv0KLEF)18x)Q?^X=BtuFM0+7wn@9d3 zy6fmPdZ0e2tjx86%{LHPQ?=;sD5rS9bRc;aOT{u{d2X5gepWYgQPJ%5rr$Ti>ZiA1<3qT2SZf>R#fh#==qDy7 zKStMHc{@kJvfy~ZqGO3=L(f6j3M+@v!cK(}3pZ3F8Y5X2e?2MRc(K;EB7DVmWX%_EVsf1;~GN=xgp?Hd6@_23H z6dhWSDV2VaEKi@1TJih*A4-{an?Oc1k`wrL%x%r7!>qd8GKwS=L#oX diff --git a/src-java/node/NodeDynamicProxyClass.java b/src-java/node/NodeDynamicProxyClass.java index 223c39ff..ce04f2bb 100644 --- a/src-java/node/NodeDynamicProxyClass.java +++ b/src-java/node/NodeDynamicProxyClass.java @@ -1,11 +1,23 @@ package node; -import java.util.HashSet; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; public class NodeDynamicProxyClass implements java.lang.reflect.InvocationHandler { + private static final Method EQUALS; + private static final Method HASHCODE; + static { + try { + EQUALS = Object.class.getMethod("equals", Object.class); + HASHCODE = Object.class.getMethod("hashCode"); + } catch (NoSuchMethodException e) { + throw new ExceptionInInitializerError(e); + } + } + private native Object callJs(long ptr, java.lang.reflect.Method m, Object[] args) throws Throwable; private native void unref(long ptr) throws Throwable; - public long ptr; + public final long ptr; public NodeDynamicProxyClass(String path, long ptr) { try{ @@ -18,13 +30,25 @@ public NodeDynamicProxyClass(String path, long ptr) { public Object invoke(Object proxy, java.lang.reflect.Method m, Object[] args) throws Throwable { - Object result = callJs(this.ptr, m, args); - //if(result == null) { - // System.out.println("invoke: null"); - //} else { - // System.out.println("invoke: " + result + " class: " + result.getClass() + " to string: " + result.toString()); - //} - return result; + try { + Object result = callJs(this.ptr, m, args); + //if(result == null) { + // System.out.println("invoke: null"); + //} else { + // System.out.println("invoke: " + result + " class: " + result.getClass() + " to string: " + result.toString()); + //} + return result; + } catch (NoSuchMethodError e) { + // use 'vanilla' implementations otherwise - the object that persists between multiple invocations is + // 'this', not the 'proxy' argument, so we operate on this. + if (EQUALS.equals(m)) { + // need to check if the arg is a Proxy, and if so, if its invocation handler == this! + return Proxy.isProxyClass(args[0].getClass()) && Proxy.getInvocationHandler(args[0]) == this; + } else if (HASHCODE.equals(m)) { + return System.identityHashCode(this); + } + throw e; + } } public void unref() throws Throwable { diff --git a/src/java.cpp b/src/java.cpp index 125d3774..3f00421c 100644 --- a/src/java.cpp +++ b/src/java.cpp @@ -13,6 +13,10 @@ #include #include +#define DYNAMIC_PROXY_NO_SUCH_METHOD_ERROR -1 +#define DYNAMIC_PROXY_NOT_A_FUNCTION_ERROR -2 +#define DYNAMIC_PROXY_NO_ENV_ERROR -3 + long v8ThreadId; /*static*/ v8::Persistent Java::s_ct; @@ -1010,60 +1014,67 @@ void EIO_AfterCallJs(uv_work_t* req) { int ret = dynamicProxyData->java->getJvm()->GetEnv((void**)&env, JNI_VERSION_1_6); if (ret != JNI_OK) { printf("ERROR: jvm->GetEnv returned %d\n", ret); - goto CleanUp; + dynamicProxyData->done = DYNAMIC_PROXY_NO_ENV_ERROR; + return; } - { - NanScope(); - v8::Array* v8Args; - v8::Function* fn; - v8::Handle* argv; - int argc; - int i; - v8::Local v8Result; - jobject javaResult; - - v8::Local dynamicProxyDataFunctions = NanNew(dynamicProxyData->functions); - v8::Local fnObj = dynamicProxyDataFunctions->Get(NanNew(dynamicProxyData->methodName.c_str())); - if(fnObj->IsUndefined() || fnObj->IsNull()) { - printf("ERROR: Could not find method %s\n", dynamicProxyData->methodName.c_str()); - goto CleanUp; - } - if(!fnObj->IsFunction()) { - printf("ERROR: %s is not a function.\n", dynamicProxyData->methodName.c_str()); - goto CleanUp; - } - - fn = v8::Function::Cast(*fnObj); - - if(dynamicProxyData->args) { - v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, dynamicProxyData->args)); - argc = v8Args->Length(); - } else { - argc = 0; - } - argv = new v8::Handle[argc]; - for(i=0; iGet(i); - } - v8Result = fn->Call(dynamicProxyDataFunctions, argc, argv); - delete[] argv; - if(!dynamicProxyDataVerify(dynamicProxyData)) { - return; - } - - javaResult = v8ToJava(env, v8Result); - if(javaResult == NULL) { - dynamicProxyData->result = NULL; - } else { - dynamicProxyData->result = env->NewGlobalRef(javaResult); - } + NanScope(); + v8::Array* v8Args; + v8::Function* fn; + v8::Handle* argv; + int argc; + int i; + v8::Local v8Result; + jobject javaResult; + + v8::Local dynamicProxyDataFunctions = NanNew(dynamicProxyData->functions); + v8::Local fnObj = dynamicProxyDataFunctions->Get(NanNew(dynamicProxyData->methodName.c_str())); + if(fnObj->IsUndefined() || fnObj->IsNull()) { + dynamicProxyData->done = DYNAMIC_PROXY_NO_SUCH_METHOD_ERROR; + return; + } + if(!fnObj->IsFunction()) { + dynamicProxyData->done = DYNAMIC_PROXY_NOT_A_FUNCTION_ERROR; + return; + } + + fn = v8::Function::Cast(*fnObj); + + if(dynamicProxyData->args) { + v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, dynamicProxyData->args)); + argc = v8Args->Length(); + } else { + argc = 0; + } + argv = new v8::Handle[argc]; + for(i=0; iGet(i); + } + v8Result = fn->Call(dynamicProxyDataFunctions, argc, argv); + delete[] argv; + if(!dynamicProxyDataVerify(dynamicProxyData)) { + return; + } + + javaResult = v8ToJava(env, v8Result); + if(javaResult == NULL) { + dynamicProxyData->result = NULL; + } else { + dynamicProxyData->result = env->NewGlobalRef(javaResult); } -CleanUp: dynamicProxyData->done = true; } +void throwNewThrowable(JNIEnv* env, const char * excClassName, std::string msg) { + jclass newExcCls = env->FindClass(excClassName); + jthrowable throwable = env->ExceptionOccurred(); + if (throwable != NULL) { + env->Throw(throwable); // this should only be Errors, according to the docs + } + env->ThrowNew(newExcCls, msg.c_str()); +} + JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jobject src, jlong ptr, jobject method, jobjectArray args) { long myThreadId = my_getThreadId(); bool hasArgsGlobalRef = false; @@ -1102,12 +1113,24 @@ JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jo } if(!dynamicProxyDataVerify(dynamicProxyData)) { - return NULL; + throwNewThrowable(env, "java/lang/IllegalStateException", "dynamicProxyData was corrupted"); } if(hasArgsGlobalRef) { env->DeleteGlobalRef(dynamicProxyData->args); } + switch (dynamicProxyData->done) { + case DYNAMIC_PROXY_NO_SUCH_METHOD_ERROR: + throwNewThrowable(env, "java/lang/NoSuchMethodError", "Could not find js function " + dynamicProxyData->methodName); + break; + case DYNAMIC_PROXY_NOT_A_FUNCTION_ERROR: + throwNewThrowable(env, "java/lang/IllegalStateException", dynamicProxyData->methodName + " is not a function"); + break; + case DYNAMIC_PROXY_NO_ENV_ERROR: + throwNewThrowable(env, "java/lang/IllegalStateException", "Could not retrieve JNIEnv"); + break; + } + jobject result = NULL; if(dynamicProxyData->result) { // need to retain a local ref so that we can return it, otherwise the returned object gets corrupted diff --git a/test/RunInterface.class b/test/RunInterface.class index 292ae6386379f93ec6bbf3a597626ae918b90861..ce10e59bbb6c903678064c995523136e86fa290e 100644 GIT binary patch literal 2447 zcma);eOKF59LK-6P202$8wJK3Qyw;^ec)l>oZHH9jDf2bP(UWbr%*B)tSP3+=1cKK z_=kEN9K_=b@I`t2-XtbrgB>`Bdv9`ozt8XS-t?b;|NI-kG+H_I;b9&Fm{;*AkGHUp z$0aOg@No`lJkFq&!6!Ln@o5e!>T+0;JIh(D;4`WHxg1ur=*5~`t!MB>1{)bX5$LU1 zj$uF38^#mUX|5WMYg+n|vhz0cGDdo(o*7 zuew&KFdD8R6IFrqjA@z9tU$6@+Mw!ud&dyCST`+W+5KVLu-EnN7T@&L+YP<7q1&c> z4qhrw)9eUbIAMrT)pjlE4flqMCpIH5Qanp5>)K*Wm~$nJLyxokz4qwVeC6*+hWlNCuEJ`pQ72^bGz?)_!xn~_R>$Z#Q}W>}IeaZ}{T%s~gl`18J<(Wt*{*SG zqXZA(TRD8EL6=6iF`Pj|!ww7$&#bcrm!Y{{NV7!*RJG?`hVk_EAR7!uLo16<#Ob^@bmZ39%)Z$!yZGo3#ERJB4 z4U{&5192iwzW~(|4}jVXyPtO(BUYWTZTpI)7&K@~jPPpi<89C5EUV2`j%zj|#}Pig z&zTo2QsqO4-?&KNnkSUtn1lii*SXSsA&lY%p9J2;dmJU8##zdCRgRDl@W%*rfe@g; zk46t6y&kOJR|sHKMG+-p1j?uc*d@wHY@yPVJc5$I|MRQpetwiLqnjTu>8Fn_gy;g% zV;*`O69M{ufX-IB%ST8h@Cz0Fu+tO)uEtQ=5S1lr&_hiUmGFZ9nZ{Gpm{$JEZ6=c| zOUFoWDu>7{A4Am{HtcZ$X=4pfpE;0`# z+9-Q|-104ieoO~`sNBDaDS9Hcrdi1(b-Qp2AFyn<$yDg=hy1^TyBuY)W`fYw%I~;v zfQykAe&iFe$kouI8b*FEOrj*p`lKoc=zbM59mAwTOe(}w>4@*pY~WD3TzQE?RNI^f zzr)b)##rf)l@3|=!{DO2mj6KEl;~z+sMG2?i;n`g;?ec|$}r>Y@gRnIJ-P%Qc=B`X dO7JWx{6*D^{&bcd+KVfk2RL8loaH|M{sRsz{=)zO delta 589 zcmYk3xlY4C6h-gEi6?fPfMXJzuml2O-@?A{OTt=EP$LxRC?FJwk`@U?<^vFafGCg< zf}X$NLqObd5MnI7W#*lk_pWc9>*(Ic_sbiAVeFZx!Ds{tjD;~CfrE(%YB4D>WkSca z#EitO2?KK`!kAZPL7gm0EJ-X&tOz)s2sxE2yKYq=T0AY6&W;a{O8KR;6N$CTed6)6 zTU1_io*B3l>)wIZmUfY{uz{37s9ZWP=T%Zrrs&;iU7Cwc?_KMNyV$beV%x%w0(Oyl za)SHf0Ii6zZ?!@YtVq$OnT^m3wcGOy@yH;623nII&=7=$Mn+b(2u)~aCXhi3ZQ9$? zo2OKLjJ1HxG2^Utd?xAN5&dO=D4WQV5@%-{b*GodZ w+lPM6m*WHj7^EgcWP-dk&+}o%0m>Ms&vLhxTG+Zl4PvOHPtYgn4W4uT0Eg)}NdN!< diff --git a/test/RunInterface.java b/test/RunInterface.java index 042664da..210c05c8 100644 --- a/test/RunInterface.java +++ b/test/RunInterface.java @@ -41,4 +41,26 @@ public void run() { latch.await(); return result[0]; } + + public boolean runEquals(final InterfaceWithReturn r) { + return r.equals(Boolean.FALSE); + } + + public int runHashCode(final InterfaceWithReturn r) { + return r.hashCode(); + } + + private InterfaceWithReturn prev; + + public void setInstance(final InterfaceWithReturn r) { + prev = r; + } + + public boolean runEqualsInstance(final InterfaceWithReturn r) { + return r.equals(prev); + } + + public String runToString(final InterfaceWithReturn r) { + return r.toString(); + } } diff --git a/test/dynamicProxy-test.js b/test/dynamicProxy-test.js index 7bdc76b9..da772b97 100644 --- a/test/dynamicProxy-test.js +++ b/test/dynamicProxy-test.js @@ -95,5 +95,112 @@ exports['Dynamic Proxy'] = nodeunit.testCase({ test.done(); }); + }, + + "java equals()": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', { + }); + + var runInterface = java.newInstanceSync("RunInterface"); + var result = runInterface.runEqualsSync(myProxy); + + test.equals(result, false); + + test.done(); + }, + + "java equals() same instance": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', { + }); + + var runInterface = java.newInstanceSync("RunInterface"); + runInterface.setInstanceSync(myProxy); + var result = runInterface.runEqualsInstanceSync(myProxy); + + test.equals(result, true); + + test.done(); + }, + + "java equals() different instance": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', {}); + var myProxy2 = java.newProxy('RunInterface$InterfaceWithReturn', {}); + + var runInterface = java.newInstanceSync("RunInterface"); + runInterface.setInstanceSync(myProxy); + var result = runInterface.runEqualsInstanceSync(myProxy2); + + test.equals(result, false); + + test.done(); + }, + + "js equals()": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', { + equals: function (obj) { + return true; + } + }); + + var runInterface = java.newInstanceSync("RunInterface"); + var result = runInterface.runEqualsSync(myProxy); + + test.equals(result, true); + + test.done(); + }, + + "java hashCode()": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', { + }); + + var runInterface = java.newInstanceSync("RunInterface"); + var result = runInterface.runHashCodeSync(myProxy); + var result2 = runInterface.runHashCodeSync(myProxy); + + test.equals(result, result2); + + test.done(); + }, + + "js hashCode()": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', { + hashCode: function() { + return 1234; + } + }); + + var runInterface = java.newInstanceSync("RunInterface"); + var result = runInterface.runHashCodeSync(myProxy); + + test.equals(result, 1234); + + test.done(); + }, + + "java toString()": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', {}); + + var runInterface = java.newInstanceSync("RunInterface"); + var result = runInterface.runToStringSync(myProxy); + + test.equals(result, "[object Object]"); + + test.done(); + }, + + "js toString()": function (test) { + var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', { + toString: function() { + return "myRunInterface"; + } + }); + + var runInterface = java.newInstanceSync("RunInterface"); + var result = runInterface.runToStringSync(myProxy); + + test.equals(result, "myRunInterface"); + + test.done(); } });