Skip to content

Commit 148411e

Browse files
committed
added support for passing objects and creating byte arrays
1 parent 463345c commit 148411e

6 files changed

Lines changed: 158 additions & 13 deletions

File tree

src/java.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstanceSync", newInstanceSync);
2020
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethod", callStaticMethod);
2121
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethodSync", callStaticMethodSync);
22+
NODE_SET_PROTOTYPE_METHOD(s_ct, "newArray", newArray);
23+
NODE_SET_PROTOTYPE_METHOD(s_ct, "newByte", newByte);
2224

2325
target->Set(v8::String::NewSymbol("Java"), s_ct->GetFunction());
2426
}
@@ -179,6 +181,9 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
179181
NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
180182
v8::Handle<v8::Value> result = baton->runSync();
181183
delete baton;
184+
if(result->IsNativeError()) {
185+
return ThrowException(result);
186+
}
182187
return scope.Close(result);
183188
}
184189

@@ -307,5 +312,103 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
307312
StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback);
308313
v8::Handle<v8::Value> result = baton->runSync();
309314
delete baton;
315+
if(result->IsNativeError()) {
316+
return ThrowException(result);
317+
}
310318
return scope.Close(result);
311319
}
320+
321+
/*static*/ v8::Handle<v8::Value> Java::newArray(const v8::Arguments& args) {
322+
v8::HandleScope scope;
323+
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
324+
v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
325+
if(!ensureJvmResults->IsUndefined()) {
326+
return ensureJvmResults;
327+
}
328+
JNIEnv* env = self->getJavaEnv();
329+
330+
// argument - className
331+
if(args.Length() < 1 || !args[0]->IsString()) {
332+
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a string")));
333+
}
334+
v8::Local<v8::String> classNameObj = v8::Local<v8::String>::Cast(args[0]);
335+
v8::String::AsciiValue classNameVal(classNameObj);
336+
std::string className = *classNameVal;
337+
338+
// argument - array
339+
if(args.Length() < 2 || !args[1]->IsArray()) {
340+
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 1 must be an array")));
341+
}
342+
v8::Local<v8::Array> arrayObj = v8::Local<v8::Array>::Cast(args[1]);
343+
344+
// find class and method
345+
jarray results;
346+
if(strcmp(className.c_str(), "byte") == 0) {
347+
results = env->NewByteArray(arrayObj->Length());
348+
for(uint32_t i=0; i<arrayObj->Length(); i++) {
349+
int methodArgType;
350+
v8::Local<v8::Value> item = arrayObj->Get(i);
351+
jobject val = v8ToJava(env, item, &methodArgType);
352+
jclass byteClazz = env->FindClass("java/lang/Byte");
353+
jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B");
354+
jbyte byteValues[1];
355+
byteValues[0] = env->CallByteMethod(val, byte_byteValue);
356+
env->SetByteArrayRegion((jbyteArray)results, i, 1, byteValues);
357+
}
358+
}
359+
360+
else
361+
{
362+
jclass clazz = javaFindClass(env, className);
363+
if(clazz == NULL) {
364+
std::ostringstream errStr;
365+
errStr << "Could not create class " << className.c_str();
366+
return ThrowException(javaExceptionToV8(env, errStr.str()));
367+
}
368+
369+
// create array
370+
results = env->NewObjectArray(arrayObj->Length(), clazz, NULL);
371+
372+
for(uint32_t i=0; i<arrayObj->Length(); i++) {
373+
int methodArgType;
374+
v8::Local<v8::Value> item = arrayObj->Get(i);
375+
jobject val = v8ToJava(env, item, &methodArgType);
376+
env->SetObjectArrayElement((jobjectArray)results, i, val);
377+
if(env->ExceptionOccurred()) {
378+
std::ostringstream errStr;
379+
v8::String::AsciiValue valStr(item);
380+
errStr << "Could not add item \"" << *valStr << "\" to array.";
381+
return ThrowException(javaExceptionToV8(env, errStr.str()));
382+
}
383+
}
384+
}
385+
386+
return scope.Close(JavaObject::New(self, results));
387+
}
388+
389+
/*static*/ v8::Handle<v8::Value> Java::newByte(const v8::Arguments& args) {
390+
v8::HandleScope scope;
391+
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
392+
v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
393+
if(!ensureJvmResults->IsUndefined()) {
394+
return ensureJvmResults;
395+
}
396+
JNIEnv* env = self->getJavaEnv();
397+
398+
if(args.Length() != 1) {
399+
return ThrowException(v8::Exception::TypeError(v8::String::New("newByte only takes 1 argument")));
400+
}
401+
402+
// argument - value
403+
if(!args[0]->IsNumber()) {
404+
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a number")));
405+
}
406+
407+
v8::Local<v8::Number> val = args[0]->ToNumber();
408+
409+
jclass clazz = env->FindClass("java/lang/Byte");
410+
jmethodID constructor = env->GetMethodID(clazz, "<init>", "(B)V");
411+
jobject newObj = env->NewObject(clazz, constructor, (jbyte)val->Value());
412+
413+
return scope.Close(JavaObject::New(self, newObj));
414+
}

src/java.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class Java : public node::ObjectWrap {
2323
static v8::Handle<v8::Value> newInstanceSync(const v8::Arguments& args);
2424
static v8::Handle<v8::Value> callStaticMethod(const v8::Arguments& args);
2525
static v8::Handle<v8::Value> callStaticMethodSync(const v8::Arguments& args);
26+
static v8::Handle<v8::Value> newArray(const v8::Arguments& args);
27+
static v8::Handle<v8::Value> newByte(const v8::Arguments& args);
2628
v8::Handle<v8::Value> ensureJvm();
2729

2830
static v8::Persistent<v8::FunctionTemplate> s_ct;

src/methodCallBaton.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ v8::Handle<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) {
7676
bool result = env->CallBooleanMethod(m_result, boolean_booleanValue);
7777
return scope.Close(v8::Boolean::New(result));
7878
}
79+
case TYPE_BYTE:
80+
{
81+
jclass byteClazz = env->FindClass("java/lang/Byte");
82+
jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B");
83+
jbyte result = env->CallByteMethod(m_result, byte_byteValue);
84+
return scope.Close(v8::Number::New(result));
85+
}
7986
case TYPE_LONG:
8087
{
8188
jclass longClazz = env->FindClass("java/lang/Long");
@@ -106,8 +113,7 @@ void NewInstanceBaton::execute(JNIEnv *env) {
106113
m_resultType = TYPE_OBJECT;
107114
m_result = env->NewGlobalRef(result);
108115
if(env->ExceptionCheck()) {
109-
env->ExceptionDescribe(); // TODO: handle error
110-
return;
116+
m_error = v8::Persistent<v8::Value>::New(javaExceptionToV8(env, "Error running method"));
111117
}
112118
}
113119

@@ -137,8 +143,7 @@ void InstanceMethodCallBaton::execute(JNIEnv *env) {
137143
jobject result = env->CallObjectMethod(m_method, method_invoke, m_javaObject->getObject(), m_args);
138144
m_result = env->NewGlobalRef(result);
139145
if(env->ExceptionCheck()) {
140-
env->ExceptionDescribe(); // TODO: handle error
141-
return;
146+
m_error = v8::Persistent<v8::Value>::New(javaExceptionToV8(env, "Error running method"));
142147
}
143148
}
144149

src/utils.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "utils.h"
33
#include <string.h>
44
#include <algorithm>
5+
#include "javaObject.h"
56

67
std::list<jobject> javaReflectionGetMethods(JNIEnv *env, jclass clazz) {
78
std::list<jobject> results;
@@ -132,6 +133,8 @@ jvalueType javaGetType(JNIEnv *env, jclass type) {
132133
return TYPE_VOID;
133134
} else if(strcmp(typeStr, "boolean") == 0) {
134135
return TYPE_BOOLEAN;
136+
} else if(strcmp(typeStr, "byte") == 0) {
137+
return TYPE_BYTE;
135138
} else if(strcmp(typeStr, "class java.lang.String") == 0) {
136139
return TYPE_STRING;
137140
}
@@ -156,11 +159,21 @@ jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg, int *methodArgType) {
156159
jclass clazz = env->FindClass("java/lang/Integer");
157160
jmethodID constructor = env->GetMethodID(clazz, "<init>", "(I)V");
158161
return env->NewObject(clazz, constructor, val);
159-
} else {
160-
// TODO: handle other arg types
161-
*methodArgType = TYPE_OBJECT;
162-
return NULL;
162+
} else if(arg->IsObject()) {
163+
v8::Local<v8::Object> obj = v8::Object::Cast(*arg);
164+
v8::String::AsciiValue constructorName(obj->GetConstructorName());
165+
if(strcmp(*constructorName, "JavaObject") == 0) {
166+
JavaObject* javaObject = node::ObjectWrap::Unwrap<JavaObject>(obj);
167+
*methodArgType = TYPE_OBJECT;
168+
return javaObject->getObject();
169+
}
163170
}
171+
172+
// TODO: handle other arg types
173+
v8::String::AsciiValue typeStr(arg);
174+
printf("Unhandled type: %s\n", *typeStr);
175+
*methodArgType = TYPE_OBJECT;
176+
return NULL;
164177
}
165178

166179
jarray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end, std::list<int> *methodArgTypes) {
@@ -185,8 +198,7 @@ v8::Handle<v8::Value> javaExceptionToV8(JNIEnv* env, const std::string& alternat
185198
printf("BEGIN Java Exception -------\n");
186199
env->ExceptionDescribe(); // TODO: handle error
187200
printf("END Java Exception ---------\n");
188-
return v8::Exception::TypeError(v8::String::New("java exception"));
189-
} else {
190-
return v8::Exception::TypeError(v8::String::New(alternateMessage.c_str()));
201+
// TODO: convert error to v8 error
191202
}
203+
return v8::Exception::TypeError(v8::String::New(alternateMessage.c_str()));
192204
}

src/utils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ typedef enum _jvalueType {
1313
TYPE_LONG = 3,
1414
TYPE_OBJECT = 4,
1515
TYPE_STRING = 5,
16-
TYPE_BOOLEAN = 6
16+
TYPE_BOOLEAN = 6,
17+
TYPE_BYTE = 7
1718
} jvalueType;
1819

1920
std::list<jobject> javaReflectionGetMethods(JNIEnv *env, jclass clazz);

test/simple-test.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ exports['Simple'] = nodeunit.testCase({
6767
list.addSync("hello");
6868
list.addSync("world");
6969
test.equal(list.sizeSync(), 2);
70-
console.log("before get");
7170
var item0 = list.getSync(0);
7271
test.equal(item0.toStringSync(), "hello");
7372
var clazz = list.getClassSync();
@@ -88,5 +87,28 @@ exports['Simple'] = nodeunit.testCase({
8887
});
8988
}
9089
});
90+
},
91+
92+
"passing objects to methods": function(test) {
93+
var data = java.newArray("byte", toAsciiArray("hello world\n"));
94+
//console.log("data", data.toStringSync());
95+
var stream = java.newInstanceSync("java.io.ByteArrayInputStream", data);
96+
//console.log("stream", stream);
97+
var reader = java.newInstanceSync("java.io.InputStreamReader", stream);
98+
//console.log("reader", reader);
99+
var bufferedReader = java.newInstanceSync("java.io.BufferedReader", reader);
100+
var str = bufferedReader.readLineSync();
101+
console.log("bufferedReader.readLineSync", str);
102+
test.equal(str, "hello world");
103+
test.done();
91104
}
92105
});
106+
107+
function toAsciiArray(str) {
108+
var results = [];
109+
for(var i=0; i<str.length; i++) {
110+
var b = java.newByte(str.charCodeAt(i));
111+
results.push(b);
112+
}
113+
return results;
114+
}

0 commit comments

Comments
 (0)