Skip to content

Commit 598bc87

Browse files
committed
Fix dynamic proxy unref() so that it can be called from Js and Java.
Add 'invocationHandler' js field to dynamic proxies which returns the original 'functions' object.
1 parent 0b67aaf commit 598bc87

File tree

9 files changed

+140
-22
lines changed

9 files changed

+140
-22
lines changed
242 Bytes
Binary file not shown.

src-java/node/NodeDynamicProxyClass.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public NodeDynamicProxyClass(String path, long ptr) {
2727
this.ptr = ptr;
2828
}
2929

30+
@Override
3031
public Object invoke(Object proxy, java.lang.reflect.Method m, Object[] args) throws Throwable
3132
{
3233
try {
@@ -45,6 +46,8 @@ public Object invoke(Object proxy, java.lang.reflect.Method m, Object[] args) th
4546
return args[0] == proxy;
4647
} else if (HASHCODE.equals(m)) {
4748
return System.identityHashCode(proxy);
49+
} else if ("unref".equals(m.getName()) && m.getParameterTypes().length == 0 && m.getReturnType() == Void.TYPE) {
50+
this.unref();
4851
}
4952
throw e;
5053
}

src/java.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
#include "java.h"
32
#include <string.h>
43
#ifdef WIN32
@@ -67,6 +66,8 @@ long my_getThreadId() {
6766
NODE_SET_PROTOTYPE_METHOD(t, "instanceOf", instanceOf);
6867

6968
target->Set(NanNew<v8::String>("Java"), t->GetFunction());
69+
70+
JavaProxyObject::init();
7071
}
7172

7273
NAN_METHOD(Java::New) {
@@ -411,7 +412,7 @@ NAN_METHOD(Java::newProxy) {
411412
return NanThrowError(javaExceptionToV8(self, env, errStr.str()));
412413
}
413414

414-
v8::Handle<v8::Value> result = javaToV8(self, env, proxyInstance);
415+
v8::Handle<v8::Value> result = javaToV8(self, env, proxyInstance, dynamicProxyData);
415416

416417
NanAssignPersistent(dynamicProxyData->jsObject, result);
417418
NanReturnValue(result);
@@ -1207,9 +1208,5 @@ JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jo
12071208

12081209
JNIEXPORT void JNICALL Java_node_NodeDynamicProxyClass_unref(JNIEnv *env, jobject src, jlong ptr) {
12091210
DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr;
1210-
if(!dynamicProxyDataVerify(dynamicProxyData)) {
1211-
return;
1212-
}
1213-
NanDisposePersistent(dynamicProxyData->jsObject);
1214-
NanDisposePersistent(dynamicProxyData->functions);
1211+
unref(dynamicProxyData);
12151212
}

src/javaObject.cpp

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
#include "javaObject.h"
32
#include "java.h"
43
#include "javaScope.h"
@@ -93,18 +92,6 @@ JavaObject::JavaObject(Java *java, jobject obj) {
9392

9493
JavaObject::~JavaObject() {
9594
JNIEnv *env = m_java->getJavaEnv();
96-
97-
jclass nodeDynamicProxyClass = env->FindClass("node/NodeDynamicProxyClass");
98-
if(env->IsInstanceOf(m_obj, nodeDynamicProxyClass)) {
99-
jfieldID ptrField = env->GetFieldID(nodeDynamicProxyClass, "ptr", "J");
100-
DynamicProxyData* proxyData = (DynamicProxyData*)(long)env->GetLongField(m_obj, ptrField);
101-
if(dynamicProxyDataVerify(proxyData)) {
102-
proxyData->markerStart = 0;
103-
proxyData->markerEnd = 0;
104-
delete proxyData;
105-
}
106-
}
107-
10895
env->DeleteGlobalRef(m_obj);
10996
env->DeleteGlobalRef(m_class);
11097
}
@@ -245,3 +232,59 @@ NAN_SETTER(JavaObject::fieldSetter) {
245232
return;
246233
}
247234
}
235+
236+
/*static*/ v8::Persistent<v8::FunctionTemplate> JavaProxyObject::s_proxyCt;
237+
238+
/*static*/ void JavaProxyObject::init() {
239+
v8::Local<v8::FunctionTemplate> t = NanNew<v8::FunctionTemplate>();
240+
NanAssignPersistent(s_proxyCt, t);
241+
t->InstanceTemplate()->SetInternalFieldCount(1);
242+
t->SetClassName(NanNew<v8::String>("NodeDynamicProxy"));
243+
244+
v8::Handle<v8::String> methodName = NanNew<v8::String>("unref");
245+
v8::Local<v8::FunctionTemplate> methodCallTemplate = NanNew<v8::FunctionTemplate>(doUnref);
246+
t->PrototypeTemplate()->Set(methodName, methodCallTemplate->GetFunction());
247+
248+
v8::Handle<v8::String> fieldName = NanNew<v8::String>("invocationHandler");
249+
t->InstanceTemplate()->SetAccessor(fieldName, invocationHandlerGetter);
250+
}
251+
252+
v8::Local<v8::Object> JavaProxyObject::New(Java *java, jobject obj, DynamicProxyData* dynamicProxyData) {
253+
NanEscapableScope();
254+
255+
v8::Local<v8::Function> ctor = s_proxyCt->GetFunction();
256+
v8::Local<v8::Object> javaObjectObj = ctor->NewInstance();
257+
javaObjectObj->SetHiddenValue(NanNew<v8::String>(V8_HIDDEN_MARKER_JAVA_OBJECT), NanNew<v8::Boolean>(true));
258+
JavaProxyObject *self = new JavaProxyObject(java, obj, dynamicProxyData);
259+
self->Wrap(javaObjectObj);
260+
261+
return NanEscapeScope(javaObjectObj);
262+
}
263+
264+
JavaProxyObject::JavaProxyObject(Java *java, jobject obj, DynamicProxyData* dynamicProxyData) : JavaObject(java, obj) {
265+
m_dynamicProxyData = dynamicProxyData;
266+
}
267+
268+
JavaProxyObject::~JavaProxyObject() {
269+
if(dynamicProxyDataVerify(m_dynamicProxyData)) {
270+
unref(m_dynamicProxyData);
271+
}
272+
}
273+
274+
NAN_METHOD(JavaProxyObject::doUnref) {
275+
JavaProxyObject* self = node::ObjectWrap::Unwrap<JavaProxyObject>(args.This());
276+
if (dynamicProxyDataVerify(self->m_dynamicProxyData)) {
277+
unref(self->m_dynamicProxyData);
278+
}
279+
NanReturnUndefined();
280+
}
281+
282+
NAN_GETTER(JavaProxyObject::invocationHandlerGetter) {
283+
NanScope();
284+
285+
JavaProxyObject* self = node::ObjectWrap::Unwrap<JavaProxyObject>(args.This());
286+
if (!dynamicProxyDataVerify(self->m_dynamicProxyData)) {
287+
return NanThrowError("dynamicProxyData has been destroyed or corrupted");
288+
}
289+
NanReturnValue(self->m_dynamicProxyData->functions);
290+
}

src/javaObject.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@ class JavaObject : public node::ObjectWrap {
1515
public:
1616
static void Init(v8::Handle<v8::Object> target);
1717
static v8::Local<v8::Object> New(Java* java, jobject obj);
18+
static v8::Local<v8::Object> NewProxy(Java* java, jobject obj, DynamicProxyData* dynamicProxyData);
1819

1920
jobject getObject() { return m_obj; }
2021
jclass getClass() { return m_class; }
2122

2223
void Ref() { node::ObjectWrap::Ref(); }
2324
void Unref() { node::ObjectWrap::Unref(); }
2425

25-
private:
26+
protected:
2627
JavaObject(Java* java, jobject obj);
2728
~JavaObject();
29+
30+
private:
2831
static NAN_METHOD(methodCall);
2932
static NAN_METHOD(methodCallSync);
3033
static NAN_GETTER(fieldGetter);
@@ -36,4 +39,20 @@ class JavaObject : public node::ObjectWrap {
3639
jclass m_class;
3740
};
3841

42+
class JavaProxyObject : public JavaObject {
43+
public:
44+
static void init();
45+
static v8::Local<v8::Object> New(Java* java, jobject obj, DynamicProxyData* dynamicProxyData);
46+
47+
private:
48+
JavaProxyObject(Java* java, jobject obj, DynamicProxyData* dynamicProxyData);
49+
~JavaProxyObject();
50+
static NAN_METHOD(doUnref);
51+
static NAN_GETTER(invocationHandlerGetter);
52+
53+
static v8::Persistent<v8::FunctionTemplate> s_proxyCt;
54+
DynamicProxyData* m_dynamicProxyData;
55+
};
56+
3957
#endif
58+

src/utils.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
#include "utils.h"
32
#include <string.h>
43
#include <algorithm>
@@ -498,6 +497,10 @@ v8::Handle<v8::Value> javaArrayToV8(Java* java, JNIEnv* env, jobjectArray objArr
498497
}
499498

500499
v8::Handle<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj) {
500+
return javaToV8(java, env, obj, NULL);
501+
}
502+
503+
v8::Handle<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj, DynamicProxyData* dynamicProxyData) {
501504
if(obj == NULL) {
502505
return NanNull();
503506
}
@@ -579,6 +582,9 @@ v8::Handle<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj) {
579582
case TYPE_STRING:
580583
return NanNew<v8::String>(javaObjectToString(env, obj).c_str());
581584
case TYPE_OBJECT:
585+
if (dynamicProxyData != NULL) {
586+
return JavaProxyObject::New(java, obj, dynamicProxyData);
587+
}
582588
return JavaObject::New(java, obj);
583589
default:
584590
printf("javaToV8: unhandled type: 0x%03x\n", resultType);
@@ -729,3 +735,14 @@ std::string methodNotFoundToString(JNIEnv *env, jclass clazz, std::string method
729735

730736
return msg.str();
731737
}
738+
739+
void unref(DynamicProxyData* dynamicProxyData) {
740+
if(!dynamicProxyDataVerify(dynamicProxyData)) {
741+
return;
742+
}
743+
NanDisposePersistent(dynamicProxyData->jsObject);
744+
NanDisposePersistent(dynamicProxyData->functions);
745+
dynamicProxyData->markerStart = 0;
746+
dynamicProxyData->markerEnd = 0;
747+
delete dynamicProxyData;
748+
}

src/utils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ std::string javaExceptionToString(JNIEnv* env, jthrowable ex);
6969
void checkJavaException(JNIEnv* env);
7070
v8::Handle<v8::Value> javaArrayToV8(Java* java, JNIEnv* env, jobjectArray objArray);
7171
v8::Handle<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj);
72+
v8::Handle<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj, DynamicProxyData* dynamicProxyData);
7273
jobjectArray javaObjectArrayToClasses(JNIEnv *env, jobjectArray objs);
7374
jobject longToJavaLongObj(JNIEnv *env, jlong l);
7475

@@ -80,6 +81,8 @@ void javaCastArguments(JNIEnv *env, jobjectArray methodArgs, jobject method);
8081

8182
std::string methodNotFoundToString(JNIEnv *env, jclass clazz, std::string methodName, bool constructor, _NAN_METHOD_ARGS_TYPE args, int argStart, int argEnd);
8283

84+
void unref(DynamicProxyData* dynamicProxyData);
85+
8386
#define UNUSED_VARIABLE(var) var = var;
8487

8588
#define ARGS_FRONT_OBJECT(ARGNAME) \

test/dynamicProxy-test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,40 @@ exports['Dynamic Proxy'] = nodeunit.testCase({
241241
test.ok(/Caused by: node\.NodeJsException: Error: newError/.test(e.message));
242242
}
243243

244+
test.done();
245+
},
246+
247+
"invocationHandler": function (test) {
248+
var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', {
249+
run: function (i) {
250+
return i + 2;
251+
}
252+
});
253+
254+
var result = myProxy.invocationHandler.run(42);
255+
256+
test.equals(result, 44);
257+
258+
test.done();
259+
},
260+
261+
"unref": function (test) {
262+
var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', {
263+
run: function (i) {
264+
return i + 1;
265+
}
266+
});
267+
268+
myProxy.unref();
269+
270+
try {
271+
myProxy.invocationHandler.run(42);
272+
} catch (e) {
273+
test.equals(e.message, "dynamicProxyData has been destroyed or corrupted");
274+
}
275+
276+
// call again
277+
myProxy.unref();
244278
test.done();
245279
}
246280
});

testHelpers.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
var java = require("./");
33
java.options.push("-Djava.awt.headless=true");
4+
//java.options.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005');
5+
46
java.classpath.push("test/");
57
java.classpath.push("test/commons-lang3-3.1.jar");
68

0 commit comments

Comments
 (0)