Skip to content

Commit b6fdecf

Browse files
committed
Merge branch 'master' of github.com:nearinfinity/node-java
2 parents a2a40f7 + 6b2341c commit b6fdecf

10 files changed

Lines changed: 153 additions & 83 deletions

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,27 @@ var byteArray = java.newArray(
6060
.map(function(c) { return java.newByte(str.charCodeAt(c)); });
6161
```
6262
63+
### Using java.lang.Long and long
64+
65+
JavaScript only supports 32-bit integers. Because of this java longs must be treated specially.
66+
When getting a long result the value may be truncated. If you need the original value there is
67+
a property off of the result called "longValue" which contains the un-truncated value as a string.
68+
If you are calling a method that takes a long you must create it using [java.newInstance](#javaNewInstance).
69+
70+
```javascript
71+
var javaLong = java.newInstanceSync("java.lang.Long", 5);
72+
console.log('Possibly truncated long value: ' + javaLong);
73+
console.log('Original long value (as a string): ' + javaLong.longValue);
74+
java.callStaticMethodSync("Test", "staticMethodThatTakesALong", javaLong);
75+
```
76+
6377
# Release Notes
6478
6579
### v0.2.0
6680
67-
* java.lang.Long and long primitives are no longer auto converted to JavaScript numbers due to the lack of
68-
precision \([See Issue #37](https://github.com/nearinfinity/node-java/issues/37)\) and
69-
lack of autoboxing support from Apache Commons Lang MethodUtils#getMatchingAccessibleMethod \([See Issue #40](https://github.com/nearinfinity/node-java/issues/40)\).
81+
* java.lang.Long and long primitives are handled better. See
82+
\([Issue #37](https://github.com/nearinfinity/node-java/issues/37)\) and
83+
\([Issue #40](https://github.com/nearinfinity/node-java/issues/40)\).
7084
7185
# Index
7286

binding.gyp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
'conditions': [
55
['target_arch=="ia32"', {
66
'arch%': "i386"
7+
}],
8+
['OS=="win"', {
9+
'javahome%': "<!(echo %JAVA_HOME%)"
10+
}],
11+
['OS=="linux" or OS=="mac"', {
12+
'javahome%': "<!(echo $JAVA_HOME)"
713
}]
814
]
915
},
@@ -19,7 +25,7 @@
1925
"src/utils.cpp"
2026
],
2127
"include_dirs": [
22-
"$(JAVA_HOME)/include",
28+
"<(javahome)/include",
2329
],
2430
'conditions': [
2531
['OS=="win"',
@@ -28,20 +34,20 @@
2834
{
2935
'action_name': 'verifyDeps',
3036
'inputs': [
31-
'$(JAVA_HOME)/lib/jvm.lib',
32-
'$(JAVA_HOME)/include/jni.h',
33-
'$(JAVA_HOME)/include/win32/jni_md.h'
37+
'<(javahome)/lib/jvm.lib',
38+
'<(javahome)/include/jni.h',
39+
'<(javahome)/include/win32/jni_md.h'
3440
],
3541
'outputs': ['./build/depsVerified'],
3642
'action': ['python', 'touch.py'],
3743
'message': 'Verify Deps'
3844
}
3945
],
4046
"include_dirs": [
41-
"$(JAVA_HOME)/include/win32",
47+
"<(javahome)/include/win32",
4248
],
4349
"libraries": [
44-
"-l$(JAVA_HOME)/lib/jvm.lib"
50+
"-l<(javahome)/lib/jvm.lib"
4551
]
4652
}
4753
],
@@ -51,21 +57,21 @@
5157
{
5258
'action_name': 'verifyDeps',
5359
'inputs': [
54-
'$(JAVA_HOME)/jre/lib/<(arch)/server/libjvm.so',
55-
'$(JAVA_HOME)/include/jni.h',
56-
'$(JAVA_HOME)/include/linux/jni_md.h'
60+
'<(javahome)/jre/lib/<(arch)/server/libjvm.so',
61+
'<(javahome)/include/jni.h',
62+
'<(javahome)/include/linux/jni_md.h'
5763
],
5864
'outputs': ['./build/depsVerified'],
5965
'action': [],
6066
'message': 'Verify Deps'
6167
}
6268
],
6369
"include_dirs": [
64-
"$(JAVA_HOME)/include/linux",
70+
"<(javahome)/include/linux",
6571
],
6672
"libraries": [
67-
"-L$(JAVA_HOME)/jre/lib/<(arch)/server/",
68-
"-Wl,-rpath,$(JAVA_HOME)/jre/lib/<(arch)/server/",
73+
"-L<(javahome)/jre/lib/<(arch)/server/",
74+
"-Wl,-rpath,<(javahome)/jre/lib/<(arch)/server/",
6975
"-ljvm"
7076
]
7177
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"jvm",
88
"bridge"
99
],
10-
"version": "0.2.0",
10+
"version": "0.2.2",
1111
"engines": {
1212
"node": ">=0.6.0"
1313
},

src/java.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "javaScope.h"
1010
#include "methodCallBaton.h"
1111
#include "node_NodeDynamicProxyClass.h"
12+
#include <node_version.h>
1213
#include <sstream>
1314

1415
std::string nativeBindingLocation;
@@ -650,7 +651,11 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
650651
void EIO_CallJs(uv_work_t* req) {
651652
}
652653

654+
#if NODE_MINOR_VERSION >= 10
655+
void EIO_AfterCallJs(uv_work_t* req, int status) {
656+
#else
653657
void EIO_AfterCallJs(uv_work_t* req) {
658+
#endif
654659
DynamicProxyData* dynamicProxyData = static_cast<DynamicProxyData*>(req->data);
655660
if(!dynamicProxyDataVerify(dynamicProxyData)) {
656661
return;
@@ -726,7 +731,11 @@ JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jo
726731
uv_work_t* req = new uv_work_t();
727732
req->data = dynamicProxyData;
728733
if(myThreadId == v8ThreadId) {
734+
#if NODE_MINOR_VERSION >= 10
735+
EIO_AfterCallJs(req, 0);
736+
#else
729737
EIO_AfterCallJs(req);
738+
#endif
730739
} else {
731740
uv_queue_work(uv_default_loop(), req, EIO_CallJs, EIO_AfterCallJs);
732741

src/javaObject.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373

7474
v8::Local<v8::Function> ctor = persistentFuncTemplate->GetFunction();
7575
v8::Local<v8::Object> javaObjectObj = ctor->NewInstance();
76-
javaObjectObj->SetHiddenValue(v8::String::New("__isJavaObject"), v8::Boolean::New(true));
76+
javaObjectObj->SetHiddenValue(v8::String::New(V8_HIDDEN_MARKER_JAVA_OBJECT), v8::Boolean::New(true));
7777
JavaObject *self = new JavaObject(java, obj);
7878
self->Wrap(javaObjectObj);
7979

src/methodCallBaton.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ v8::Handle<v8::Value> MethodCallBaton::runSync() {
5050
javaDetachCurrentThread(self->m_java->getJvm());
5151
}
5252

53+
#if NODE_MINOR_VERSION >= 10
54+
/*static*/ void MethodCallBaton::EIO_AfterMethodCall(uv_work_t* req, int status) {
55+
#else
5356
/*static*/ void MethodCallBaton::EIO_AfterMethodCall(uv_work_t* req) {
57+
#endif
5458
MethodCallBaton* self = static_cast<MethodCallBaton*>(req->data);
5559
JNIEnv *env = self->m_java->getJavaEnv();
5660
self->after(env);
@@ -78,7 +82,6 @@ v8::Handle<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) {
7882

7983
if(m_error) {
8084
v8::Handle<v8::Value> err = javaExceptionToV8(env, m_error, m_errorString);
81-
env->DeleteGlobalRef(m_error);
8285
return scope.Close(err);
8386
}
8487

@@ -92,9 +95,8 @@ void NewInstanceBaton::execute(JNIEnv *env) {
9295
//printf("invoke: %s\n", javaMethodCallToString(env, m_method, constructor_newInstance, m_args).c_str());
9396

9497
jobject result = env->CallObjectMethod(m_method, constructor_newInstance, m_args);
95-
jthrowable err = env->ExceptionOccurred();
96-
if(err) {
97-
m_error = (jthrowable)env->NewGlobalRef(err);
98+
if(env->ExceptionCheck()) {
99+
m_error = env->ExceptionOccurred();
98100
m_errorString = "Error creating class";
99101
env->ExceptionClear();
100102
return;
@@ -117,9 +119,8 @@ void StaticMethodCallBaton::execute(JNIEnv *env) {
117119

118120
jobject result = env->CallObjectMethod(m_method, method_invoke, NULL, m_args);
119121

120-
jthrowable err = env->ExceptionOccurred();
121-
if(err) {
122-
m_error = (jthrowable)env->NewGlobalRef(err);
122+
if(env->ExceptionCheck()) {
123+
m_error = env->ExceptionOccurred();
123124
m_errorString = "Error running static method";
124125
env->ExceptionClear();
125126
return;
@@ -142,9 +143,8 @@ void InstanceMethodCallBaton::execute(JNIEnv *env) {
142143

143144
jobject result = env->CallObjectMethod(m_method, method_invoke, m_javaObject->getObject(), m_args);
144145

145-
jthrowable err = env->ExceptionOccurred();
146-
if(err) {
147-
m_error = (jthrowable)env->NewGlobalRef(err);
146+
if(env->ExceptionCheck()) {
147+
m_error = env->ExceptionOccurred();
148148
m_errorString = "Error running instance method";
149149
env->ExceptionClear();
150150
return;

src/methodCallBaton.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "utils.h"
66
#include <v8.h>
77
#include <node.h>
8+
#include <node_version.h>
89
#include <jni.h>
910
#include <list>
1011

@@ -17,7 +18,11 @@ class MethodCallBaton {
1718
virtual ~MethodCallBaton();
1819

1920
static void EIO_MethodCall(uv_work_t* req);
21+
#if NODE_MINOR_VERSION >= 10
22+
static void EIO_AfterMethodCall(uv_work_t* req, int status);
23+
#else
2024
static void EIO_AfterMethodCall(uv_work_t* req);
25+
#endif
2126
void run();
2227
v8::Handle<v8::Value> runSync();
2328

src/utils.cpp

Lines changed: 80 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
#define MODIFIER_STATIC 9
1010

11+
jobject v8ToJava_javaObject(JNIEnv* env, v8::Local<v8::Object> obj);
12+
jobject v8ToJava_javaLong(JNIEnv* env, v8::Local<v8::Object> obj);
13+
1114
void javaReflectionGetMethods(JNIEnv *env, jclass clazz, std::list<jobject>* methods, bool includeStatic) {
1215
jclass clazzclazz = env->FindClass("java/lang/Class");
1316
jmethodID clazz_getMethods = env->GetMethodID(clazzclazz, "getMethods", "()[Ljava/lang/reflect/Method;");
@@ -267,55 +270,15 @@ jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg) {
267270

268271
if(arg->IsObject()) {
269272
v8::Local<v8::Object> obj = v8::Object::Cast(*arg);
270-
v8::Local<v8::Value> isJavaObject = obj->GetHiddenValue(v8::String::New("__isJavaObject"));
273+
274+
v8::Local<v8::Value> isJavaObject = obj->GetHiddenValue(v8::String::New(V8_HIDDEN_MARKER_JAVA_OBJECT));
271275
if(!isJavaObject.IsEmpty() && isJavaObject->IsBoolean()) {
272-
JavaObject* javaObject = node::ObjectWrap::Unwrap<JavaObject>(obj);
273-
jobject jobj = javaObject->getObject();
274-
275-
jclass nodeDynamicProxyClass = env->FindClass("node/NodeDynamicProxyClass");
276-
if(env->IsInstanceOf(jobj, nodeDynamicProxyClass)) {
277-
jfieldID ptrField = env->GetFieldID(nodeDynamicProxyClass, "ptr", "J");
278-
DynamicProxyData* proxyData = (DynamicProxyData*)(long)env->GetLongField(jobj, ptrField);
279-
if(!dynamicProxyDataVerify(proxyData)) {
280-
return NULL;
281-
}
282-
283-
jclass dynamicInterface = javaFindClass(env, proxyData->interfaceName);
284-
if(dynamicInterface == NULL) {
285-
printf("Could not find interface %s\n", proxyData->interfaceName.c_str());
286-
return NULL;
287-
}
288-
jclass classClazz = env->FindClass("java/lang/Class");
289-
jobjectArray classArray = env->NewObjectArray(1, classClazz, NULL);
290-
env->SetObjectArrayElement(classArray, 0, dynamicInterface);
291-
292-
jmethodID class_getClassLoader = env->GetMethodID(classClazz, "getClassLoader", "()Ljava/lang/ClassLoader;");
293-
jobject classLoader = env->CallObjectMethod(dynamicInterface, class_getClassLoader);
294-
if(classLoader == NULL) {
295-
jclass objectClazz = env->FindClass("java/lang/Object");
296-
jmethodID object_getClass = env->GetMethodID(objectClazz, "getClass", "()Ljava/lang/Class;");
297-
jobject jobjClass = env->CallObjectMethod(jobj, object_getClass);
298-
classLoader = env->CallObjectMethod(jobjClass, class_getClassLoader);
299-
}
300-
301-
jclass proxyClass = env->FindClass("java/lang/reflect/Proxy");
302-
jmethodID proxy_newProxyInstance = env->GetStaticMethodID(proxyClass, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;");
303-
if(classLoader == NULL) {
304-
printf("Could not get classloader for Proxy\n");
305-
return NULL;
306-
}
307-
if(classArray == NULL) {
308-
printf("Could not create class array for Proxy\n");
309-
return NULL;
310-
}
311-
if(jobj == NULL) {
312-
printf("Not a valid object to wrap\n");
313-
return NULL;
314-
}
315-
jobj = env->CallStaticObjectMethod(proxyClass, proxy_newProxyInstance, classLoader, classArray, jobj);
316-
}
276+
return v8ToJava_javaObject(env, obj);
277+
}
317278

318-
return jobj;
279+
v8::Local<v8::Value> isJavaLong = obj->GetHiddenValue(v8::String::New(V8_HIDDEN_MARKER_JAVA_LONG));
280+
if(!isJavaLong.IsEmpty() && isJavaLong->IsBoolean()) {
281+
return v8ToJava_javaLong(env, obj);
319282
}
320283
}
321284

@@ -325,6 +288,64 @@ jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg) {
325288
return NULL;
326289
}
327290

291+
jobject v8ToJava_javaObject(JNIEnv* env, v8::Local<v8::Object> obj) {
292+
JavaObject* javaObject = node::ObjectWrap::Unwrap<JavaObject>(obj);
293+
jobject jobj = javaObject->getObject();
294+
295+
jclass nodeDynamicProxyClass = env->FindClass("node/NodeDynamicProxyClass");
296+
if(env->IsInstanceOf(jobj, nodeDynamicProxyClass)) {
297+
jfieldID ptrField = env->GetFieldID(nodeDynamicProxyClass, "ptr", "J");
298+
DynamicProxyData* proxyData = (DynamicProxyData*)(long)env->GetLongField(jobj, ptrField);
299+
if(!dynamicProxyDataVerify(proxyData)) {
300+
return NULL;
301+
}
302+
303+
jclass dynamicInterface = javaFindClass(env, proxyData->interfaceName);
304+
if(dynamicInterface == NULL) {
305+
printf("Could not find interface %s\n", proxyData->interfaceName.c_str());
306+
return NULL;
307+
}
308+
jclass classClazz = env->FindClass("java/lang/Class");
309+
jobjectArray classArray = env->NewObjectArray(1, classClazz, NULL);
310+
env->SetObjectArrayElement(classArray, 0, dynamicInterface);
311+
312+
jmethodID class_getClassLoader = env->GetMethodID(classClazz, "getClassLoader", "()Ljava/lang/ClassLoader;");
313+
jobject classLoader = env->CallObjectMethod(dynamicInterface, class_getClassLoader);
314+
if(classLoader == NULL) {
315+
jclass objectClazz = env->FindClass("java/lang/Object");
316+
jmethodID object_getClass = env->GetMethodID(objectClazz, "getClass", "()Ljava/lang/Class;");
317+
jobject jobjClass = env->CallObjectMethod(jobj, object_getClass);
318+
classLoader = env->CallObjectMethod(jobjClass, class_getClassLoader);
319+
}
320+
321+
jclass proxyClass = env->FindClass("java/lang/reflect/Proxy");
322+
jmethodID proxy_newProxyInstance = env->GetStaticMethodID(proxyClass, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;");
323+
if(classLoader == NULL) {
324+
printf("Could not get classloader for Proxy\n");
325+
return NULL;
326+
}
327+
if(classArray == NULL) {
328+
printf("Could not create class array for Proxy\n");
329+
return NULL;
330+
}
331+
if(jobj == NULL) {
332+
printf("Not a valid object to wrap\n");
333+
return NULL;
334+
}
335+
jobj = env->CallStaticObjectMethod(proxyClass, proxy_newProxyInstance, classLoader, classArray, jobj);
336+
}
337+
338+
return jobj;
339+
}
340+
341+
jobject v8ToJava_javaLong(JNIEnv* env, v8::Local<v8::Object> obj) {
342+
jobject longValue = v8ToJava(env, obj->Get(v8::String::New("longValue")));
343+
jclass longClazz = env->FindClass("java/lang/Long");
344+
jmethodID long_constructor = env->GetMethodID(longClazz, "<init>", "(Ljava/lang/String;)V");
345+
jobject jobj = env->NewObject(longClazz, long_constructor, longValue);
346+
return jobj;
347+
}
348+
328349
jobjectArray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end) {
329350
jclass clazz = env->FindClass("java/lang/Object");
330351
jobjectArray results = env->NewObjectArray(end-start, clazz, NULL);
@@ -509,14 +530,18 @@ v8::Handle<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj) {
509530
jbyte result = env->CallByteMethod(obj, byte_byteValue);
510531
return scope.Close(v8::Number::New(result));
511532
}
512-
// Removed to support long return types and long parameters.
513-
// case TYPE_LONG:
514-
// {
515-
// jclass longClazz = env->FindClass("java/lang/Long");
516-
// jmethodID long_longValue = env->GetMethodID(longClazz, "longValue", "()J");
517-
// jlong result = env->CallLongMethod(obj, long_longValue);
518-
// return scope.Close(v8::Number::New(result));
519-
// }
533+
case TYPE_LONG:
534+
{
535+
jclass longClazz = env->FindClass("java/lang/Long");
536+
jmethodID long_longValue = env->GetMethodID(longClazz, "longValue", "()J");
537+
jlong result = env->CallLongMethod(obj, long_longValue);
538+
std::string strValue = javaObjectToString(env, obj);
539+
v8::Local<v8::Value> v8Result = v8::NumberObject::New(result);
540+
v8::NumberObject* v8ResultNumberObject = v8::NumberObject::Cast(*v8Result);
541+
v8ResultNumberObject->Set(v8::String::New("longValue"), v8::String::New(strValue.c_str()));
542+
v8ResultNumberObject->SetHiddenValue(v8::String::New(V8_HIDDEN_MARKER_JAVA_LONG), v8::Boolean::New(true));
543+
return scope.Close(v8Result);
544+
}
520545
case TYPE_INT:
521546
{
522547
jclass integerClazz = env->FindClass("java/lang/Integer");

0 commit comments

Comments
 (0)