#include "java.h" #include #include "javaObject.h" #include "methodCallBaton.h" #include /*static*/ v8::Persistent Java::s_ct; /*static*/ void Java::Init(v8::Handle target) { v8::HandleScope scope; v8::Local t = v8::FunctionTemplate::New(New); s_ct = v8::Persistent::New(t); s_ct->InstanceTemplate()->SetInternalFieldCount(1); s_ct->SetClassName(v8::String::NewSymbol("Java")); NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstance", newInstance); NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstanceSync", newInstanceSync); NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethod", callStaticMethod); NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethodSync", callStaticMethodSync); 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()); } /*static*/ v8::Handle Java::New(const v8::Arguments& args) { v8::HandleScope scope; Java *self = new Java(); self->Wrap(args.This()); self->handle_->Set(v8::String::New("classpath"), v8::Array::New()); return args.This(); } Java::Java() { this->m_jvm = NULL; this->m_env = NULL; } Java::~Java() { } v8::Handle Java::ensureJvm() { if(!m_jvm) { return createJVM(&this->m_jvm, &this->m_env); } return v8::Undefined(); } v8::Handle Java::createJVM(JavaVM** jvm, JNIEnv** env) { JavaVM* jvmTemp; JavaVMInitArgs args; std::ostringstream classPath; classPath << "-Djava.class.path="; v8::Local classPathValue = handle_->Get(v8::String::New("classpath")); if(!classPathValue->IsArray()) { return ThrowException(v8::Exception::TypeError(v8::String::New("Classpath must be an array"))); } v8::Local classPathArray = v8::Array::Cast(*classPathValue); for(uint32_t i=0; iLength(); i++) { if(i != 0) { #ifdef WIN32 classPath << ";"; #else classPath << ":"; #endif } v8::Local arrayItemValue = classPathArray->Get(i); if(!arrayItemValue->IsString()) { return ThrowException(v8::Exception::TypeError(v8::String::New("Classpath must only contain strings"))); } v8::Local arrayItem = arrayItemValue->ToString(); v8::String::AsciiValue arrayItemStr(arrayItem); classPath << *arrayItemStr; } JavaVMOption options[1]; options[0].optionString = strdup(classPath.str().c_str()); JNI_GetDefaultJavaVMInitArgs(&args); args.version = JNI_VERSION_1_6; args.ignoreUnrecognized = false; args.options = options; args.nOptions = 1; JNI_CreateJavaVM(&jvmTemp, (void **)env, &args); *jvm = jvmTemp; return v8::Undefined(); } /*static*/ v8::Handle Java::newInstance(const v8::Arguments& args) { v8::HandleScope scope; Java* self = node::ObjectWrap::Unwrap(args.This()); v8::Handle ensureJvmResults = self->ensureJvm(); if(!ensureJvmResults->IsUndefined()) { return ensureJvmResults; } JNIEnv* env = self->getJavaEnv(); bool callbackProvided; int argsEnd = args.Length(); // 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 classNameObj = v8::Local::Cast(args[0]); v8::String::AsciiValue classNameVal(classNameObj); std::string className = *classNameVal; // argument - callback v8::Handle callback; if(args[args.Length()-1]->IsFunction()) { callback = args[argsEnd-1]; argsEnd--; callbackProvided = true; } else { callback = v8::Null(); callbackProvided = false; } jobjectArray methodArgs = v8ToJava(env, args, 1, argsEnd); jclass clazz = javaFindClass(env, className); if(clazz == NULL) { std::ostringstream errStr; errStr << "Could not create class " << className.c_str(); v8::Handle error = javaExceptionToV8(env, errStr.str()); v8::Handle argv[2]; argv[0] = error; argv[1] = v8::Undefined(); v8::Function::Cast(*callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv); return v8::Undefined(); } jobject method = javaFindConstructor(env, clazz, methodArgs); if(method == NULL) { std::ostringstream errStr; errStr << "Could not find constructor"; v8::Handle error = javaExceptionToV8(env, errStr.str()); v8::Handle argv[2]; argv[0] = error; argv[1] = v8::Undefined(); v8::Function::Cast(*callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv); return v8::Undefined(); } // run NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback); baton->run(); if(callbackProvided) { return v8::Undefined(); } else { std::ostringstream str; str << "\"Constructor for class '" << className << "' called without a callback did you mean to use the Sync version?\""; return scope.Close(v8::String::New(str.str().c_str())); } } /*static*/ v8::Handle Java::newInstanceSync(const v8::Arguments& args) { v8::HandleScope scope; Java* self = node::ObjectWrap::Unwrap(args.This()); v8::Handle ensureJvmResults = self->ensureJvm(); if(!ensureJvmResults->IsUndefined()) { return ensureJvmResults; } JNIEnv* env = self->getJavaEnv(); int argsEnd = args.Length(); int argsStart = 0; // 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 classNameObj = v8::Local::Cast(args[0]); v8::String::AsciiValue classNameVal(classNameObj); std::string className = *classNameVal; argsStart++; jobjectArray methodArgs = v8ToJava(env, args, argsStart, argsEnd); 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())); } jobject method = javaFindConstructor(env, clazz, methodArgs); if(method == NULL) { std::ostringstream errStr; errStr << "Could not find constructor"; return ThrowException(javaExceptionToV8(env, errStr.str())); } // run v8::Handle callback = v8::Object::New(); NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback); v8::Handle result = baton->runSync(); delete baton; if(result->IsNativeError()) { return ThrowException(result); } return scope.Close(result); } /*static*/ v8::Handle Java::callStaticMethod(const v8::Arguments& args) { v8::HandleScope scope; Java* self = node::ObjectWrap::Unwrap(args.This()); v8::Handle ensureJvmResults = self->ensureJvm(); if(!ensureJvmResults->IsUndefined()) { return ensureJvmResults; } JNIEnv* env = self->getJavaEnv(); bool callbackProvided; int argsEnd = args.Length(); // 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 classNameObj = v8::Local::Cast(args[0]); v8::String::AsciiValue classNameVal(classNameObj); std::string className = *classNameVal; // argument - method name if(args.Length() < 2 || !args[1]->IsString()) { return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 1 must be a string"))); } v8::Local methodNameObj = v8::Local::Cast(args[1]); v8::String::AsciiValue methodNameVal(methodNameObj); std::string methodName = *methodNameVal; // argument - callback v8::Handle callback; if(args[args.Length()-1]->IsFunction()) { callback = args[argsEnd-1]; argsEnd--; callbackProvided = true; } else { callback = v8::Null(); callbackProvided = false; } // build args jobjectArray methodArgs = v8ToJava(env, args, 2, argsEnd); // find class and method jclass clazz = javaFindClass(env, className); if(clazz == NULL) { std::ostringstream errStr; errStr << "Could not create class " << className.c_str(); v8::Handle error = javaExceptionToV8(env, errStr.str()); v8::Handle argv[2]; argv[0] = error; argv[1] = v8::Undefined(); v8::Function::Cast(*callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv); return v8::Undefined(); } // find method jobject method = javaFindMethod(env, clazz, methodName, methodArgs); if(method == NULL) { std::ostringstream errStr; errStr << "Could not find method \"" << methodName.c_str() << "\""; v8::Handle error = javaExceptionToV8(env, errStr.str()); v8::Handle argv[2]; argv[0] = error; argv[1] = v8::Undefined(); v8::Function::Cast(*callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv); return v8::Undefined(); } // run StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback); baton->run(); if(callbackProvided) { return v8::Undefined(); } else { std::ostringstream str; str << "\"Static method '" << methodName << "' called without a callback did you mean to use the Sync version?\""; return scope.Close(v8::String::New(str.str().c_str())); } } /*static*/ v8::Handle Java::callStaticMethodSync(const v8::Arguments& args) { v8::HandleScope scope; Java* self = node::ObjectWrap::Unwrap(args.This()); v8::Handle ensureJvmResults = self->ensureJvm(); if(!ensureJvmResults->IsUndefined()) { return ensureJvmResults; } JNIEnv* env = self->getJavaEnv(); int argsEnd = args.Length(); // 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 classNameObj = v8::Local::Cast(args[0]); v8::String::AsciiValue classNameVal(classNameObj); std::string className = *classNameVal; // argument - method name if(args.Length() < 2 || !args[1]->IsString()) { return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 1 must be a string"))); } v8::Local methodNameObj = v8::Local::Cast(args[1]); v8::String::AsciiValue methodNameVal(methodNameObj); std::string methodName = *methodNameVal; // build args jobjectArray methodArgs = v8ToJava(env, args, 2, argsEnd); // find class and method 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())); } // find method jobject method = javaFindMethod(env, clazz, methodName, methodArgs); if(method == NULL) { std::ostringstream errStr; errStr << "Could not find method \"" << methodName.c_str() << "\""; return ThrowException(javaExceptionToV8(env, errStr.str())); } // run v8::Handle callback = v8::Object::New(); StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback); v8::Handle result = baton->runSync(); delete baton; if(result->IsNativeError()) { return ThrowException(result); } return scope.Close(result); } /*static*/ v8::Handle Java::newArray(const v8::Arguments& args) { v8::HandleScope scope; Java* self = node::ObjectWrap::Unwrap(args.This()); v8::Handle 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 classNameObj = v8::Local::Cast(args[0]); v8::String::AsciiValue classNameVal(classNameObj); std::string className = *classNameVal; // argument - array if(args.Length() < 2 || !args[1]->IsArray()) { return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 1 must be an array"))); } v8::Local arrayObj = v8::Local::Cast(args[1]); // find class and method jarray results; if(strcmp(className.c_str(), "byte") == 0) { results = env->NewByteArray(arrayObj->Length()); for(uint32_t i=0; iLength(); i++) { v8::Local item = arrayObj->Get(i); jobject val = v8ToJava(env, item); jclass byteClazz = env->FindClass("java/lang/Byte"); jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B"); jbyte byteValues[1]; byteValues[0] = env->CallByteMethod(val, byte_byteValue); env->SetByteArrayRegion((jbyteArray)results, i, 1, byteValues); } } else { 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())); } // create array results = env->NewObjectArray(arrayObj->Length(), clazz, NULL); for(uint32_t i=0; iLength(); i++) { v8::Local item = arrayObj->Get(i); jobject val = v8ToJava(env, item); env->SetObjectArrayElement((jobjectArray)results, i, val); if(env->ExceptionOccurred()) { std::ostringstream errStr; v8::String::AsciiValue valStr(item); errStr << "Could not add item \"" << *valStr << "\" to array."; return ThrowException(javaExceptionToV8(env, errStr.str())); } } } return scope.Close(JavaObject::New(self, results)); } /*static*/ v8::Handle Java::newByte(const v8::Arguments& args) { v8::HandleScope scope; Java* self = node::ObjectWrap::Unwrap(args.This()); v8::Handle ensureJvmResults = self->ensureJvm(); if(!ensureJvmResults->IsUndefined()) { return ensureJvmResults; } JNIEnv* env = self->getJavaEnv(); if(args.Length() != 1) { return ThrowException(v8::Exception::TypeError(v8::String::New("newByte only takes 1 argument"))); } // argument - value if(!args[0]->IsNumber()) { return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a number"))); } v8::Local val = args[0]->ToNumber(); jclass clazz = env->FindClass("java/lang/Byte"); jmethodID constructor = env->GetMethodID(clazz, "", "(B)V"); jobject newObj = env->NewObject(clazz, constructor, (jbyte)val->Value()); return scope.Close(JavaObject::New(self, newObj)); } /*static*/ v8::Handle Java::getStaticFieldValue(const v8::Arguments& args) { v8::HandleScope scope; Java* self = node::ObjectWrap::Unwrap(args.This()); v8::Handle 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 classNameObj = v8::Local::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 fieldNameObj = v8::Local::Cast(args[1]); v8::String::AsciiValue fieldNameVal(fieldNameObj); std::string fieldName = *fieldNameVal; // 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_get = env->GetMethodID(fieldClazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); // get field value jobject val = env->CallObjectMethod(field, field_get, NULL); if(env->ExceptionOccurred()) { std::ostringstream errStr; errStr << "Could not get field " << fieldName.c_str() << " on class " << className.c_str(); return ThrowException(javaExceptionToV8(env, errStr.str())); } return scope.Close(javaToV8(self, env, val)); } /*static*/ v8::Handle Java::setStaticFieldValue(const v8::Arguments& args) { v8::HandleScope scope; Java* self = node::ObjectWrap::Unwrap(args.This()); v8::Handle 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 classNameObj = v8::Local::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 fieldNameObj = v8::Local::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"))); } jobject newValue = v8ToJava(env, args[2]); // 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"); //printf("newValue: %s\n", javaObjectToString(env, newValue).c_str()); // set field value env->CallObjectMethod(field, field_set, NULL, newValue); if(env->ExceptionOccurred()) { std::ostringstream errStr; errStr << "Could not set field " << fieldName.c_str() << " on class " << className.c_str(); return ThrowException(javaExceptionToV8(env, errStr.str())); } return v8::Undefined(); }