Skip to content

Commit

Permalink
added set static field
Browse files Browse the repository at this point in the history
added unit tests for static fields
  • Loading branch information
joeferner committed Jan 22, 2012
1 parent 78f857b commit 191cbc9
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 58 deletions.
78 changes: 70 additions & 8 deletions src/java.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
NODE_SET_PROTOTYPE_METHOD(s_ct, "newArray", newArray);
NODE_SET_PROTOTYPE_METHOD(s_ct, "newByte", newByte);
NODE_SET_PROTOTYPE_METHOD(s_ct, "getStaticFieldValue", getStaticFieldValue);
NODE_SET_PROTOTYPE_METHOD(s_ct, "setStaticFieldValue", setStaticFieldValue);

target->Set(v8::String::NewSymbol("Java"), s_ct->GetFunction());
}
Expand Down Expand Up @@ -121,7 +122,7 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
callback = v8::Null();
}

std::list<int> methodArgTypes;
std::list<jvalueType> methodArgTypes;
jarray methodArgs = v8ToJava(env, args, 1, argsEnd, &methodArgTypes);

jclass clazz = javaFindClass(env, className);
Expand Down Expand Up @@ -179,7 +180,7 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
std::string className = *classNameVal;
argsStart++;

std::list<int> methodArgTypes;
std::list<jvalueType> methodArgTypes;
jarray methodArgs = v8ToJava(env, args, argsStart, argsEnd, &methodArgTypes);

jclass clazz = javaFindClass(env, className);
Expand Down Expand Up @@ -244,7 +245,7 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
}

// build args
std::list<int> methodArgTypes;
std::list<jvalueType> methodArgTypes;
jarray methodArgs = v8ToJava(env, args, 2, argsEnd, &methodArgTypes);

// find class and method
Expand Down Expand Up @@ -309,7 +310,7 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
std::string methodName = *methodNameVal;

// build args
std::list<int> methodArgTypes;
std::list<jvalueType> methodArgTypes;
jarray methodArgs = v8ToJava(env, args, 2, argsEnd, &methodArgTypes);

// find class and method
Expand Down Expand Up @@ -366,7 +367,7 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
if(strcmp(className.c_str(), "byte") == 0) {
results = env->NewByteArray(arrayObj->Length());
for(uint32_t i=0; i<arrayObj->Length(); i++) {
int methodArgType;
jvalueType methodArgType;
v8::Local<v8::Value> item = arrayObj->Get(i);
jobject val = v8ToJava(env, item, &methodArgType);
jclass byteClazz = env->FindClass("java/lang/Byte");
Expand All @@ -390,7 +391,7 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
results = env->NewObjectArray(arrayObj->Length(), clazz, NULL);

for(uint32_t i=0; i<arrayObj->Length(); i++) {
int methodArgType;
jvalueType methodArgType;
v8::Local<v8::Value> item = arrayObj->Get(i);
jobject val = v8ToJava(env, item, &methodArgType);
env->SetObjectArrayElement((jobjectArray)results, i, val);
Expand Down Expand Up @@ -474,11 +475,72 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
return ThrowException(javaExceptionToV8(env, errStr.str()));
}

// get field value
jclass fieldClazz = env->FindClass("java/lang/reflect/Field");
jmethodID field_get = env->GetMethodID(fieldClazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
jmethodID field_getType = env->GetMethodID(fieldClazz, "getType", "()Ljava/lang/Class;");

// get field type
jclass fieldTypeClazz = (jclass)env->CallObjectMethod(field, field_getType);
jvalueType resultType = javaGetType(env, fieldTypeClazz);

// get field value
jobject val = env->CallObjectMethod(field, field_get, NULL);

return scope.Close(JavaObject::New(self, val));
return scope.Close(javaToV8(self, env, resultType, val));
}

/*static*/ v8::Handle<v8::Value> Java::setStaticFieldValue(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();

// argument - className
if(args.Length() < 1 || !args[0]->IsString()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a string")));
}
v8::Local<v8::String> classNameObj = v8::Local<v8::String>::Cast(args[0]);
v8::String::AsciiValue classNameVal(classNameObj);
std::string className = *classNameVal;

// argument - field name
if(args.Length() < 2 || !args[1]->IsString()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 1 must be a string")));
}
v8::Local<v8::String> fieldNameObj = v8::Local<v8::String>::Cast(args[1]);
v8::String::AsciiValue fieldNameVal(fieldNameObj);
std::string fieldName = *fieldNameVal;

// argument - new value
if(args.Length() < 3) {
return ThrowException(v8::Exception::TypeError(v8::String::New("setStaticFieldValue requires 3 arguments")));
}
jvalueType methodArgType;
jobject newValue = v8ToJava(env, args[2], &methodArgType);

// find the class
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
std::ostringstream errStr;
errStr << "Could not create class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}

// get the field
jobject field = javaFindField(env, clazz, fieldName);
if(field == NULL) {
std::ostringstream errStr;
errStr << "Could not find field " << fieldName.c_str() << " on class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}

jclass fieldClazz = env->FindClass("java/lang/reflect/Field");
jmethodID field_set = env->GetMethodID(fieldClazz, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V");

// set field value
env->CallObjectMethod(field, field_set, NULL, newValue);
return v8::Undefined();
}
1 change: 1 addition & 0 deletions src/java.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Java : public node::ObjectWrap {
static v8::Handle<v8::Value> newArray(const v8::Arguments& args);
static v8::Handle<v8::Value> newByte(const v8::Arguments& args);
static v8::Handle<v8::Value> getStaticFieldValue(const v8::Arguments& args);
static v8::Handle<v8::Value> setStaticFieldValue(const v8::Arguments& args);
v8::Handle<v8::Value> ensureJvm();

static v8::Persistent<v8::FunctionTemplate> s_ct;
Expand Down
4 changes: 2 additions & 2 deletions src/javaObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ JavaObject::~JavaObject() {
callback = v8::Null();
}

std::list<int> methodArgTypes;
std::list<jvalueType> methodArgTypes;
jarray methodArgs = v8ToJava(env, args, 0, argsEnd, &methodArgTypes);

jobject method = javaFindBestMatchingMethod(env, self->m_methods, *methodName, methodArgTypes);
Expand All @@ -95,7 +95,7 @@ JavaObject::~JavaObject() {

v8::String::AsciiValue methodName(args.Data());

std::list<int> methodArgTypes;
std::list<jvalueType> methodArgTypes;
jarray methodArgs = v8ToJava(env, args, 0, args.Length(), &methodArgTypes);

jobject method = javaFindBestMatchingMethod(env, self->m_methods, *methodName, methodArgTypes);
Expand Down
38 changes: 1 addition & 37 deletions src/methodCallBaton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,43 +76,7 @@ v8::Handle<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) {
return scope.Close(err);
}

switch(m_resultType) {
case TYPE_VOID:
return v8::Undefined();
case TYPE_BOOLEAN:
{
jclass booleanClazz = env->FindClass("java/lang/Boolean");
jmethodID boolean_booleanValue = env->GetMethodID(booleanClazz, "booleanValue", "()Z");
bool result = env->CallBooleanMethod(m_result, boolean_booleanValue);
return scope.Close(v8::Boolean::New(result));
}
case TYPE_BYTE:
{
jclass byteClazz = env->FindClass("java/lang/Byte");
jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B");
jbyte result = env->CallByteMethod(m_result, byte_byteValue);
return scope.Close(v8::Number::New(result));
}
case TYPE_LONG:
{
jclass longClazz = env->FindClass("java/lang/Long");
jmethodID long_longValue = env->GetMethodID(longClazz, "longValue", "()J");
jlong result = env->CallLongMethod(m_result, long_longValue);
return scope.Close(v8::Number::New(result));
}
case TYPE_INT:
{
jclass integerClazz = env->FindClass("java/lang/Integer");
jmethodID integer_intValue = env->GetMethodID(integerClazz, "intValue", "()I");
jint result = env->CallIntMethod(m_result, integer_intValue);
return scope.Close(v8::Integer::New(result));
}
case TYPE_OBJECT:
return scope.Close(JavaObject::New(m_java, m_result));
case TYPE_STRING:
return scope.Close(v8::String::New(javaObjectToString(env, m_result).c_str()));
}
return v8::Undefined();
return scope.Close(javaToV8(m_java, env, m_resultType, m_result));
}

void NewInstanceBaton::execute(JNIEnv *env) {
Expand Down
2 changes: 1 addition & 1 deletion src/methodCallBaton.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class MethodCallBaton {
jarray m_args;
jobject m_result;
jobject m_method;
int m_resultType;
jvalueType m_resultType;
};

class InstanceMethodCallBaton : public MethodCallBaton {
Expand Down
53 changes: 48 additions & 5 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <algorithm>
#include <sstream>
#include "javaObject.h"
#include "java.h"

std::list<jobject> javaReflectionGetMethods(JNIEnv *env, jclass clazz) {
std::list<jobject> results;
Expand Down Expand Up @@ -73,7 +74,7 @@ jobject javaFindBestMatchingMethod(
JNIEnv *env,
std::list<jobject>& methods,
const char *methodName,
std::list<int>& argTypes) {
std::list<jvalueType>& argTypes) {

std::list<jobject> possibleMatches;
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
Expand Down Expand Up @@ -112,7 +113,7 @@ jobject javaFindBestMatchingMethod(
jobject javaFindBestMatchingConstructor(
JNIEnv *env,
std::list<jobject>& constructors,
std::list<int>& argTypes) {
std::list<jvalueType>& argTypes) {

std::list<jobject> possibleMatches;
jclass constructorClazz = env->FindClass("java/lang/reflect/Constructor");
Expand Down Expand Up @@ -204,7 +205,7 @@ jobject javaFindField(JNIEnv* env, jclass clazz, std::string fieldName) {
return NULL;
}

jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg, int *methodArgType) {
jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg, jvalueType *methodArgType) {
if(arg->IsNull()) {
return NULL;
}
Expand Down Expand Up @@ -248,12 +249,12 @@ jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg, int *methodArgType) {
return NULL;
}

jarray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end, std::list<int> *methodArgTypes) {
jarray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end, std::list<jvalueType> *methodArgTypes) {
jclass clazz = env->FindClass("java/lang/Object");
jobjectArray results = env->NewObjectArray(end-start, clazz, NULL);

for(int i=start; i<end; i++) {
int methodArgType;
jvalueType methodArgType;
jobject val = v8ToJava(env, args[i], &methodArgType);
env->SetObjectArrayElement(results, i - start, val);
if(methodArgTypes) {
Expand Down Expand Up @@ -298,3 +299,45 @@ v8::Handle<v8::Value> javaExceptionToV8(JNIEnv* env, const std::string& alternat
jthrowable ex = env->ExceptionOccurred();
return scope.Close(javaExceptionToV8(env, ex, alternateMessage));
}

v8::Handle<v8::Value> javaToV8(Java* java, JNIEnv* env, jvalueType resultType, jobject obj) {
v8::HandleScope scope;

switch(resultType) {
case TYPE_VOID:
return v8::Undefined();
case TYPE_BOOLEAN:
{
jclass booleanClazz = env->FindClass("java/lang/Boolean");
jmethodID boolean_booleanValue = env->GetMethodID(booleanClazz, "booleanValue", "()Z");
bool result = env->CallBooleanMethod(obj, boolean_booleanValue);
return scope.Close(v8::Boolean::New(result));
}
case TYPE_BYTE:
{
jclass byteClazz = env->FindClass("java/lang/Byte");
jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B");
jbyte result = env->CallByteMethod(obj, byte_byteValue);
return scope.Close(v8::Number::New(result));
}
case TYPE_LONG:
{
jclass longClazz = env->FindClass("java/lang/Long");
jmethodID long_longValue = env->GetMethodID(longClazz, "longValue", "()J");
jlong result = env->CallLongMethod(obj, long_longValue);
return scope.Close(v8::Number::New(result));
}
case TYPE_INT:
{
jclass integerClazz = env->FindClass("java/lang/Integer");
jmethodID integer_intValue = env->GetMethodID(integerClazz, "intValue", "()I");
jint result = env->CallIntMethod(obj, integer_intValue);
return scope.Close(v8::Integer::New(result));
}
case TYPE_OBJECT:
return scope.Close(JavaObject::New(java, obj));
case TYPE_STRING:
return scope.Close(v8::String::New(javaObjectToString(env, obj).c_str()));
}
return v8::Undefined();
}
11 changes: 7 additions & 4 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <list>
#include <string>

class Java;

typedef enum _jvalueType {
TYPE_VOID = 1,
TYPE_INT = 2,
Expand All @@ -26,19 +28,20 @@ jobject javaFindBestMatchingMethod(
JNIEnv *env,
std::list<jobject>& methods,
const char *methodName,
std::list<int>& argTypes);
std::list<jvalueType>& argTypes);
jobject javaFindBestMatchingConstructor(
JNIEnv *env,
std::list<jobject>& constructors,
std::list<int>& argTypes);
std::list<jvalueType>& argTypes);
JNIEnv* javaAttachCurrentThread(JavaVM* jvm);
void javaDetachCurrentThread(JavaVM* jvm);
jvalueType javaGetType(JNIEnv *env, jclass type);
jclass javaFindClass(JNIEnv* env, std::string className);
jobject javaFindField(JNIEnv* env, jclass clazz, std::string fieldName);
jarray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end, std::list<int> *methodArgTypes);
jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg, int *methodArgType);
jarray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end, std::list<jvalueType> *methodArgTypes);
jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg, jvalueType *methodArgType);
v8::Handle<v8::Value> javaExceptionToV8(JNIEnv* env, const std::string& alternateMessage);
v8::Handle<v8::Value> javaExceptionToV8(JNIEnv* env, jthrowable ex, const std::string& alternateMessage);
v8::Handle<v8::Value> javaToV8(Java* java, JNIEnv* env, jvalueType resultType, jobject obj);

#endif
Binary file modified test/Test.class
Binary file not shown.
3 changes: 2 additions & 1 deletion test/Test.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

public class Test {
private int i;
private int i;
public static int staticFieldInt = 42;

public Test() {}
public Test(int i) { this.i = i; }
Expand Down
20 changes: 20 additions & 0 deletions test/java-staticField-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

var java = require("./testHelpers").java;

var nodeunit = require("nodeunit");
var util = require("util");

exports['Java - Static Field'] = nodeunit.testCase({
"getStaticFieldValue int": function(test) {
var val = java.getStaticFieldValue("Test", "staticFieldInt");
test.equal(val, 42);
test.done();
},

"setStaticFieldValue int": function(test) {
java.setStaticFieldValue("Test", "staticFieldInt", 112);
var val = java.getStaticFieldValue("Test", "staticFieldInt");
test.equal(val, 112);
test.done();
},
});

0 comments on commit 191cbc9

Please sign in to comment.