summaryrefslogtreecommitdiffstats
path: root/WebCore/bridge/jni/JNIBridge.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/bridge/jni/JNIBridge.cpp')
-rw-r--r--WebCore/bridge/jni/JNIBridge.cpp576
1 files changed, 576 insertions, 0 deletions
diff --git a/WebCore/bridge/jni/JNIBridge.cpp b/WebCore/bridge/jni/JNIBridge.cpp
new file mode 100644
index 0000000..627d227
--- /dev/null
+++ b/WebCore/bridge/jni/JNIBridge.cpp
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2007, 2009 Apple Inc. All rights reserved.
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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)
+{
+ m_type = JavaString(env, type);
+ m_JNIType = JNITypeFromClassName(m_type.UTF8String());
+}
+
+JavaField::JavaField(JNIEnv* env, jobject aField)
+{
+ // Get field type
+ jobject fieldType = callJNIMethod<jobject>(aField, "getType", "()Ljava/lang/Class;");
+ jstring fieldTypeName = static_cast<jstring>(callJNIMethod<jobject>(fieldType, "getName", "()Ljava/lang/String;"));
+ m_type = JavaString(env, fieldTypeName);
+ m_JNIType = JNITypeFromClassName(m_type.UTF8String());
+
+ // Get field name
+ jstring fieldName = static_cast<jstring>(callJNIMethod<jobject>(aField, "getName", "()Ljava/lang/String;"));
+ m_name = JavaString(env, fieldName);
+
+ m_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(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 = m_field->m_instance;
+ JNIEnv* env = getJNIEnv();
+ jvalue result;
+
+ bzero(&result, sizeof(jvalue));
+ jclass cls = env->GetObjectClass(fieldJInstance);
+ if (cls) {
+ jmethodID mid = env->GetMethodID(cls, name, sig);
+ if (mid) {
+ 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 (m_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)
+ 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, static_cast<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, static_cast<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 = m_field->m_instance;
+ JNIEnv* env = getJNIEnv();
+
+ jclass cls = env->GetObjectClass(fieldJInstance);
+ if (cls) {
+ jmethodID mid = env->GetMethodID(cls, name, sig);
+ if (mid) {
+ 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, m_JNIType, type());
+
+ JS_LOG("setting value %s to %s\n", UString(name()).UTF8String().c_str(), aValue.toString(exec).ascii());
+
+ switch (m_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 = static_cast<jstring>(callJNIMethod<jobject>(returnType, "getName", "()Ljava/lang/String;"));
+ m_returnType = JavaString(env, returnTypeName);
+ m_JNIReturnType = JNITypeFromClassName(m_returnType.UTF8String());
+ env->DeleteLocalRef(returnType);
+ env->DeleteLocalRef(returnTypeName);
+
+ // Get method name
+ jstring methodName = static_cast<jstring>(callJNIMethod<jobject>(aMethod, "getName", "()Ljava/lang/String;"));
+ m_name = JavaString(env, methodName);
+ env->DeleteLocalRef(methodName);
+
+ // Get parameters
+ jarray jparameters = static_cast<jarray>(callJNIMethod<jobject>(aMethod, "getParameterTypes", "()[Ljava/lang/Class;"));
+ m_numParameters = env->GetArrayLength(jparameters);
+ m_parameters = new JavaParameter[m_numParameters];
+
+ for (int i = 0; i < m_numParameters; i++) {
+ jobject aParameter = env->GetObjectArrayElement(static_cast<jobjectArray>(jparameters), i);
+ jstring parameterName = static_cast<jstring>(callJNIMethod<jobject>(aParameter, "getName", "()Ljava/lang/String;"));
+ m_parameters[i] = JavaParameter(env, parameterName);
+ env->DeleteLocalRef(aParameter);
+ env->DeleteLocalRef(parameterName);
+ }
+ env->DeleteLocalRef(jparameters);
+
+ // Created lazily.
+ m_signature = 0;
+ m_methodID = 0;
+
+ jclass modifierClass = env->FindClass("java/lang/reflect/Modifier");
+ int modifiers = callJNIMethod<jint>(aMethod, "getModifiers", "()I");
+ m_isStatic = static_cast<bool>(callJNIStaticMethod<jboolean>(modifierClass, "isStatic", "(I)Z", modifiers));
+ env->DeleteLocalRef(modifierClass);
+}
+
+JavaMethod::~JavaMethod()
+{
+ if (m_signature)
+ free(m_signature);
+ delete[] m_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* c = strdup(className);
+
+ char* result = c;
+ while (*c) {
+ if (*c == '.')
+ *c = '/';
+ c++;
+ }
+
+ builder.append(result);
+
+ free(result);
+}
+
+const char* JavaMethod::signature() const
+{
+ if (!m_signature) {
+ JSLock lock(SilenceAssertionsOnly);
+
+ StringBuilder signatureBuilder;
+ signatureBuilder.append("(");
+ for (int i = 0; i < m_numParameters; i++) {
+ JavaParameter* aParameter = parameterAt(i);
+ JNIType type = aParameter->getJNIType();
+ if (type == array_type)
+ appendClassName(signatureBuilder, aParameter->type());
+ else {
+ signatureBuilder.append(signatureFromPrimitiveType(type));
+ if (type == object_type) {
+ appendClassName(signatureBuilder, aParameter->type());
+ signatureBuilder.append(";");
+ }
+ }
+ }
+ signatureBuilder.append(")");
+
+ const char* returnType = m_returnType.UTF8String();
+ if (m_JNIReturnType == array_type)
+ appendClassName(signatureBuilder, returnType);
+ else {
+ signatureBuilder.append(signatureFromPrimitiveType(m_JNIReturnType));
+ if (m_JNIReturnType == object_type) {
+ appendClassName(signatureBuilder, returnType);
+ signatureBuilder.append(";");
+ }
+ }
+
+ String signatureString = signatureBuilder.toString();
+ m_signature = strdup(signatureString.utf8().data());
+ }
+
+ return m_signature;
+}
+
+JNIType JavaMethod::JNIReturnType() const
+{
+ return m_JNIReturnType;
+}
+
+jmethodID JavaMethod::methodID(jobject obj) const
+{
+ if (!m_methodID)
+ m_methodID = getMethodID(obj, m_name.UTF8String(), signature());
+ return m_methodID;
+}
+
+
+JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject)
+ : Array(rootObject)
+{
+ m_array = new JObjectWrapper(array);
+ // Java array are fixed length, so we can cache length.
+ JNIEnv* env = getJNIEnv();
+ m_length = env->GetArrayLength(static_cast<jarray>(m_array->m_instance));
+ m_type = strdup(type);
+ m_rootObject = rootObject;
+}
+
+JavaArray::~JavaArray()
+{
+ free(const_cast<char*>(m_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(m_type[1]);
+ if (m_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(&m_type[2]);
+ javaClassName[strchr(javaClassName, ';')-javaClassName] = 0;
+ }
+ jvalue aJValue = convertValueToJValue(exec, aValue, arrayType, javaClassName);
+
+ switch (arrayType) {
+ case object_type:
+ {
+ env->SetObjectArrayElement(static_cast<jobjectArray>(javaArray()), index, aJValue.l);
+ break;
+ }
+
+ case boolean_type:
+ {
+ env->SetBooleanArrayRegion(static_cast<jbooleanArray>(javaArray()), index, 1, &aJValue.z);
+ break;
+ }
+
+ case byte_type:
+ {
+ env->SetByteArrayRegion(static_cast<jbyteArray>(javaArray()), index, 1, &aJValue.b);
+ break;
+ }
+
+ case char_type:
+ {
+ env->SetCharArrayRegion(static_cast<jcharArray>(javaArray()), index, 1, &aJValue.c);
+ break;
+ }
+
+ case short_type:
+ {
+ env->SetShortArrayRegion(static_cast<jshortArray>(javaArray()), index, 1, &aJValue.s);
+ break;
+ }
+
+ case int_type:
+ {
+ env->SetIntArrayRegion(static_cast<jintArray>(javaArray()), index, 1, &aJValue.i);
+ break;
+ }
+
+ case long_type:
+ {
+ env->SetLongArrayRegion(static_cast<jlongArray>(javaArray()), index, 1, &aJValue.j);
+ }
+
+ case float_type:
+ {
+ env->SetFloatArrayRegion(static_cast<jfloatArray>(javaArray()), index, 1, &aJValue.f);
+ break;
+ }
+
+ case double_type:
+ {
+ env->SetDoubleArrayRegion(static_cast<jdoubleArray>(javaArray()), index, 1, &aJValue.d);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (javaClassName)
+ free(const_cast<char*>(javaClassName));
+}
+
+
+JSValue JavaArray::valueAt(ExecState* exec, unsigned index) const
+{
+ JNIEnv* env = getJNIEnv();
+ JNIType arrayType = JNITypeFromPrimitiveType(m_type[1]);
+ switch (arrayType) {
+ case object_type:
+ {
+ jobjectArray objectArray = static_cast<jobjectArray>(javaArray());
+ jobject anObject;
+ anObject = env->GetObjectArrayElement(objectArray, index);
+
+ // No object?
+ if (!anObject)
+ return jsNull();
+
+ // Nested array?
+ if (m_type[1] == '[')
+ return JavaArray::convertJObjectToArray(exec, anObject, m_type + 1, rootObject());
+ // or array of other object type?
+ return JavaInstance::create(anObject, rootObject())->createRuntimeObject(exec);
+ }
+
+ case boolean_type:
+ {
+ jbooleanArray booleanArray = static_cast<jbooleanArray>(javaArray());
+ jboolean aBoolean;
+ env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean);
+ return jsBoolean(aBoolean);
+ }
+
+ case byte_type:
+ {
+ jbyteArray byteArray = static_cast<jbyteArray>(javaArray());
+ jbyte aByte;
+ env->GetByteArrayRegion(byteArray, index, 1, &aByte);
+ return jsNumber(exec, aByte);
+ }
+
+ case char_type:
+ {
+ jcharArray charArray = static_cast<jcharArray>(javaArray());
+ jchar aChar;
+ env->GetCharArrayRegion(charArray, index, 1, &aChar);
+ return jsNumber(exec, aChar);
+ break;
+ }
+
+ case short_type:
+ {
+ jshortArray shortArray = static_cast<jshortArray>(javaArray());
+ jshort aShort;
+ env->GetShortArrayRegion(shortArray, index, 1, &aShort);
+ return jsNumber(exec, aShort);
+ }
+
+ case int_type:
+ {
+ jintArray intArray = static_cast<jintArray>(javaArray());
+ jint anInt;
+ env->GetIntArrayRegion(intArray, index, 1, &anInt);
+ return jsNumber(exec, anInt);
+ }
+
+ case long_type:
+ {
+ jlongArray longArray = static_cast<jlongArray>(javaArray());
+ jlong aLong;
+ env->GetLongArrayRegion(longArray, index, 1, &aLong);
+ return jsNumber(exec, aLong);
+ }
+
+ case float_type:
+ {
+ jfloatArray floatArray = static_cast<jfloatArray>(javaArray());
+ jfloat aFloat;
+ env->GetFloatArrayRegion(floatArray, index, 1, &aFloat);
+ return jsNumber(exec, aFloat);
+ }
+
+ case double_type:
+ {
+ jdoubleArray doubleArray = static_cast<jdoubleArray>(javaArray());
+ jdouble aDouble;
+ env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble);
+ return jsNumber(exec, aDouble);
+ }
+ default:
+ break;
+ }
+ return jsUndefined();
+}
+
+unsigned int JavaArray::getLength() const
+{
+ return m_length;
+}
+
+#endif // ENABLE(MAC_JAVA_BRIDGE)