diff options
Diffstat (limited to '')
-rw-r--r-- | sca-cpp/trunk/modules/java/eval.hpp | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/sca-cpp/trunk/modules/java/eval.hpp b/sca-cpp/trunk/modules/java/eval.hpp new file mode 100644 index 0000000000..b51a57337e --- /dev/null +++ b/sca-cpp/trunk/modules/java/eval.hpp @@ -0,0 +1,398 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_java_eval_hpp +#define tuscany_java_eval_hpp + +/** + * Java component implementation evaluation logic. + */ +#include <jni.h> + +#include "list.hpp" +#include "value.hpp" +#include "io.hpp" + +namespace tuscany { +namespace java { + +/** + * Initialize a JVM. + */ +jobject JNICALL nativeInvoke(JNIEnv *env, jobject self, jobject proxy, jobject method, jobjectArray args); + +class JavaRuntime { +public: + JavaRuntime() { + + // Create a JVM + JavaVMInitArgs args; + args.version = JNI_VERSION_1_6; + args.ignoreUnrecognized = JNI_FALSE; + JavaVMOption options[1]; + options[0].optionString = const_cast<char*>("-Djava.class.path=."); + args.options = options; + args.nOptions = 1; + JNI_CreateJavaVM(&jvm, (void**)&env, &args); + + // Capture JVM standard IO + setupIO(); + + // Lookup the system classes and methods we need + classClass = env->FindClass("java/lang/Class"); + methodClass = env->FindClass("java/lang/reflect/Method"); + objectClass = env->FindClass("java/lang/Object"); + doubleClass = env->FindClass("java/lang/Double"); + booleanClass = env->FindClass("java/lang/Boolean"); + stringClass = env->FindClass("java/lang/String"); + objectArrayClass = env->FindClass("[Ljava/lang/Object;"); + doubleValueOf = env->GetStaticMethodID(doubleClass, "valueOf", "(D)Ljava/lang/Double;"); + doubleValue = env->GetMethodID(doubleClass, "doubleValue", "()D"); + booleanValueOf = env->GetStaticMethodID(booleanClass, "valueOf", "(Z)Ljava/lang/Boolean;"); + booleanValue = env->GetMethodID(booleanClass, "booleanValue", "()Z"); + declaredMethods = env->GetMethodID(classClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); + methodName = env->GetMethodID(methodClass, "getName", "()Ljava/lang/String;"); + + // Register our native invocation handler native + invokerClass = env->FindClass("org/apache/tuscany/InvocationHandler"); + invokerValueOf = env->GetStaticMethodID(invokerClass, "valueOf", "(Ljava/lang/Class;J)Ljava/lang/Object;"); + invokerLambda = env->GetFieldID(invokerClass, "lambda", "J"); + JNINativeMethod nm; + nm.name = const_cast<char*>("invoke"); + nm.signature = const_cast<char*>("(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); + nm.fnPtr = (void*)nativeInvoke; + env->RegisterNatives(invokerClass, &nm, 1); + } + + ~JavaRuntime() { + if (jvm == NULL) + return; + jvm->DestroyJavaVM(); + } + + JavaVM* jvm; + JNIEnv* env; + + jclass classClass; + jclass methodClass; + jclass objectClass; + jclass doubleClass; + jclass booleanClass; + jclass stringClass; + jclass objectArrayClass; + jmethodID doubleValueOf; + jmethodID doubleValue; + jmethodID booleanValueOf; + jmethodID booleanValue; + jmethodID declaredMethods; + jmethodID methodName; + + jclass invokerClass; + jmethodID invokerValueOf; + jfieldID invokerLambda; + +} javaRuntime; + +/** + * Return the last exception that occurred in a JVM. + */ +string lastException(const JavaRuntime& jr) { + if (!jr.env->ExceptionCheck()) + return "No Exception"; + jr.env->ExceptionDescribe(); + jr.env->ExceptionClear(); + return "UnknownException"; +} + +/** + * Declare conversion functions. + */ +const jobject valueToJobject(const JavaRuntime& jr, const value& v); +const value jobjectToValue(const JavaRuntime& jr, const jobject o); +const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v); +const list<value> jarrayToValues(const JavaRuntime& jr, const jobjectArray o); + +/** + * Convert a Java class name to a JNI class name. + */ +const bool jniClassNameHelper(char* to, const char* from) { + if (*from == '\0') { + *to = '\0'; + return true; + } + *to = *from == '.'? '/' : *from; + return jniClassNameHelper(to + 1, from + 1); +} + +const string jniClassName(const string& from) { + char buf[length(from) + 1]; + jniClassNameHelper(buf, c_str(from)); + return string(buf); +} + +/** + * Create a new Java object representing a lambda expression. + */ +class javaLambda { +public: + javaLambda(const JavaRuntime& jr, const value& iface, const lambda<value(const list<value>&)>& func) : jr(jr), iface(iface), func(func) { + } + + const JavaRuntime& jr; + const value iface; + const lambda<value(const list<value>&)> func; +}; + +/** + * Invocation handler invoke method, dispatches to the lambda function wrapped + * in the invocation handler. + */ +jobject JNICALL nativeInvoke(JNIEnv* env, jobject self, unused jobject proxy, jobject method, jobjectArray args) { + + // Retrieve the lambda function from the invocation handler + jclass clazz = env->GetObjectClass(self); + jfieldID f = env->GetFieldID(clazz, "lambda", "J"); + const javaLambda& jl = *(javaLambda*)(long)env->GetLongField(self, f); + + // Retrieve the function name + const jstring s = (jstring)env->CallObjectMethod(method, jl.jr.methodName); + const char* c = env->GetStringUTFChars(s, NULL); + const value func(c); + env->ReleaseStringUTFChars(s, c); + + // Build the expression to evaluate + const list<value> expr = cons<value>(func, jarrayToValues(jl.jr, args)); + + // Invoke the lambda function + value result = jl.func(expr); + + // Convert result to a jobject + return valueToJobject(jl.jr, result); +} + +const jobject mkJavaLambda(const JavaRuntime& jr, const lambda<value(const list<value>&)>& l) { + + // The lambda function is given the opportunity to give us a + // Java interface name that it implements, and which will be + // used as the type of the Java proxy representing it. If the + // lambda function doesn't specify an interface, then the + // proxy implements javax.script.Invocable. + const value iface = l(mklist<value>("interface")); + const gc_ptr<javaLambda> jl = new (gc_new<javaLambda>()) javaLambda(jr, iface, l); + + jclass jc = jr.env->FindClass(c_str(jniClassName(string(iface)))); + const jobject obj = jr.env->CallStaticObjectMethod(jr.invokerClass, jr.invokerValueOf, jc, (long)(javaLambda*)jl); + return obj; +} + +/** + * Convert a list of values to a Java jobjectArray. + */ +const jobjectArray valuesToJarrayHelper(const JavaRuntime& jr, jobjectArray a, const list<value>& v, const int i) { + if (isNil(v)) + return a; + jr.env->SetObjectArrayElement(a, i, valueToJobject(jr, car(v))); + return valuesToJarrayHelper(jr, a, cdr(v), i + 1); +} + +const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v) { + jobjectArray a = jr.env->NewObjectArray(length(v), jr.objectClass, NULL); + return valuesToJarrayHelper(jr, a, v, 0); +} + +/** + * Convert a value to a Java jobject. + */ +const jobject valueToJobject(const JavaRuntime& jr, const value& v) { + switch (type(v)) { + case value::List: + return valuesToJarray(jr, v); + case value::Lambda: + return mkJavaLambda(jr, v); + case value::Symbol: + return jr.env->NewStringUTF(c_str(string("'") + v)); + case value::String: + return jr.env->NewStringUTF(c_str(v)); + case value::Number: + return jr.env->CallStaticObjectMethod(jr.doubleClass, jr.doubleValueOf, (double)v); + case value::Bool: + return jr.env->CallStaticObjectMethod(jr.booleanClass, jr.booleanValueOf, (double)v); + default: + return NULL; + } +} + +/** + * Convert a list of values to an array of jvalues. + */ +const jvalue* valuesToJvaluesHelper(const JavaRuntime& jr, jvalue* a, const list<value>& v) { + if (isNil(v)) + return a; + a->l = valueToJobject(jr, car(v)); + return valuesToJvaluesHelper(jr, a + 1, cdr(v)); +} + +const jvalue* valuesToJvalues(const JavaRuntime& jr, const list<value>& v) { + const int n = length(v); + jvalue* a = new (gc_anew<jvalue>(n)) jvalue[n]; + valuesToJvaluesHelper(jr, a, v); + return a; +} + +/** + * Convert a Java jobjectArray to a list of values. + */ +const list<value> jarrayToValuesHelper(const JavaRuntime& jr, jobjectArray a, const int i, const int size) { + if (i == size) + return list<value>(); + return cons(jobjectToValue(jr, jr.env->GetObjectArrayElement(a, i)), jarrayToValuesHelper(jr, a, i + 1, size)); +} + +const list<value> jarrayToValues(const JavaRuntime& jr, jobjectArray o) { + return jarrayToValuesHelper(jr, o, 0, jr.env->GetArrayLength(o)); +} + +/** + * Lambda function used to represent a Java callable object. + */ +struct javaCallable { + const JavaRuntime& jr; + const jobject obj; + + javaCallable(const JavaRuntime& jr, const jobject obj) : jr(jr), obj(obj) { + } + + const value operator()(const list<value>& args) const { + jobjectArray jargs = valuesToJarray(jr, args); + jobject result = jargs; //CallObject(func, jargs); + return jobjectToValue(jr, result); + } +}; + +/** + * Convert a Java jobject to a value. + */ +const value jobjectToValue(const JavaRuntime& jr, const jobject o) { + if (o == NULL) + return value(); + const jclass clazz = jr.env->GetObjectClass(o); + if ((jr.env->IsSameObject(clazz, jr.stringClass))) { + const char* s = jr.env->GetStringUTFChars((jstring)o, NULL); + if (*s == '\'') { + const value v(s + 1); + jr.env->ReleaseStringUTFChars((jstring)o, s); + return v; + } + const value v = string(s); + jr.env->ReleaseStringUTFChars((jstring)o, s); + return v; + } + if (jr.env->IsSameObject(clazz, jr.booleanClass)) + return value((bool)jr.env->CallBooleanMethod(o, jr.booleanValue)); + if (jr.env->IsSameObject(clazz, jr.doubleClass)) + return value((double)jr.env->CallDoubleMethod(o, jr.doubleValue)); + if ((jr.env->IsSameObject(clazz, jr.objectArrayClass))) + return jarrayToValues(jr, (jobjectArray)o); + return lambda<value(const list<value>&)>(javaCallable(jr, o)); +} + +/** + * Returns a balanced tree of a class' methods. + */ +const value methodToValue(const JavaRuntime& jr, const jobject m) { + const jobject s = jr.env->CallObjectMethod(m, jr.methodName); + const char* c = jr.env->GetStringUTFChars((jstring)s, NULL); + const string& name = string(c); + jr.env->ReleaseStringUTFChars((jstring)s, c); + const jmethodID mid = jr.env->FromReflectedMethod(m); + return mklist<value>(c_str(name), (double)(long)mid); +} + +const list<value> methodsToValues(const JavaRuntime& jr, const jobjectArray m, const int i) { + if (i == 0) + return list<value>(); + return cons<value>(methodToValue(jr, jr.env->GetObjectArrayElement(m, i - 1)), methodsToValues(jr, m, i - 1)); +} + +const list<value> methodsToValues(const JavaRuntime& jr, const jclass clazz) { + const jobjectArray m = (jobjectArray)jr.env->CallObjectMethod(clazz, jr.declaredMethods); + return methodsToValues(jr, m, jr.env->GetArrayLength(m)); +} + +/** + * Represents a Java Class. + */ +class JavaClass { +public: + JavaClass() : clazz(NULL), obj(NULL) { + } + + JavaClass(const jclass clazz, const jobject obj, const list<value> m) : clazz(clazz), obj(obj), m(m) { + } + + const jclass clazz; + const jobject obj; + const list<value> m; +}; + +/** + * Read a class. + */ +const failable<JavaClass> readClass(const JavaRuntime& jr, const string& name) { + const string jname = jniClassName(name); + const jclass clazz = jr.env->FindClass(c_str(jname)); + if (clazz == NULL) + return mkfailure<JavaClass>(string("Couldn't load class: ") + name + " : " + lastException(jr)); + const jmethodID constr = jr.env->GetMethodID(clazz, "<init>", "()V"); + if (constr == NULL) + return mkfailure<JavaClass>(string("Couldn't find constructor: ") + name + " : " + lastException(jr)); + const jobject obj = jr.env->NewObject(clazz, constr); + if (obj == NULL) + return mkfailure<JavaClass>(string("Couldn't construct object: ") + name + " : " + lastException(jr)); + return JavaClass(clazz, obj, methodsToValues(jr, clazz)); +} + +/** + * Evaluate an expression against a Java class. + */ +const failable<value> evalClass(const JavaRuntime& jr, const value& expr, const JavaClass jc) { + + // Lookup the Java function named as the expression operand + const list<value> func = assoc<value>(car<value>(expr), jc.m); + if (isNil(func)) + return mkfailure<value>(string("Couldn't find function: ") + car<value>(expr) + " : " + lastException(jr)); + const jmethodID fid = (jmethodID)(long)(double)cadr(func); + + // Convert args to Java jvalues + const jvalue *args = valuesToJvalues(jr, cdr<value>(expr)); + + // Call the Java function + const jobject result = jr.env->CallObjectMethodA(jc.obj, fid, args); + if (result == NULL) + return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastException(jr)); + + // Convert Java result to a value + return jobjectToValue(jr, result); +} + +} +} +#endif /* tuscany_java_eval_hpp */ |