diff options
Diffstat (limited to 'WebCore/bridge/jni/JNIBridge.cpp')
-rw-r--r-- | WebCore/bridge/jni/JNIBridge.cpp | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/WebCore/bridge/jni/JNIBridge.cpp b/WebCore/bridge/jni/JNIBridge.cpp new file mode 100644 index 0000000..042e8dc --- /dev/null +++ b/WebCore/bridge/jni/JNIBridge.cpp @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JNIBridge.h" + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "CString.h" +#include "JNIUtility.h" +#include "JNIUtilityPrivate.h" +#include "StringBuilder.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <runtime/Error.h> +#include <runtime/JSLock.h> + +#ifdef NDEBUG +#define JS_LOG(formatAndArgs...) ((void)0) +#else +#define JS_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +using namespace JSC; +using namespace JSC::Bindings; +using namespace WebCore; + + +JavaParameter::JavaParameter (JNIEnv *env, jstring type) +{ + _type = JavaString (env, type); + _JNIType = JNITypeFromClassName (_type.UTF8String()); +} + +JavaField::JavaField (JNIEnv *env, jobject aField) +{ + // Get field type + jobject fieldType = callJNIMethod<jobject>(aField, "getType", "()Ljava/lang/Class;"); + jstring fieldTypeName = (jstring)callJNIMethod<jobject>(fieldType, "getName", "()Ljava/lang/String;"); + _type = JavaString(env, fieldTypeName); + _JNIType = JNITypeFromClassName (_type.UTF8String()); + + // Get field name + jstring fieldName = (jstring)callJNIMethod<jobject>(aField, "getName", "()Ljava/lang/String;"); + _name = JavaString(env, fieldName); + + _field = new JObjectWrapper(aField); +} + +JSValue JavaArray::convertJObjectToArray(ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject> rootObject) +{ + if (type[0] != '[') + return jsUndefined(); + + return new (exec) RuntimeArray(exec, new JavaArray((jobject)anObject, type, rootObject)); +} + +jvalue JavaField::dispatchValueFromInstance(ExecState *exec, const JavaInstance *instance, const char *name, const char *sig, JNIType returnType) const +{ + jobject jinstance = instance->javaInstance(); + jobject fieldJInstance = _field->m_instance; + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + jclass cls = env->GetObjectClass(fieldJInstance); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + RootObject* rootObject = instance->rootObject(); + if (rootObject && rootObject->nativeHandle()) { + JSValue exceptionDescription; + jvalue args[1]; + + args[0].l = jinstance; + dispatchJNICall(exec, rootObject->nativeHandle(), fieldJInstance, false, returnType, mid, args, result, 0, exceptionDescription); + if (exceptionDescription) + throwError(exec, GeneralError, exceptionDescription.toString(exec)); + } + } + } + return result; +} + +JSValue JavaField::valueFromInstance(ExecState* exec, const Instance* i) const +{ + const JavaInstance *instance = static_cast<const JavaInstance *>(i); + + JSValue jsresult = jsUndefined(); + + switch (_JNIType) { + case array_type: + case object_type: { + jvalue result = dispatchValueFromInstance (exec, instance, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", object_type); + jobject anObject = result.l; + + const char *arrayType = type(); + if (arrayType[0] == '[') { + jsresult = JavaArray::convertJObjectToArray(exec, anObject, arrayType, instance->rootObject()); + } + else if (anObject != 0){ + jsresult = JavaInstance::create(anObject, instance->rootObject())->createRuntimeObject(exec); + } + } + break; + + case boolean_type: + jsresult = jsBoolean(dispatchValueFromInstance(exec, instance, "getBoolean", "(Ljava/lang/Object;)Z", boolean_type).z); + break; + + case byte_type: + case char_type: + case short_type: + + case int_type: { + jint value; + jvalue result = dispatchValueFromInstance (exec, instance, "getInt", "(Ljava/lang/Object;)I", int_type); + value = result.i; + jsresult = jsNumber(exec, (int)value); + } + break; + + case long_type: + case float_type: + case double_type: { + jdouble value; + jvalue result = dispatchValueFromInstance (exec, instance, "getDouble", "(Ljava/lang/Object;)D", double_type); + value = result.i; + jsresult = jsNumber(exec, (double)value); + } + break; + default: + break; + } + + JS_LOG ("getting %s = %s\n", UString(name()).UTF8String().c_str(), jsresult.toString(exec).ascii()); + + return jsresult; +} + +void JavaField::dispatchSetValueToInstance(ExecState *exec, const JavaInstance *instance, jvalue javaValue, const char *name, const char *sig) const +{ + jobject jinstance = instance->javaInstance(); + jobject fieldJInstance = _field->m_instance; + JNIEnv *env = getJNIEnv(); + + jclass cls = env->GetObjectClass(fieldJInstance); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + RootObject* rootObject = instance->rootObject(); + if (rootObject && rootObject->nativeHandle()) { + JSValue exceptionDescription; + jvalue args[2]; + jvalue result; + + args[0].l = jinstance; + args[1] = javaValue; + dispatchJNICall(exec, rootObject->nativeHandle(), fieldJInstance, false, void_type, mid, args, result, 0, exceptionDescription); + if (exceptionDescription) + throwError(exec, GeneralError, exceptionDescription.toString(exec)); + } + } + } +} + +void JavaField::setValueToInstance(ExecState* exec, const Instance* i, JSValue aValue) const +{ + const JavaInstance *instance = static_cast<const JavaInstance *>(i); + jvalue javaValue = convertValueToJValue (exec, aValue, _JNIType, type()); + + JS_LOG ("setting value %s to %s\n", UString(name()).UTF8String().c_str(), aValue.toString(exec).ascii()); + + switch (_JNIType) { + case array_type: + case object_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V"); + } + break; + + case boolean_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setBoolean", "(Ljava/lang/Object;Z)V"); + } + break; + + case byte_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setByte", "(Ljava/lang/Object;B)V"); + } + break; + + case char_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setChar", "(Ljava/lang/Object;C)V"); + } + break; + + case short_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setShort", "(Ljava/lang/Object;S)V"); + } + break; + + case int_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setInt", "(Ljava/lang/Object;I)V"); + } + break; + + case long_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setLong", "(Ljava/lang/Object;J)V"); + } + break; + + case float_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setFloat", "(Ljava/lang/Object;F)V"); + } + break; + + case double_type: { + dispatchSetValueToInstance (exec, instance, javaValue, "setDouble", "(Ljava/lang/Object;D)V"); + } + break; + default: + break; + } +} + +JavaMethod::JavaMethod (JNIEnv *env, jobject aMethod) +{ + // Get return type + jobject returnType = callJNIMethod<jobject>(aMethod, "getReturnType", "()Ljava/lang/Class;"); + jstring returnTypeName = (jstring)callJNIMethod<jobject>(returnType, "getName", "()Ljava/lang/String;"); + _returnType =JavaString (env, returnTypeName); + _JNIReturnType = JNITypeFromClassName (_returnType.UTF8String()); + env->DeleteLocalRef (returnType); + env->DeleteLocalRef (returnTypeName); + + // Get method name + jstring methodName = (jstring)callJNIMethod<jobject>(aMethod, "getName", "()Ljava/lang/String;"); + _name = JavaString (env, methodName); + env->DeleteLocalRef (methodName); + + // Get parameters + jarray jparameters = (jarray)callJNIMethod<jobject>(aMethod, "getParameterTypes", "()[Ljava/lang/Class;"); + _numParameters = env->GetArrayLength (jparameters); + _parameters = new JavaParameter[_numParameters]; + + int i; + for (i = 0; i < _numParameters; i++) { + jobject aParameter = env->GetObjectArrayElement ((jobjectArray)jparameters, i); + jstring parameterName = (jstring)callJNIMethod<jobject>(aParameter, "getName", "()Ljava/lang/String;"); + _parameters[i] = JavaParameter(env, parameterName); + env->DeleteLocalRef (aParameter); + env->DeleteLocalRef (parameterName); + } + env->DeleteLocalRef (jparameters); + + // Created lazily. + _signature = 0; + _methodID = 0; + + jclass modifierClass = env->FindClass("java/lang/reflect/Modifier"); + int modifiers = callJNIMethod<jint>(aMethod, "getModifiers", "()I"); + _isStatic = (bool)callJNIStaticMethod<jboolean>(modifierClass, "isStatic", "(I)Z", modifiers); + env->DeleteLocalRef(modifierClass); +} + +JavaMethod::~JavaMethod() +{ + if (_signature) + free(_signature); + delete [] _parameters; +}; + +// JNI method signatures use '/' between components of a class name, but +// we get '.' between components from the reflection API. +static void appendClassName(StringBuilder& builder, const char* className) +{ + ASSERT(JSLock::lockCount() > 0); + + char *result, *cp = strdup(className); + + result = cp; + while (*cp) { + if (*cp == '.') + *cp = '/'; + cp++; + } + + builder.append(result); + + free(result); +} + +const char *JavaMethod::signature() const +{ + if (!_signature) { + JSLock lock(SilenceAssertionsOnly); + + StringBuilder signatureBuilder; + signatureBuilder.append("("); + for (int i = 0; i < _numParameters; i++) { + JavaParameter* aParameter = parameterAt(i); + JNIType _JNIType = aParameter->getJNIType(); + if (_JNIType == array_type) + appendClassName(signatureBuilder, aParameter->type()); + else { + signatureBuilder.append(signatureFromPrimitiveType(_JNIType)); + if (_JNIType == object_type) { + appendClassName(signatureBuilder, aParameter->type()); + signatureBuilder.append(";"); + } + } + } + signatureBuilder.append(")"); + + const char *returnType = _returnType.UTF8String(); + if (_JNIReturnType == array_type) { + appendClassName(signatureBuilder, returnType); + } else { + signatureBuilder.append(signatureFromPrimitiveType(_JNIReturnType)); + if (_JNIReturnType == object_type) { + appendClassName(signatureBuilder, returnType); + signatureBuilder.append(";"); + } + } + + String signatureString = signatureBuilder.toString(); + _signature = strdup(signatureString.utf8().data()); + } + + return _signature; +} + +JNIType JavaMethod::JNIReturnType() const +{ + return _JNIReturnType; +} + +jmethodID JavaMethod::methodID (jobject obj) const +{ + if (_methodID == 0) { + _methodID = getMethodID (obj, _name.UTF8String(), signature()); + } + return _methodID; +} + + +JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject) + : Array(rootObject) +{ + _array = new JObjectWrapper(array); + // Java array are fixed length, so we can cache length. + JNIEnv *env = getJNIEnv(); + _length = env->GetArrayLength((jarray)_array->m_instance); + _type = strdup(type); + m_rootObject = rootObject; +} + +JavaArray::~JavaArray () +{ + free ((void *)_type); +} + +RootObject* JavaArray::rootObject() const +{ + return m_rootObject && m_rootObject->isValid() ? m_rootObject.get() : 0; +} + +void JavaArray::setValueAt(ExecState* exec, unsigned index, JSValue aValue) const +{ + JNIEnv *env = getJNIEnv(); + char *javaClassName = 0; + + JNIType arrayType = JNITypeFromPrimitiveType(_type[1]); + if (_type[1] == 'L'){ + // The type of the array will be something like: + // "[Ljava.lang.string;". This is guaranteed, so no need + // for extra sanity checks. + javaClassName = strdup(&_type[2]); + javaClassName[strchr(javaClassName, ';')-javaClassName] = 0; + } + jvalue aJValue = convertValueToJValue (exec, aValue, arrayType, javaClassName); + + switch (arrayType) { + case object_type: { + env->SetObjectArrayElement((jobjectArray)javaArray(), index, aJValue.l); + break; + } + + case boolean_type: { + env->SetBooleanArrayRegion((jbooleanArray)javaArray(), index, 1, &aJValue.z); + break; + } + + case byte_type: { + env->SetByteArrayRegion((jbyteArray)javaArray(), index, 1, &aJValue.b); + break; + } + + case char_type: { + env->SetCharArrayRegion((jcharArray)javaArray(), index, 1, &aJValue.c); + break; + } + + case short_type: { + env->SetShortArrayRegion((jshortArray)javaArray(), index, 1, &aJValue.s); + break; + } + + case int_type: { + env->SetIntArrayRegion((jintArray)javaArray(), index, 1, &aJValue.i); + break; + } + + case long_type: { + env->SetLongArrayRegion((jlongArray)javaArray(), index, 1, &aJValue.j); + } + + case float_type: { + env->SetFloatArrayRegion((jfloatArray)javaArray(), index, 1, &aJValue.f); + break; + } + + case double_type: { + env->SetDoubleArrayRegion((jdoubleArray)javaArray(), index, 1, &aJValue.d); + break; + } + default: + break; + } + + if (javaClassName) + free ((void *)javaClassName); +} + + +JSValue JavaArray::valueAt(ExecState* exec, unsigned index) const +{ + JNIEnv *env = getJNIEnv(); + JNIType arrayType = JNITypeFromPrimitiveType(_type[1]); + switch (arrayType) { + case object_type: { + jobjectArray objectArray = (jobjectArray)javaArray(); + jobject anObject; + anObject = env->GetObjectArrayElement(objectArray, index); + + // No object? + if (!anObject) { + return jsNull(); + } + + // Nested array? + if (_type[1] == '[') { + return JavaArray::convertJObjectToArray(exec, anObject, _type+1, rootObject()); + } + // or array of other object type? + return JavaInstance::create(anObject, rootObject())->createRuntimeObject(exec); + } + + case boolean_type: { + jbooleanArray booleanArray = (jbooleanArray)javaArray(); + jboolean aBoolean; + env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean); + return jsBoolean(aBoolean); + } + + case byte_type: { + jbyteArray byteArray = (jbyteArray)javaArray(); + jbyte aByte; + env->GetByteArrayRegion(byteArray, index, 1, &aByte); + return jsNumber(exec, aByte); + } + + case char_type: { + jcharArray charArray = (jcharArray)javaArray(); + jchar aChar; + env->GetCharArrayRegion(charArray, index, 1, &aChar); + return jsNumber(exec, aChar); + break; + } + + case short_type: { + jshortArray shortArray = (jshortArray)javaArray(); + jshort aShort; + env->GetShortArrayRegion(shortArray, index, 1, &aShort); + return jsNumber(exec, aShort); + } + + case int_type: { + jintArray intArray = (jintArray)javaArray(); + jint anInt; + env->GetIntArrayRegion(intArray, index, 1, &anInt); + return jsNumber(exec, anInt); + } + + case long_type: { + jlongArray longArray = (jlongArray)javaArray(); + jlong aLong; + env->GetLongArrayRegion(longArray, index, 1, &aLong); + return jsNumber(exec, aLong); + } + + case float_type: { + jfloatArray floatArray = (jfloatArray)javaArray(); + jfloat aFloat; + env->GetFloatArrayRegion(floatArray, index, 1, &aFloat); + return jsNumber(exec, aFloat); + } + + case double_type: { + jdoubleArray doubleArray = (jdoubleArray)javaArray(); + jdouble aDouble; + env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble); + return jsNumber(exec, aDouble); + } + default: + break; + } + return jsUndefined(); +} + +unsigned int JavaArray::getLength() const +{ + return _length; +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) |