Skip to content

Commit 4b3eec3

Browse files
committed
provide access to length and elements of a node-java wrapped java array
1 parent 2b51e9d commit 4b3eec3

6 files changed

Lines changed: 86 additions & 23 deletions

File tree

src/java.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ NAN_METHOD(Java::newProxy) {
492492

493493
jmethodID class_getClassLoader = env->GetMethodID(classClazz, "getClassLoader", "()Ljava/lang/ClassLoader;");
494494
jobject classLoader = env->CallObjectMethod(dynamicInterface, class_getClassLoader);
495-
assert(!env->ExceptionCheck());
495+
assertNoException(env);
496496

497497
if(classLoader == NULL) {
498498
jclass objectClazz = env->FindClass("java/lang/Object");
@@ -757,7 +757,7 @@ NAN_METHOD(Java::newArray) {
757757
jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B");
758758
jbyte byteValues[1];
759759
byteValues[0] = env->CallByteMethod(val, byte_byteValue);
760-
assert(!env->ExceptionCheck());
760+
assertNoException(env);
761761
env->SetByteArrayRegion((jbyteArray)results, i, 1, byteValues);
762762
}
763763
}
@@ -785,7 +785,7 @@ NAN_METHOD(Java::newArray) {
785785
jmethodID short_shortValue = env->GetMethodID(shortClazz, "shortValue", "()S");
786786
jshort shortValues[1];
787787
shortValues[0] = env->CallShortMethod(val, short_shortValue);
788-
assert(!env->ExceptionCheck());
788+
assertNoException(env);
789789
env->SetShortArrayRegion((jshortArray)results, i, 1, shortValues);
790790
}
791791
}
@@ -799,7 +799,7 @@ NAN_METHOD(Java::newArray) {
799799
jmethodID double_doubleValue = env->GetMethodID(doubleClazz, "doubleValue", "()D");
800800
jdouble doubleValues[1];
801801
doubleValues[0] = env->CallDoubleMethod(val, double_doubleValue);
802-
assert(!env->ExceptionCheck());
802+
assertNoException(env);
803803
env->SetDoubleArrayRegion((jdoubleArray)results, i, 1, doubleValues);
804804
}
805805
}
@@ -813,7 +813,7 @@ NAN_METHOD(Java::newArray) {
813813
jmethodID integer_intValue = env->GetMethodID(integerClazz, "intValue", "()I");
814814
jint intValues[1];
815815
intValues[0] = env->CallIntMethod(val, integer_intValue);
816-
assert(!env->ExceptionCheck());
816+
assertNoException(env);
817817
env->SetIntArrayRegion((jintArray)results, i, 1, intValues);
818818
}
819819
}
@@ -1079,7 +1079,7 @@ NAN_METHOD(Java::getStaticFieldValue) {
10791079
jobject field = javaFindField(env, clazz, fieldName);
10801080
if(field == NULL) {
10811081
std::ostringstream errStr;
1082-
errStr << "Could not find field " << fieldName.c_str() << " on class " << className.c_str();
1082+
errStr << "Could not find field \"" << fieldName.c_str() << "\" on class \"" << className.c_str() << "\"";
10831083
return Nan::ThrowError(javaExceptionToV8(self, env, errStr.str()));
10841084
}
10851085

@@ -1137,7 +1137,7 @@ NAN_METHOD(Java::setStaticFieldValue) {
11371137
jobject field = javaFindField(env, clazz, fieldName);
11381138
if(field == NULL) {
11391139
std::ostringstream errStr;
1140-
errStr << "Could not find field " << fieldName.c_str() << " on class " << className.c_str();
1140+
errStr << "Could not find field \"" << fieldName.c_str() << "\" on class \"" << className.c_str() << "\"";
11411141
Nan::ThrowError(javaExceptionToV8(self, env, errStr.str()));
11421142
return;
11431143
}
@@ -1315,7 +1315,7 @@ JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jo
13151315
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
13161316
jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;");
13171317
dynamicProxyData->methodName = javaObjectToString(env, env->CallObjectMethod(method, method_getName));
1318-
assert(!env->ExceptionCheck());
1318+
assertNoException(env);
13191319

13201320
uv_work_t* req = new uv_work_t();
13211321
req->data = dynamicProxyData;

src/javaObject.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@
4545
funcTemplate->InstanceTemplate()->SetInternalFieldCount(1);
4646
funcTemplate->SetClassName(Nan::New<v8::String>(className.c_str()).ToLocalChecked());
4747

48+
// copy methods to template
4849
std::list<jobject> methods;
4950
javaReflectionGetMethods(env, objClazz, &methods, false);
5051
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
5152
jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;");
5253
for(std::list<jobject>::iterator it = methods.begin(); it != methods.end(); ++it) {
5354
jstring methodNameJava = (jstring)env->CallObjectMethod(*it, method_getName);
54-
assert(!env->ExceptionCheck());
55+
assertNoException(env);
5556
std::string methodNameStr = javaToString(env, methodNameJava);
5657

5758
v8::Local<v8::String> baseMethodName = Nan::New<v8::String>(methodNameStr.c_str()).ToLocalChecked();
@@ -78,6 +79,7 @@
7879
}
7980
}
8081

82+
// copy fields to template
8183
std::list<jobject> fields;
8284
javaReflectionGetFields(env, objClazz, &fields);
8385
jclass fieldClazz = env->FindClass("java/lang/reflect/Field");
@@ -91,6 +93,16 @@
9193
Nan::SetAccessor(funcTemplate->InstanceTemplate(), fieldName, fieldGetter, fieldSetter);
9294
}
9395

96+
// copy array methods to template
97+
jmethodID class_isArray = env->GetMethodID(classClazz, "isArray", "()Z");
98+
jboolean isArray = env->CallBooleanMethod(objClazz, class_isArray);
99+
if(isArray) {
100+
v8::Local<v8::String> fieldName = Nan::New<v8::String>("length").ToLocalChecked();
101+
Nan::SetAccessor(funcTemplate->InstanceTemplate(), fieldName, fieldGetter, NULL);
102+
103+
Nan::SetIndexedPropertyHandler(funcTemplate->InstanceTemplate(), indexGetter);
104+
}
105+
94106
Nan::Persistent<v8::FunctionTemplate>* persistentFuncTemplate = new Nan::Persistent<v8::FunctionTemplate>();
95107
persistentFuncTemplate->Reset(funcTemplate);
96108
sFunctionTemplates[className] = persistentFuncTemplate;
@@ -200,8 +212,22 @@ NAN_GETTER(JavaObject::fieldGetter) {
200212
std::string propertyStr = *propertyCStr;
201213
jobject field = javaFindField(env, self->m_class, propertyStr);
202214
if(field == NULL) {
215+
if(propertyStr == "length") {
216+
jclass classClazz = env->FindClass("java/lang/Class");
217+
jmethodID class_isArray = env->GetMethodID(classClazz, "isArray", "()Z");
218+
jboolean isArray = env->CallBooleanMethod(self->m_class, class_isArray);
219+
if(isArray) {
220+
jclass arrayClass = env->FindClass("java/lang/reflect/Array");
221+
jmethodID array_getLength = env->GetStaticMethodID(arrayClass, "getLength", "(Ljava/lang/Object;)I");
222+
jint arrayLength = env->CallStaticIntMethod(arrayClass, array_getLength, self->m_obj);
223+
assertNoException(env);
224+
info.GetReturnValue().Set(arrayLength);
225+
return;
226+
}
227+
}
228+
203229
std::ostringstream errStr;
204-
errStr << "Could not find field " << propertyStr;
230+
errStr << "Could not find field \"" << propertyStr << "\" for get";
205231
v8::Local<v8::Value> ex = javaExceptionToV8(self->m_java, env, errStr.str());
206232
Nan::ThrowError(ex);
207233
return;
@@ -238,7 +264,7 @@ NAN_SETTER(JavaObject::fieldSetter) {
238264
jobject field = javaFindField(env, self->m_class, propertyStr);
239265
if(field == NULL) {
240266
std::ostringstream errStr;
241-
errStr << "Could not find field " << propertyStr;
267+
errStr << "Could not find field \"" << propertyStr << "\" for set";
242268
v8::Local<v8::Value> error = javaExceptionToV8(self->m_java, env, errStr.str());
243269
Nan::ThrowError(error);
244270
return;
@@ -260,6 +286,29 @@ NAN_SETTER(JavaObject::fieldSetter) {
260286
}
261287
}
262288

289+
NAN_INDEX_GETTER(JavaObject::indexGetter) {
290+
Nan::HandleScope scope;
291+
JavaObject* self = Nan::ObjectWrap::Unwrap<JavaObject>(info.This());
292+
JNIEnv *env = self->m_java->getJavaEnv();
293+
JavaScope javaScope(env);
294+
295+
jclass arrayClass = env->FindClass("java/lang/reflect/Array");
296+
297+
jmethodID array_getLength = env->GetStaticMethodID(arrayClass, "getLength", "(Ljava/lang/Object;)I");
298+
jint arrayLength = env->CallStaticIntMethod(arrayClass, array_getLength, self->m_obj);
299+
assertNoException(env);
300+
if ((jint)index >= arrayLength) {
301+
info.GetReturnValue().SetUndefined();
302+
return;
303+
}
304+
305+
jmethodID array_get = env->GetStaticMethodID(arrayClass, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;");
306+
jobject item = env->CallStaticObjectMethod(arrayClass, array_get, self->m_obj, index);
307+
assertNoException(env);
308+
v8::Local<v8::Value> result = javaToV8(self->m_java, env, item);
309+
info.GetReturnValue().Set(result);
310+
}
311+
263312
/*static*/ Nan::Persistent<v8::FunctionTemplate> JavaProxyObject::s_proxyCt;
264313

265314
/*static*/ void JavaProxyObject::init() {

src/javaObject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class JavaObject : public Nan::ObjectWrap {
3232
static NAN_METHOD(methodCallSync);
3333
static NAN_GETTER(fieldGetter);
3434
static NAN_SETTER(fieldSetter);
35+
static NAN_INDEX_GETTER(indexGetter);
3536

3637
static std::map<std::string, Nan::Persistent<v8::FunctionTemplate>*> sFunctionTemplates;
3738
Java* m_java;

src/utils.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ void javaReflectionGetMethods(JNIEnv *env, jclass clazz, std::list<jobject>* met
2323
for(jsize i=0; i<methodCount; i++) {
2424
jobject method = env->GetObjectArrayElement(methodObjects, i);
2525
jint methodModifiers = env->CallIntMethod(method, method_getModifiers);
26-
assert(!env->ExceptionCheck());
26+
assertNoException(env);
2727
if(!includeStatic && (methodModifiers & MODIFIER_STATIC) == MODIFIER_STATIC) {
2828
continue;
2929
}
@@ -51,7 +51,7 @@ void javaReflectionGetFields(JNIEnv *env, jclass clazz, std::list<jobject>* fiel
5151
jmethodID field_getModifiers = env->GetMethodID(fieldClazz, "getModifiers", "()I");
5252

5353
jobjectArray fieldObjects = (jobjectArray)env->CallObjectMethod(clazz, clazz_getFields);
54-
assert(!env->ExceptionCheck());
54+
assertNoException(env);
5555
jsize fieldCount = env->GetArrayLength(fieldObjects);
5656
for(jsize i=0; i<fieldCount; i++) {
5757
jobject field = env->GetObjectArrayElement(fieldObjects, i);
@@ -109,7 +109,7 @@ std::string javaObjectToString(JNIEnv *env, jobject obj) {
109109
jclass objClazz = env->GetObjectClass(obj);
110110
jmethodID methodId = env->GetMethodID(objClazz, "toString", "()Ljava/lang/String;");
111111
jstring result = (jstring)env->CallObjectMethod(obj, methodId);
112-
assert(!env->ExceptionCheck());
112+
assertNoException(env);
113113
return javaToString(env, result);
114114
}
115115

@@ -155,7 +155,7 @@ JNIEnv* javaGetEnv(JavaVM* jvm, jobject classLoader) {
155155
jobject currentThread = env->CallStaticObjectMethod(threadClazz, thread_currentThread);
156156
checkJavaException(env);
157157
env->CallObjectMethod(currentThread, thread_setContextClassLoader, classLoader);
158-
assert(!env->ExceptionCheck());
158+
assertNoException(env);
159159

160160
env->DeleteLocalRef(threadClazz);
161161
env->DeleteLocalRef(currentThread);
@@ -180,7 +180,7 @@ jvalueType javaGetType(JNIEnv *env, jclass type) {
180180
jmethodID class_isArray = env->GetMethodID(clazzClazz, "isArray", "()Z");
181181

182182
jboolean isArray = env->CallBooleanMethod(type, class_isArray);
183-
assert(!env->ExceptionCheck());
183+
assertNoException(env);
184184
if(isArray) {
185185
return TYPE_ARRAY;
186186
} else {
@@ -467,7 +467,7 @@ jvalueType javaGetArrayComponentType(JNIEnv *env, jobjectArray array) {
467467

468468
jmethodID object_getClass = env->GetMethodID(objectClazz, "getClass", "()Ljava/lang/Class;");
469469
jobject arrayClass = env->CallObjectMethod(array, object_getClass);
470-
assert(!env->ExceptionCheck());
470+
assertNoException(env);
471471

472472
jmethodID class_getComponentType = env->GetMethodID(clazzclazz, "getComponentType", "()Ljava/lang/Class;");
473473
jobject arrayComponentTypeClass = env->CallObjectMethod(arrayClass, class_getComponentType);
@@ -623,7 +623,7 @@ v8::Local<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj, DynamicProxy
623623
jclass booleanClazz = env->FindClass("java/lang/Boolean");
624624
jmethodID boolean_booleanValue = env->GetMethodID(booleanClazz, "booleanValue", "()Z");
625625
bool result = env->CallBooleanMethod(obj, boolean_booleanValue);
626-
assert(!env->ExceptionCheck());
626+
assertNoException(env);
627627
return Nan::New<v8::Boolean>(result);
628628
}
629629
case TYPE_BYTE:
@@ -660,7 +660,7 @@ v8::Local<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj, DynamicProxy
660660
jclass shortClazz = env->FindClass("java/lang/Short");
661661
jmethodID short_shortValue = env->GetMethodID(shortClazz, "shortValue", "()S");
662662
jshort result = env->CallShortMethod(obj, short_shortValue);
663-
assert(!env->ExceptionCheck());
663+
assertNoException(env);
664664
return Nan::New<v8::Integer>(result);
665665
}
666666
case TYPE_DOUBLE:
@@ -676,7 +676,7 @@ v8::Local<v8::Value> javaToV8(Java* java, JNIEnv* env, jobject obj, DynamicProxy
676676
jclass floatClazz = env->FindClass("java/lang/Float");
677677
jmethodID float_floatValue = env->GetMethodID(floatClazz, "floatValue", "()F");
678678
jfloat result = env->CallFloatMethod(obj, float_floatValue);
679-
assert(!env->ExceptionCheck());
679+
assertNoException(env);
680680
return Nan::New<v8::Number>(result);
681681
}
682682
case TYPE_STRING:
@@ -761,7 +761,7 @@ jobject javaFindConstructor(JNIEnv *env, jclass clazz, jobjectArray methodArgs)
761761
jmethodID constructorUtils_getMatchingAccessibleConstructor = env->GetStaticMethodID(constructorUtilsClazz, "getMatchingAccessibleConstructor", "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;");
762762
jobjectArray methodArgClasses = javaObjectArrayToClasses(env, methodArgs);
763763
jobject method = env->CallStaticObjectMethod(constructorUtilsClazz, constructorUtils_getMatchingAccessibleConstructor, clazz, methodArgClasses);
764-
assert(!env->ExceptionCheck());
764+
assertNoException(env);
765765
return method;
766766
}
767767

@@ -822,7 +822,7 @@ std::string methodNotFoundToString(JNIEnv *env, jclass clazz, std::string method
822822
int count = 0;
823823
for(std::list<jobject>::iterator it = methods.begin(); it != methods.end(); ++it) {
824824
jstring methodNameTestJava = (jstring)env->CallObjectMethod(*it, member_getName);
825-
assert(!env->ExceptionCheck());
825+
assertNoException(env);
826826
std::string methodNameTest = javaToString(env, methodNameTestJava);
827827
if(methodNameTest == methodName) {
828828
msg << " " << javaObjectToString(env, *it).c_str() << "\n";

src/utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ jobject javaFindMethod(JNIEnv *env, jclass clazz, std::string& methodName, jobje
8282
jobject javaFindConstructor(JNIEnv *env, jclass clazz, jobjectArray methodArgs);
8383
void javaCastArguments(JNIEnv *env, jobjectArray methodArgs, jobject method);
8484

85+
#define assertNoException(env) \
86+
if (env->ExceptionCheck()) { \
87+
env->ExceptionDescribe(); \
88+
assert(false); \
89+
}
90+
8591
std::string methodNotFoundToString(JNIEnv *env, jclass clazz, std::string methodName, bool constructor, Nan::NAN_METHOD_ARGS_TYPE args, int argStart, int argEnd);
8692

8793
void unref(DynamicProxyData* dynamicProxyData);

test/simple-test.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,14 @@ exports['Simple'] = nodeunit.testCase({
319319
test.equal(r[1], false);
320320
test.done();
321321
},
322-
322+
"new byte array object": function(test) {
323+
var byteArray = java.newArray("byte", [1, 2, 3]);
324+
test.equal(byteArray.length, 3);
325+
test.equal(byteArray[0], 1);
326+
test.equal(byteArray[1], 2);
327+
test.equal(byteArray[2], 3);
328+
test.done();
329+
},
323330
"new boolean array": function(test) {
324331
var booleanArray = java.newArray("boolean", [true, false]);
325332
var r = java.callStaticMethodSync("Test", "staticBooleanArray", booleanArray);

0 commit comments

Comments
 (0)