diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
commit | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch) | |
tree | 4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/bridge/jni | |
parent | 9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff) | |
download | external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/bridge/jni')
-rw-r--r-- | WebCore/bridge/jni/jni_class.cpp | 143 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_class.h | 66 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_instance.cpp | 334 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_instance.h | 110 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_jsobject.h | 129 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_jsobject.mm | 720 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_objc.mm | 83 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_runtime.cpp | 547 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_runtime.h | 191 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_utility.cpp | 584 | ||||
-rw-r--r-- | WebCore/bridge/jni/jni_utility.h | 288 |
11 files changed, 3195 insertions, 0 deletions
diff --git a/WebCore/bridge/jni/jni_class.cpp b/WebCore/bridge/jni/jni_class.cpp new file mode 100644 index 0000000..4140524 --- /dev/null +++ b/WebCore/bridge/jni/jni_class.cpp @@ -0,0 +1,143 @@ +/* + * 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 "jni_class.h" + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "JSDOMWindow.h" +#include <kjs/identifier.h> +#include <runtime/JSLock.h> +#include "jni_utility.h" +#include "jni_runtime.h" + +using namespace JSC::Bindings; + +JavaClass::JavaClass(jobject anInstance) +{ + jobject aClass = callJNIMethod<jobject>(anInstance, "getClass", "()Ljava/lang/Class;"); + + if (!aClass) { + fprintf(stderr, "%s: unable to call getClass on instance %p\n", __PRETTY_FUNCTION__, anInstance); + return; + } + + jstring className = (jstring)callJNIMethod<jobject>(aClass, "getName", "()Ljava/lang/String;"); + const char *classNameC = getCharactersFromJString(className); + _name = strdup(classNameC); + releaseCharactersForJString(className, classNameC); + + int i; + JNIEnv *env = getJNIEnv(); + + JSGlobalData* globalData = WebCore::JSDOMWindow::commonJSGlobalData(); + + // Get the fields + jarray fields = (jarray)callJNIMethod<jobject>(aClass, "getFields", "()[Ljava/lang/reflect/Field;"); + int numFields = env->GetArrayLength(fields); + for (i = 0; i < numFields; i++) { + jobject aJField = env->GetObjectArrayElement((jobjectArray)fields, i); + Field *aField = new JavaField(env, aJField); // deleted in the JavaClass destructor + { + JSLock lock(false); + _fields.set(Identifier(globalData, UString(aField->name())).ustring().rep(), aField); + } + env->DeleteLocalRef(aJField); + } + + // Get the methods + jarray methods = (jarray)callJNIMethod<jobject>(aClass, "getMethods", "()[Ljava/lang/reflect/Method;"); + int numMethods = env->GetArrayLength(methods); + for (i = 0; i < numMethods; i++) { + jobject aJMethod = env->GetObjectArrayElement((jobjectArray)methods, i); + Method *aMethod = new JavaMethod(env, aJMethod); // deleted in the JavaClass destructor + MethodList* methodList; + { + JSLock lock(false); + + methodList = _methods.get(Identifier(globalData, UString(aMethod->name())).ustring().rep()); + if (!methodList) { + methodList = new MethodList(); + _methods.set(Identifier(globalData, UString(aMethod->name())).ustring().rep(), methodList); + } + } + methodList->append(aMethod); + env->DeleteLocalRef(aJMethod); + } +} + +JavaClass::~JavaClass() { + free((void *)_name); + + JSLock lock(false); + + deleteAllValues(_fields); + _fields.clear(); + + MethodListMap::const_iterator end = _methods.end(); + for (MethodListMap::const_iterator it = _methods.begin(); it != end; ++it) { + const MethodList* methodList = it->second; + deleteAllValues(*methodList); + delete methodList; + } + _methods.clear(); +} + +MethodList JavaClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList *methodList = _methods.get(identifier.ustring().rep()); + + if (methodList) + return *methodList; + return MethodList(); +} + +Field *JavaClass::fieldNamed(const Identifier& identifier, Instance*) const +{ + return _fields.get(identifier.ustring().rep()); +} + +bool JavaClass::isNumberClass() const +{ + return ((strcmp(_name, "java.lang.Byte") == 0 || + strcmp(_name, "java.lang.Short") == 0 || + strcmp(_name, "java.lang.Integer") == 0 || + strcmp(_name, "java.lang.Long") == 0 || + strcmp(_name, "java.lang.Float") == 0 || + strcmp(_name, "java.lang.Double") == 0) ); +} + +bool JavaClass::isBooleanClass() const +{ + return strcmp(_name, "java.lang.Boolean") == 0; +} + +bool JavaClass::isStringClass() const +{ + return strcmp(_name, "java.lang.String") == 0; +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_class.h b/WebCore/bridge/jni/jni_class.h new file mode 100644 index 0000000..75cfd89 --- /dev/null +++ b/WebCore/bridge/jni/jni_class.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef JNI_CLASS_H_ +#define JNI_CLASS_H_ + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <jni_runtime.h> +#include <wtf/HashMap.h> + +namespace JSC { + +namespace Bindings { + +class JavaClass : public Class { +public: + JavaClass (jobject anInstance); + ~JavaClass (); + + virtual const char *name() const { return _name; }; + + virtual MethodList methodsNamed(const Identifier&, Instance* instance) const; + virtual Field *fieldNamed(const Identifier&, Instance* instance) const; + + bool isNumberClass() const; + bool isBooleanClass() const; + bool isStringClass() const; + +private: + JavaClass (); // prevent default construction + + const char *_name; + FieldMap _fields; + MethodListMap _methods; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) + +#endif // JNI_CLASS_H_ diff --git a/WebCore/bridge/jni/jni_instance.cpp b/WebCore/bridge/jni/jni_instance.cpp new file mode 100644 index 0000000..6b23900 --- /dev/null +++ b/WebCore/bridge/jni/jni_instance.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2003, 2008 Apple 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 "jni_instance.h" + +#ifdef ANDROID +#include <assert.h> +#endif + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "jni_class.h" +#include "jni_runtime.h" +#include "jni_utility.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <runtime/ArgList.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::Bindings; +using namespace JSC; + +JavaInstance::JavaInstance (jobject instance, PassRefPtr<RootObject> rootObject) + : Instance(rootObject) +{ + _instance = new JObjectWrapper (instance); + _class = 0; +} + +JavaInstance::~JavaInstance () +{ + delete _class; +} + +#define NUM_LOCAL_REFS 64 + +void JavaInstance::virtualBegin() +{ + getJNIEnv()->PushLocalFrame (NUM_LOCAL_REFS); +} + +void JavaInstance::virtualEnd() +{ + getJNIEnv()->PopLocalFrame (NULL); +} + +Class *JavaInstance::getClass() const +{ + if (_class == 0) + _class = new JavaClass (_instance->_instance); + return _class; +} + +JSValue* JavaInstance::stringValue(ExecState* exec) const +{ + JSLock lock(false); + + jstring stringValue = (jstring)callJNIMethod<jobject>(_instance->_instance, "toString", "()Ljava/lang/String;"); + JNIEnv *env = getJNIEnv(); + const jchar *c = getUCharactersFromJStringInEnv(env, stringValue); + UString u((const UChar *)c, (int)env->GetStringLength(stringValue)); + releaseUCharactersForJStringInEnv(env, stringValue, c); + return jsString(exec, u); +} + +JSValue* JavaInstance::numberValue(ExecState* exec) const +{ + jdouble doubleValue = callJNIMethod<jdouble>(_instance->_instance, "doubleValue", "()D"); + return jsNumber(exec, doubleValue); +} + +JSValue* JavaInstance::booleanValue() const +{ + jboolean booleanValue = callJNIMethod<jboolean>(_instance->_instance, "booleanValue", "()Z"); + return jsBoolean(booleanValue); +} + +JSValue* JavaInstance::invokeMethod (ExecState *exec, const MethodList &methodList, const ArgList &args) +{ + int i, count = args.size(); + jvalue *jArgs; + JSValue* resultValue; + Method *method = 0; + size_t numMethods = methodList.size(); + + // Try to find a good match for the overloaded method. The + // fundamental problem is that JavaScript doesn have the + // notion of method overloading and Java does. We could + // get a bit more sophisticated and attempt to does some + // type checking as we as checking the number of parameters. + Method *aMethod; + for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) { + aMethod = methodList[methodIndex]; + if (aMethod->numParameters() == count) { + method = aMethod; + break; + } + } + if (method == 0) { + JS_LOG ("unable to find an appropiate method\n"); + return jsUndefined(); + } + + const JavaMethod *jMethod = static_cast<const JavaMethod*>(method); + JS_LOG ("call %s %s on %p\n", method->name(), jMethod->signature(), _instance->_instance); + + if (count > 0) { + jArgs = (jvalue *)malloc (count * sizeof(jvalue)); + } + else + jArgs = 0; + + for (i = 0; i < count; i++) { + JavaParameter* aParameter = jMethod->parameterAt(i); + jArgs[i] = convertValueToJValue(exec, args.at(exec, i), aParameter->getJNIType(), aParameter->type()); + JS_LOG("arg[%d] = %s\n", i, args.at(exec, i)->toString(exec).ascii()); + } + + jvalue result; + + // Try to use the JNI abstraction first, otherwise fall back to + // nornmal JNI. The JNI dispatch abstraction allows the Java plugin + // to dispatch the call on the appropriate internal VM thread. + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return jsUndefined(); + + bool handled = false; + if (rootObject->nativeHandle()) { + jobject obj = _instance->_instance; + JSValue* exceptionDescription = noValue(); + const char *callingURL = 0; // FIXME, need to propagate calling URL to Java + handled = dispatchJNICall(exec, rootObject->nativeHandle(), obj, jMethod->isStatic(), jMethod->JNIReturnType(), jMethod->methodID(obj), jArgs, result, callingURL, exceptionDescription); + if (exceptionDescription) { + throwError(exec, GeneralError, exceptionDescription->toString(exec)); + free (jArgs); + return jsUndefined(); + } + } + + // The following code can be conditionally removed once we have a Tiger update that + // contains the new Java plugin. It is needed for builds prior to Tiger. + if (!handled) { + jobject obj = _instance->_instance; + switch (jMethod->JNIReturnType()){ + case void_type: + callJNIMethodIDA<void>(obj, jMethod->methodID(obj), jArgs); + break; + case object_type: + result.l = callJNIMethodIDA<jobject>(obj, jMethod->methodID(obj), jArgs); + break; + case boolean_type: + result.z = callJNIMethodIDA<jboolean>(obj, jMethod->methodID(obj), jArgs); + break; + case byte_type: + result.b = callJNIMethodIDA<jbyte>(obj, jMethod->methodID(obj), jArgs); + break; + case char_type: + result.c = callJNIMethodIDA<jchar>(obj, jMethod->methodID(obj), jArgs); + break; + case short_type: + result.s = callJNIMethodIDA<jshort>(obj, jMethod->methodID(obj), jArgs); + break; + case int_type: + result.i = callJNIMethodIDA<jint>(obj, jMethod->methodID(obj), jArgs); + break; + + case long_type: + result.j = callJNIMethodIDA<jlong>(obj, jMethod->methodID(obj), jArgs); + break; + case float_type: + result.f = callJNIMethodIDA<jfloat>(obj, jMethod->methodID(obj), jArgs); + break; + case double_type: + result.d = callJNIMethodIDA<jdouble>(obj, jMethod->methodID(obj), jArgs); + break; + case invalid_type: + default: + break; + } + } + + switch (jMethod->JNIReturnType()){ + case void_type: { + resultValue = jsUndefined(); + } + break; + + case object_type: { + if (result.l != 0) { + const char *arrayType = jMethod->returnType(); + if (arrayType[0] == '[') { + resultValue = JavaArray::convertJObjectToArray(exec, result.l, arrayType, rootObject); + } + else { + resultValue = Instance::createRuntimeObject(exec, JavaInstance::create(result.l, rootObject)); + } + } + else { + resultValue = jsUndefined(); + } + } + break; + + case boolean_type: { + resultValue = jsBoolean(result.z); + } + break; + + case byte_type: { + resultValue = jsNumber(exec, result.b); + } + break; + + case char_type: { + resultValue = jsNumber(exec, result.c); + } + break; + + case short_type: { + resultValue = jsNumber(exec, result.s); + } + break; + + case int_type: { + resultValue = jsNumber(exec, result.i); + } + break; + + case long_type: { + resultValue = jsNumber(exec, result.j); + } + break; + + case float_type: { + resultValue = jsNumber(exec, result.f); + } + break; + + case double_type: { + resultValue = jsNumber(exec, result.d); + } + break; + + case invalid_type: + default: { + resultValue = jsUndefined(); + } + break; + } + + free (jArgs); + + return resultValue; +} + +JSValue* JavaInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + if (hint == PreferString) + return stringValue(exec); + if (hint == PreferNumber) + return numberValue(exec); + JavaClass *aClass = static_cast<JavaClass*>(getClass()); + if (aClass->isStringClass()) + return stringValue(exec); + if (aClass->isNumberClass()) + return numberValue(exec); + if (aClass->isBooleanClass()) + return booleanValue(); + return valueOf(exec); +} + +JSValue* JavaInstance::valueOf(ExecState* exec) const +{ + return stringValue(exec); +} + +JObjectWrapper::JObjectWrapper(jobject instance) +: _refCount(0) +{ + assert (instance != 0); + + // Cache the JNIEnv used to get the global ref for this java instanace. + // It'll be used to delete the reference. + _env = getJNIEnv(); + + _instance = _env->NewGlobalRef (instance); + + JS_LOG ("new global ref %p for %p\n", _instance, instance); + + if (_instance == NULL) { + fprintf (stderr, "%s: could not get GlobalRef for %p\n", __PRETTY_FUNCTION__, instance); + } +} + +JObjectWrapper::~JObjectWrapper() { + JS_LOG ("deleting global ref %p\n", _instance); + _env->DeleteGlobalRef (_instance); +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_instance.h b/WebCore/bridge/jni/jni_instance.h new file mode 100644 index 0000000..7104865 --- /dev/null +++ b/WebCore/bridge/jni/jni_instance.h @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#ifndef _JNI_INSTANCE_H_ +#define _JNI_INSTANCE_H_ + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "runtime.h" +#include "runtime_root.h" + +#include <JavaVM/jni.h> + +namespace JSC { + +namespace Bindings { + +class JavaClass; + +class JObjectWrapper +{ +friend class RefPtr<JObjectWrapper>; +friend class JavaArray; +friend class JavaField; +friend class JavaInstance; +friend class JavaMethod; + +protected: + JObjectWrapper(jobject instance); + ~JObjectWrapper(); + + void ref() { _refCount++; } + void deref() + { + if (--_refCount == 0) + delete this; + } + + jobject _instance; + +private: + JNIEnv *_env; + unsigned int _refCount; +}; + +class JavaInstance : public Instance +{ +public: + static PassRefPtr<JavaInstance> create(jobject instance, PassRefPtr<RootObject> rootObject) + { + return adoptRef(new JavaInstance(instance, rootObject)); + } + + ~JavaInstance(); + + virtual Class *getClass() const; + + virtual JSValue* valueOf(ExecState*) const; + virtual JSValue* defaultValue(ExecState*, PreferredPrimitiveType) const; + + virtual JSValue* invokeMethod(ExecState* exec, const MethodList& method, const ArgList& args); + + jobject javaInstance() const { return _instance->_instance; } + + JSValue* stringValue(ExecState*) const; + JSValue* numberValue(ExecState*) const; + JSValue* booleanValue() const; + + virtual BindingLanguage getBindingLanguage() const { return JavaLanguage; } + +protected: + virtual void virtualBegin(); + virtual void virtualEnd(); + +private: + JavaInstance(jobject instance, PassRefPtr<RootObject>); + + RefPtr<JObjectWrapper> _instance; + mutable JavaClass *_class; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) + +#endif // _JNI_INSTANCE_H_ diff --git a/WebCore/bridge/jni/jni_jsobject.h b/WebCore/bridge/jni/jni_jsobject.h new file mode 100644 index 0000000..812317d --- /dev/null +++ b/WebCore/bridge/jni/jni_jsobject.h @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#ifndef JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H +#define JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <CoreFoundation/CoreFoundation.h> +#include <JavaVM/jni.h> +#include <runtime/JSValue.h> +#include <wtf/RefPtr.h> + +#define jlong_to_ptr(a) ((void*)(uintptr_t)(a)) +#define jlong_to_impptr(a) (static_cast<JSC::JSObject*>(((void*)(uintptr_t)(a)))) +#define ptr_to_jlong(a) ((jlong)(uintptr_t)(a)) + +namespace JSC { + +class ArgList; +class ExecState; +class JSObject; + +namespace Bindings { + +class RootObject; + +enum JSObjectCallType { + CreateNative, + Call, + Eval, + GetMember, + SetMember, + RemoveMember, + GetSlot, + SetSlot, + ToString, + Finalize +}; + +struct JSObjectCallContext +{ + JSObjectCallType type; + jlong nativeHandle; + jstring string; + jobjectArray args; + jint index; + jobject value; + CFRunLoopRef originatingLoop; + jvalue result; +}; + +class JavaJSObject +{ +public: + JavaJSObject(jlong nativeHandle); + + static jlong createNative(jlong nativeHandle); + jobject call(jstring methodName, jobjectArray args) const; + jobject eval(jstring script) const; + jobject getMember(jstring memberName) const; + void setMember(jstring memberName, jobject value) const; + void removeMember(jstring memberName) const; + jobject getSlot(jint index) const; + void setSlot(jint index, jobject value) const; + jstring toString() const; + void finalize() const; + + static jvalue invoke(JSObjectCallContext*); + + jobject convertValueToJObject(JSValue*) const; + JSValue* convertJObjectToValue(ExecState*, jobject) const; + void getListFromJArray(ExecState*, jobjectArray, ArgList&) const; + + RootObject* rootObject() const; + + // Must be called from the thread that will be used to access JavaScript. + static void initializeJNIThreading(); +private: + RefPtr<RootObject> _rootObject; + JSObject* _imp; +}; + + +} // namespace Bindings + +} // namespace JSC + +extern "C" { + +// The Java VM calls these functions to handle calls to methods in Java's JSObject class. +jlong KJS_JSCreateNativeJSObject(JNIEnv*, jclass, jstring jurl, jlong nativeHandle, jboolean ctx); +void KJS_JSObject_JSFinalize(JNIEnv*, jclass, jlong nativeJSObject); +jobject KJS_JSObject_JSObjectCall(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring methodName, jobjectArray args, jboolean ctx); +jobject KJS_JSObject_JSObjectEval(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jscript, jboolean ctx); +jobject KJS_JSObject_JSObjectGetMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jboolean ctx); +void KJS_JSObject_JSObjectSetMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jobject value, jboolean ctx); +void KJS_JSObject_JSObjectRemoveMember(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jstring jname, jboolean ctx); +jobject KJS_JSObject_JSObjectGetSlot(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jint jindex, jboolean ctx); +void KJS_JSObject_JSObjectSetSlot(JNIEnv*, jclass, jlong nativeJSObject, jstring jurl, jint jindex, jobject value, jboolean ctx); +jstring KJS_JSObject_JSObjectToString(JNIEnv*, jclass, jlong nativeJSObject); + +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) + +#endif // JAVASCRIPTCORE_BINDINGS_JNI_JSOBJECT_H diff --git a/WebCore/bridge/jni/jni_jsobject.mm b/WebCore/bridge/jni/jni_jsobject.mm new file mode 100644 index 0000000..042d1ed --- /dev/null +++ b/WebCore/bridge/jni/jni_jsobject.mm @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2003, 2008 Apple 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 "jni_jsobject.h" + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "Frame.h" +#include "WebCoreFrameView.h" +#include "jni_runtime.h" +#include "jni_utility.h" +#include "ScriptController.h" +#include "runtime_object.h" +#include "runtime_root.h" +#include <runtime/ExecState.h> +#include <runtime/JSGlobalObject.h> +#include <runtime/JSLock.h> +#include <kjs/completion.h> +#include <kjs/interpreter.h> +#include <wtf/Assertions.h> +#include <kjs/SourceProvider.h> + +using WebCore::Frame; + +using namespace JSC::Bindings; +using namespace JSC; + +#ifdef NDEBUG +#define JS_LOG(formatAndArgs...) ((void)0) +#else +#define JS_LOG(formatAndArgs...) { \ + fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, _performJavaScriptRunLoop, CFRunLoopGetCurrent()); \ + fprintf(stderr, formatAndArgs); \ +} +#endif + +#define UndefinedHandle 1 + +static CFRunLoopSourceRef _performJavaScriptSource; +static CFRunLoopRef _performJavaScriptRunLoop; + +// May only be set by dispatchToJavaScriptThread(). +static CFRunLoopSourceRef completionSource; + +static void completedJavaScriptAccess (void *i) +{ + assert (CFRunLoopGetCurrent() != _performJavaScriptRunLoop); + + JSObjectCallContext *callContext = (JSObjectCallContext *)i; + CFRunLoopRef runLoop = (CFRunLoopRef)callContext->originatingLoop; + + assert (CFRunLoopGetCurrent() == runLoop); + + CFRunLoopStop(runLoop); +} + +static pthread_once_t javaScriptAccessLockOnce = PTHREAD_ONCE_INIT; +static pthread_mutex_t javaScriptAccessLock; +static int javaScriptAccessLockCount = 0; + +static void initializeJavaScriptAccessLock() +{ + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&javaScriptAccessLock, &attr); +} + +static inline void lockJavaScriptAccess() +{ + // Perhaps add deadlock detection? + pthread_once(&javaScriptAccessLockOnce, initializeJavaScriptAccessLock); + pthread_mutex_lock(&javaScriptAccessLock); + javaScriptAccessLockCount++; +} + +static inline void unlockJavaScriptAccess() +{ + javaScriptAccessLockCount--; + pthread_mutex_unlock(&javaScriptAccessLock); +} + +static void dispatchToJavaScriptThread(JSObjectCallContext *context) +{ + // This lock guarantees that only one thread can invoke + // at a time, and also guarantees that completionSource; + // won't get clobbered. + lockJavaScriptAccess(); + + CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent(); + + assert (currentRunLoop != _performJavaScriptRunLoop); + + // Setup a source to signal once the invocation of the JavaScript + // call completes. + // + // FIXME: This could be a potential performance issue. Creating and + // adding run loop sources is expensive. We could create one source + // per thread, as needed, instead. + context->originatingLoop = currentRunLoop; + CFRunLoopSourceContext sourceContext = {0, context, NULL, NULL, NULL, NULL, NULL, NULL, NULL, completedJavaScriptAccess}; + completionSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(currentRunLoop, completionSource, kCFRunLoopDefaultMode); + + // Wakeup JavaScript access thread and make it do it's work. + CFRunLoopSourceSignal(_performJavaScriptSource); + if (CFRunLoopIsWaiting(_performJavaScriptRunLoop)) + CFRunLoopWakeUp(_performJavaScriptRunLoop); + + // Wait until the JavaScript access thread is done. + CFRunLoopRun (); + + CFRunLoopRemoveSource(currentRunLoop, completionSource, kCFRunLoopDefaultMode); + CFRelease (completionSource); + + unlockJavaScriptAccess(); +} + +static void performJavaScriptAccess(void*) +{ + assert (CFRunLoopGetCurrent() == _performJavaScriptRunLoop); + + // Dispatch JavaScript calls here. + CFRunLoopSourceContext sourceContext; + CFRunLoopSourceGetContext (completionSource, &sourceContext); + JSObjectCallContext *callContext = (JSObjectCallContext *)sourceContext.info; + CFRunLoopRef originatingLoop = callContext->originatingLoop; + + JavaJSObject::invoke (callContext); + + // Signal the originating thread that we're done. + CFRunLoopSourceSignal (completionSource); + if (CFRunLoopIsWaiting(originatingLoop)) + CFRunLoopWakeUp(originatingLoop); +} + +// Must be called from the thread that will be used to access JavaScript. +void JavaJSObject::initializeJNIThreading() { + // Should only be called once. + ASSERT(!_performJavaScriptRunLoop); + + // Assume that we can retain this run loop forever. It'll most + // likely (always?) be the main loop. + _performJavaScriptRunLoop = (CFRunLoopRef)CFRetain(CFRunLoopGetCurrent()); + + // Setup a source the other threads can use to signal the _runLoop + // thread that a JavaScript call needs to be invoked. + CFRunLoopSourceContext sourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, performJavaScriptAccess}; + _performJavaScriptSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(_performJavaScriptRunLoop, _performJavaScriptSource, kCFRunLoopDefaultMode); +} + +static bool isJavaScriptThread() +{ + return (_performJavaScriptRunLoop == CFRunLoopGetCurrent()); +} + +jvalue JavaJSObject::invoke(JSObjectCallContext *context) +{ + jvalue result; + + bzero ((void *)&result, sizeof(jvalue)); + + if (!isJavaScriptThread()) { + // Send the call context to the thread that is allowed to + // call JavaScript. + dispatchToJavaScriptThread(context); + result = context->result; + } + else { + jlong nativeHandle = context->nativeHandle; + if (nativeHandle == UndefinedHandle || nativeHandle == 0) { + return result; + } + + if (context->type == CreateNative) { + result.j = JavaJSObject::createNative(nativeHandle); + } + else { + JSObject *imp = jlong_to_impptr(nativeHandle); + if (!findProtectingRootObject(imp)) { + fprintf (stderr, "%s:%d: Attempt to access JavaScript from destroyed applet, type %d.\n", __FILE__, __LINE__, context->type); + return result; + } + + switch (context->type){ + case Call: { + result.l = JavaJSObject(nativeHandle).call(context->string, context->args); + break; + } + + case Eval: { + result.l = JavaJSObject(nativeHandle).eval(context->string); + break; + } + + case GetMember: { + result.l = JavaJSObject(nativeHandle).getMember(context->string); + break; + } + + case SetMember: { + JavaJSObject(nativeHandle).setMember(context->string, context->value); + break; + } + + case RemoveMember: { + JavaJSObject(nativeHandle).removeMember(context->string); + break; + } + + case GetSlot: { + result.l = JavaJSObject(nativeHandle).getSlot(context->index); + break; + } + + case SetSlot: { + JavaJSObject(nativeHandle).setSlot(context->index, context->value); + break; + } + + case ToString: { + result.l = (jobject) JavaJSObject(nativeHandle).toString(); + break; + } + + case Finalize: { + JavaJSObject(nativeHandle).finalize(); + break; + } + + default: { + fprintf (stderr, "%s: invalid JavaScript call\n", __PRETTY_FUNCTION__); + } + } + } + context->result = result; + } + + return result; +} + + +JavaJSObject::JavaJSObject(jlong nativeJSObject) +{ + _imp = jlong_to_impptr(nativeJSObject); + + ASSERT(_imp); + _rootObject = findProtectingRootObject(_imp); + ASSERT(_rootObject); +} + +RootObject* JavaJSObject::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0; +} + +jobject JavaJSObject::call(jstring methodName, jobjectArray args) const +{ + JS_LOG ("methodName = %s\n", JavaString(methodName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + // Lookup the function object. + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(false); + + Identifier identifier(exec, JavaString(methodName)); + JSValue* function = _imp->get(exec, identifier); + CallData callData; + CallType callType = function->getCallData(callData); + if (callType == CallTypeNone) + return 0; + + // Call the function object. + ArgList argList; + getListFromJArray(exec, args, argList); + rootObject->globalObject()->startTimeoutCheck(); + JSValue* result = JSC::call(exec, function, callType, callData, _imp, argList); + rootObject->globalObject()->stopTimeoutCheck(); + + return convertValueToJObject(result); +} + +jobject JavaJSObject::eval(jstring script) const +{ + JS_LOG ("script = %s\n", JavaString(script).UTF8String()); + + JSValue* result; + + JSLock lock(false); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + rootObject->globalObject()->startTimeoutCheck(); + Completion completion = Interpreter::evaluate(rootObject->globalObject()->globalExec(), rootObject->globalObject()->globalScopeChain(), makeSource(JavaString(script))); + rootObject->globalObject()->stopTimeoutCheck(); + ComplType type = completion.complType(); + + if (type == Normal) { + result = completion.value(); + if (!result) + result = jsUndefined(); + } else + result = jsUndefined(); + + return convertValueToJObject (result); +} + +jobject JavaJSObject::getMember(jstring memberName) const +{ + JS_LOG ("(%p) memberName = %s\n", _imp, JavaString(memberName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(false); + JSValue* result = _imp->get(exec, Identifier(exec, JavaString(memberName))); + + return convertValueToJObject(result); +} + +void JavaJSObject::setMember(jstring memberName, jobject value) const +{ + JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName).UTF8String(), value); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(false); + PutPropertySlot slot; + _imp->put(exec, Identifier(exec, JavaString(memberName)), convertJObjectToValue(exec, value), slot); +} + + +void JavaJSObject::removeMember(jstring memberName) const +{ + JS_LOG ("memberName = %s\n", JavaString(memberName).UTF8String()); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(false); + _imp->deleteProperty(exec, Identifier(exec, JavaString(memberName))); +} + + +jobject JavaJSObject::getSlot(jint index) const +{ +#ifdef __LP64__ + JS_LOG ("index = %d\n", index); +#else + JS_LOG ("index = %ld\n", index); +#endif + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + + JSLock lock(false); + JSValue* result = _imp->get(exec, index); + + return convertValueToJObject(result); +} + + +void JavaJSObject::setSlot(jint index, jobject value) const +{ +#ifdef __LP64__ + JS_LOG ("index = %d, value = %p\n", index, value); +#else + JS_LOG ("index = %ld, value = %p\n", index, value); +#endif + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JSLock lock(false); + _imp->put(exec, (unsigned)index, convertJObjectToValue(exec, value)); +} + + +jstring JavaJSObject::toString() const +{ + JS_LOG ("\n"); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + JSLock lock(false); + JSObject *thisObj = const_cast<JSObject*>(_imp); + ExecState* exec = rootObject->globalObject()->globalExec(); + + return (jstring)convertValueToJValue (exec, thisObj, object_type, "java.lang.String").l; +} + +void JavaJSObject::finalize() const +{ + if (RootObject* rootObject = this->rootObject()) + rootObject->gcUnprotect(_imp); +} + +static PassRefPtr<RootObject> createRootObject(void* nativeHandle) +{ + Frame* frame = 0; + for (NSView *view = (NSView *)nativeHandle; view; view = [view superview]) { + if ([view conformsToProtocol:@protocol(WebCoreFrameView)]) { + NSView<WebCoreFrameView> *webCoreFrameView = static_cast<NSView<WebCoreFrameView>*>(view); + frame = [webCoreFrameView _web_frame]; + break; + } + } + if (!frame) + return 0; + return frame->script()->createRootObject(nativeHandle); +} + +// We're either creating a 'Root' object (via a call to JavaJSObject.getWindow()), or +// another JavaJSObject. +jlong JavaJSObject::createNative(jlong nativeHandle) +{ + JS_LOG ("nativeHandle = %d\n", (int)nativeHandle); + + if (nativeHandle == UndefinedHandle) + return nativeHandle; + + if (findProtectingRootObject(jlong_to_impptr(nativeHandle))) + return nativeHandle; + + RefPtr<RootObject> rootObject = createRootObject(jlong_to_ptr(nativeHandle)); + + // If rootObject is !NULL We must have been called via netscape.javascript.JavaJSObject.getWindow(), + // otherwise we are being called after creating a JavaJSObject in + // JavaJSObject::convertValueToJObject(). + if (rootObject) { + JSObject* globalObject = rootObject->globalObject(); + // We call gcProtect here to get the object into the root object's "protect set" which + // is used to test if a native handle is valid as well as getting the root object given the handle. + rootObject->gcProtect(globalObject); + return ptr_to_jlong(globalObject); + } + + return nativeHandle; +} + +jobject JavaJSObject::convertValueToJObject(JSValue* value) const +{ + JSLock lock(false); + + RootObject* rootObject = this->rootObject(); + if (!rootObject) + return 0; + + ExecState* exec = rootObject->globalObject()->globalExec(); + JNIEnv *env = getJNIEnv(); + jobject result = 0; + + // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition', + // figure 22-5. + // number -> java.lang.Double + // string -> java.lang.String + // boolean -> java.lang.Boolean + // Java instance -> Java instance + // Everything else -> JavaJSObject + + if (value->isNumber()) { + jclass JSObjectClass = env->FindClass ("java/lang/Double"); + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(D)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, (jdouble)value->toNumber(exec)); + } + } else if (value->isString()) { + UString stringValue = value->toString(exec); + JNIEnv *env = getJNIEnv(); + result = env->NewString ((const jchar *)stringValue.data(), stringValue.size()); + } else if (value->isBoolean()) { + jclass JSObjectClass = env->FindClass ("java/lang/Boolean"); + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(Z)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, (jboolean)value->toBoolean(exec)); + } + } + else { + // Create a JavaJSObject. + jlong nativeHandle; + + if (value->isObject()) { + JSObject* imp = asObject(value); + + // We either have a wrapper around a Java instance or a JavaScript + // object. If we have a wrapper around a Java instance, return that + // instance, otherwise create a new Java JavaJSObject with the JSObject* + // as it's nativeHandle. + if (imp->classInfo() && strcmp(imp->classInfo()->className, "RuntimeObject") == 0) { + RuntimeObjectImp* runtimeImp = static_cast<RuntimeObjectImp*>(imp); + JavaInstance *runtimeInstance = static_cast<JavaInstance *>(runtimeImp->getInternalInstance()); + if (!runtimeInstance) + return 0; + + return runtimeInstance->javaInstance(); + } + else { + nativeHandle = ptr_to_jlong(imp); + rootObject->gcProtect(imp); + } + } + // All other types will result in an undefined object. + else { + nativeHandle = UndefinedHandle; + } + + // Now create the Java JavaJSObject. Look for the JavaJSObject in it's new (Tiger) + // location and in the original Java 1.4.2 location. + jclass JSObjectClass; + + JSObjectClass = env->FindClass ("sun/plugin/javascript/webkit/JSObject"); + if (!JSObjectClass) { + env->ExceptionDescribe(); + env->ExceptionClear(); + JSObjectClass = env->FindClass ("apple/applet/JSObject"); + } + + jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(J)V"); + if (constructorID != NULL) { + result = env->NewObject (JSObjectClass, constructorID, nativeHandle); + } + } + + return result; +} + +JSValue* JavaJSObject::convertJObjectToValue(ExecState* exec, jobject theObject) const +{ + // Instances of netscape.javascript.JSObject get converted back to + // JavaScript objects. All other objects are wrapped. It's not + // possible to pass primitive types from the Java to JavaScript. + // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition', + // figure 22-4. + jobject classOfInstance = callJNIMethod<jobject>(theObject, "getClass", "()Ljava/lang/Class;"); + jstring className = (jstring)callJNIMethod<jobject>(classOfInstance, "getName", "()Ljava/lang/String;"); + + // Only the sun.plugin.javascript.webkit.JSObject has a member called nativeJSObject. This class is + // created above to wrap internal browser objects. The constructor of this class takes the native + // pointer and stores it in this object, so that it can be retrieved below. + if (strcmp(JavaString(className).UTF8String(), "sun.plugin.javascript.webkit.JSObject") == 0) { + // Pull the nativeJSObject value from the Java instance. This is a + // pointer to the JSObject. + JNIEnv *env = getJNIEnv(); + jfieldID fieldID = env->GetFieldID((jclass)classOfInstance, "nativeJSObject", "J"); + if (fieldID == NULL) { + return jsUndefined(); + } + jlong nativeHandle = env->GetLongField(theObject, fieldID); + if (nativeHandle == UndefinedHandle) { + return jsUndefined(); + } + JSObject *imp = static_cast<JSObject*>(jlong_to_impptr(nativeHandle)); + return imp; + } + + JSLock lock(false); + + return JSC::Bindings::Instance::createRuntimeObject(exec, JavaInstance::create(theObject, _rootObject)); +} + +void JavaJSObject::getListFromJArray(ExecState* exec, jobjectArray jArray, ArgList& list) const +{ + JNIEnv *env = getJNIEnv(); + int numObjects = jArray ? env->GetArrayLength(jArray) : 0; + + for (int i = 0; i < numObjects; i++) { + jobject anObject = env->GetObjectArrayElement ((jobjectArray)jArray, i); + if (anObject) { + list.append(convertJObjectToValue(exec, anObject)); + env->DeleteLocalRef (anObject); + } + else { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } +} + +extern "C" { + +jlong KJS_JSCreateNativeJSObject (JNIEnv*, jclass, jstring, jlong nativeHandle, jboolean) +{ + JSObjectCallContext context; + context.type = CreateNative; + context.nativeHandle = nativeHandle; + return JavaJSObject::invoke (&context).j; +} + +void KJS_JSObject_JSFinalize (JNIEnv*, jclass, jlong nativeHandle) +{ + JSObjectCallContext context; + context.type = Finalize; + context.nativeHandle = nativeHandle; + JavaJSObject::invoke (&context); +} + +jobject KJS_JSObject_JSObjectCall (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring methodName, jobjectArray args, jboolean) +{ + JSObjectCallContext context; + context.type = Call; + context.nativeHandle = nativeHandle; + context.string = methodName; + context.args = args; + return JavaJSObject::invoke (&context).l; +} + +jobject KJS_JSObject_JSObjectEval (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jscript, jboolean) +{ + JSObjectCallContext context; + context.type = Eval; + context.nativeHandle = nativeHandle; + context.string = jscript; + return JavaJSObject::invoke (&context).l; +} + +jobject KJS_JSObject_JSObjectGetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean) +{ + JSObjectCallContext context; + context.type = GetMember; + context.nativeHandle = nativeHandle; + context.string = jname; + return JavaJSObject::invoke (&context).l; +} + +void KJS_JSObject_JSObjectSetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jobject value, jboolean) +{ + JSObjectCallContext context; + context.type = SetMember; + context.nativeHandle = nativeHandle; + context.string = jname; + context.value = value; + JavaJSObject::invoke (&context); +} + +void KJS_JSObject_JSObjectRemoveMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean) +{ + JSObjectCallContext context; + context.type = RemoveMember; + context.nativeHandle = nativeHandle; + context.string = jname; + JavaJSObject::invoke (&context); +} + +jobject KJS_JSObject_JSObjectGetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jboolean) +{ + JSObjectCallContext context; + context.type = GetSlot; + context.nativeHandle = nativeHandle; + context.index = jindex; + return JavaJSObject::invoke (&context).l; +} + +void KJS_JSObject_JSObjectSetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jobject value, jboolean) +{ + JSObjectCallContext context; + context.type = SetSlot; + context.nativeHandle = nativeHandle; + context.index = jindex; + context.value = value; + JavaJSObject::invoke (&context); +} + +jstring KJS_JSObject_JSObjectToString (JNIEnv*, jclass, jlong nativeHandle) +{ + JSObjectCallContext context; + context.type = ToString; + context.nativeHandle = nativeHandle; + return (jstring)JavaJSObject::invoke (&context).l; +} + +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_objc.mm b/WebCore/bridge/jni/jni_objc.mm new file mode 100644 index 0000000..45ee9f5 --- /dev/null +++ b/WebCore/bridge/jni/jni_objc.mm @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2004 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" + +#if ENABLE(MAC_JAVA_BRIDGE) + +#import <Foundation/Foundation.h> +#import "jni_utility.h" +#import "objc_utility.h" +#include <runtime/JSLock.h> + +using namespace JSC::Bindings; + +@interface NSObject (WebScriptingPrivate) +- (jvalue)webPlugInCallJava:(jobject)object method:(jmethodID)method returnType:(JNIType)returnType arguments:(jvalue*)args; +- (jvalue)webPlugInCallJava:(jobject)object + isStatic:(BOOL)isStatic + returnType:(JNIType)returnType + method:(jmethodID)method + arguments:(jvalue*)args + callingURL:(NSURL *)url + exceptionDescription:(NSString **)exceptionString; +@end + +bool JSC::Bindings::dispatchJNICall(ExecState* exec, const void* targetAppletView, jobject obj, bool isStatic, JNIType returnType, jmethodID methodID, jvalue* args, jvalue &result, const char*, JSValue*& exceptionDescription) +{ + id view = (id)targetAppletView; + + // As array_type is not known by the Mac JVM, change it to a compatible type. + if (returnType == array_type) + returnType = object_type; + + if ([view respondsToSelector:@selector(webPlugInCallJava:isStatic:returnType:method:arguments:callingURL:exceptionDescription:)]) { + NSString *_exceptionDescription = 0; + + // Passing nil as the calling URL will cause the Java plugin to use the URL + // of the page that contains the applet. The execution restrictions + // implemented in WebCore will guarantee that only appropriate JavaScript + // can reference the applet. + { + JSLock::DropAllLocks dropAllLocks(false); + result = [view webPlugInCallJava:obj isStatic:isStatic returnType:returnType method:methodID arguments:args callingURL:nil exceptionDescription:&_exceptionDescription]; + } + + if (_exceptionDescription != 0) { + exceptionDescription = convertNSStringToString(exec, _exceptionDescription); + } + return true; + } + else if ([view respondsToSelector:@selector(webPlugInCallJava:method:returnType:arguments:)]) { + JSLock::DropAllLocks dropAllLocks(false); + result = [view webPlugInCallJava:obj method:methodID returnType:returnType arguments:args]; + return true; + } + + bzero (&result, sizeof(jvalue)); + return false; +} + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_runtime.cpp b/WebCore/bridge/jni/jni_runtime.cpp new file mode 100644 index 0000000..3a9b950 --- /dev/null +++ b/WebCore/bridge/jni/jni_runtime.cpp @@ -0,0 +1,547 @@ +/* + * 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 <jni_runtime.h> + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <jni_utility.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; + + +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->_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 = noValue(); + 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 = Instance::createRuntimeObject(exec, JavaInstance::create(anObject, instance->rootObject())); + } + } + 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", name(), 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->_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 = noValue(); + 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", name(), 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); +} + +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(UString& aString, const char* className) +{ + ASSERT(JSLock::lockCount() > 0); + + char *result, *cp = strdup(className); + + result = cp; + while (*cp) { + if (*cp == '.') + *cp = '/'; + cp++; + } + + aString.append(result); + + free (result); +} + +const char *JavaMethod::signature() const +{ + if (!_signature) { + JSLock lock(false); + + UString signatureBuilder("("); + 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(";"); + } + } + + _signature = strdup(signatureBuilder.ascii()); + } + + return _signature; +} + +JNIType JavaMethod::JNIReturnType() const +{ + return _JNIReturnType; +} + +jmethodID JavaMethod::methodID (jobject obj) const +{ + if (_methodID == 0) { + _methodID = getMethodID (obj, name(), 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->_instance); + _type = strdup(type); + _rootObject = rootObject; +} + +JavaArray::~JavaArray () +{ + free ((void *)_type); +} + +RootObject* JavaArray::rootObject() const +{ + return _rootObject && _rootObject->isValid() ? _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 Instance::createRuntimeObject(exec, JavaInstance::create(anObject, rootObject())); + } + + 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) diff --git a/WebCore/bridge/jni/jni_runtime.h b/WebCore/bridge/jni/jni_runtime.h new file mode 100644 index 0000000..67bc06e --- /dev/null +++ b/WebCore/bridge/jni/jni_runtime.h @@ -0,0 +1,191 @@ +/* + * 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. + */ + +#ifndef _JNI_RUNTIME_H_ +#define _JNI_RUNTIME_H_ + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <jni_utility.h> +#include <jni_instance.h> +#include <runtime/JSLock.h> + + +namespace JSC +{ + +namespace Bindings +{ + +typedef const char* RuntimeType; + +class JavaString +{ +public: + JavaString() + { + JSLock lock(false); + _rep = UString().rep(); + } + + void _commonInit (JNIEnv *e, jstring s) + { + int _size = e->GetStringLength (s); + const jchar *uc = getUCharactersFromJStringInEnv (e, s); + { + JSLock lock(false); + _rep = UString((UChar *)uc,_size).rep(); + } + releaseUCharactersForJStringInEnv (e, s, uc); + } + + JavaString (JNIEnv *e, jstring s) { + _commonInit (e, s); + } + + JavaString (jstring s) { + _commonInit (getJNIEnv(), s); + } + + ~JavaString() + { + JSLock lock(false); + _rep = 0; + } + + const char *UTF8String() const { + if (_utf8String.c_str() == 0) { + JSLock lock(false); + _utf8String = UString(_rep).UTF8String(); + } + return _utf8String.c_str(); + } + const jchar *uchars() const { return (const jchar *)_rep->data(); } + int length() const { return _rep->size(); } + operator UString() const { return UString(_rep); } + +private: + RefPtr<UString::Rep> _rep; + mutable CString _utf8String; +}; + +class JavaParameter +{ +public: + JavaParameter () : _JNIType(invalid_type) {}; + JavaParameter (JNIEnv *env, jstring type); + virtual ~JavaParameter() { } + + RuntimeType type() const { return _type.UTF8String(); } + JNIType getJNIType() const { return _JNIType; } + +private: + JavaString _type; + JNIType _JNIType; +}; + + +class JavaField : public Field +{ +public: + JavaField (JNIEnv *env, jobject aField); + + virtual JSValue* valueFromInstance(ExecState *exec, const Instance *instance) const; + virtual void setValueToInstance(ExecState *exec, const Instance *instance, JSValue* aValue) const; + + virtual const char *name() const { return _name.UTF8String(); } + virtual RuntimeType type() const { return _type.UTF8String(); } + + JNIType getJNIType() const { return _JNIType; } + +private: + void dispatchSetValueToInstance(ExecState *exec, const JavaInstance *instance, jvalue javaValue, const char *name, const char *sig) const; + jvalue dispatchValueFromInstance(ExecState *exec, const JavaInstance *instance, const char *name, const char *sig, JNIType returnType) const; + + JavaString _name; + JavaString _type; + JNIType _JNIType; + RefPtr<JObjectWrapper> _field; +}; + + +class JavaMethod : public Method +{ +public: + JavaMethod(JNIEnv* env, jobject aMethod); + ~JavaMethod(); + + virtual const char *name() const { return _name.UTF8String(); }; + RuntimeType returnType() const { return _returnType.UTF8String(); }; + JavaParameter* parameterAt(int i) const { return &_parameters[i]; }; + int numParameters() const { return _numParameters; }; + + const char *signature() const; + JNIType JNIReturnType() const; + + jmethodID methodID (jobject obj) const; + + bool isStatic() const { return _isStatic; } + +private: + JavaParameter* _parameters; + int _numParameters; + JavaString _name; + mutable char* _signature; + JavaString _returnType; + JNIType _JNIReturnType; + mutable jmethodID _methodID; + bool _isStatic; +}; + +class JavaArray : public Array +{ +public: + JavaArray(jobject array, const char* type, PassRefPtr<RootObject>); + virtual ~JavaArray(); + + RootObject* rootObject() const; + + virtual void setValueAt(ExecState *exec, unsigned int index, JSValue* aValue) const; + virtual JSValue* valueAt(ExecState *exec, unsigned int index) const; + virtual unsigned int getLength() const; + + jobject javaArray() const { return _array->_instance; } + + static JSValue* convertJObjectToArray (ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject>); + +private: + RefPtr<JObjectWrapper> _array; + unsigned int _length; + const char *_type; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) + +#endif // _JNI_RUNTIME_H_ diff --git a/WebCore/bridge/jni/jni_utility.cpp b/WebCore/bridge/jni/jni_utility.cpp new file mode 100644 index 0000000..0fe7174 --- /dev/null +++ b/WebCore/bridge/jni/jni_utility.cpp @@ -0,0 +1,584 @@ +/* + * 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 "jni_utility.h" + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include "jni_runtime.h" +#include "runtime_array.h" +#include "runtime_object.h" +#include <runtime/JSArray.h> +#include <runtime/JSLock.h> +#include <dlfcn.h> + +namespace JSC { + +namespace Bindings { + +static jint KJS_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) +{ + static void* javaVMFramework = 0; + if (!javaVMFramework) + javaVMFramework = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM", RTLD_LAZY); + if (!javaVMFramework) + return JNI_ERR; + + static jint(*functionPointer)(JavaVM**, jsize, jsize *) = 0; + if (!functionPointer) + functionPointer = (jint(*)(JavaVM**, jsize, jsize *))dlsym(javaVMFramework, "JNI_GetCreatedJavaVMs"); + if (!functionPointer) + return JNI_ERR; + return functionPointer(vmBuf, bufLen, nVMs); +} + +static JavaVM *jvm = 0; + +// Provide the ability for an outside component to specify the JavaVM to use +// If the jvm value is set, the getJavaVM function below will just return. +// In getJNIEnv(), if AttachCurrentThread is called to a VM that is already +// attached, the result is a no-op. +void setJavaVM(JavaVM *javaVM) +{ + jvm = javaVM; +} + +JavaVM *getJavaVM() +{ + if (jvm) + return jvm; + + JavaVM *jvmArray[1]; + jsize bufLen = 1; + jsize nJVMs = 0; + jint jniError = 0; + + // Assumes JVM is already running ..., one per process + jniError = KJS_GetCreatedJavaVMs(jvmArray, bufLen, &nJVMs); + if ( jniError == JNI_OK && nJVMs > 0 ) { + jvm = jvmArray[0]; + } + else + fprintf(stderr, "%s: JNI_GetCreatedJavaVMs failed, returned %ld\n", __PRETTY_FUNCTION__, (long)jniError); + + return jvm; +} + +JNIEnv* getJNIEnv() +{ + union { + JNIEnv* env; + void* dummy; + } u; + jint jniError = 0; + + jniError = (getJavaVM())->AttachCurrentThread(&u.dummy, NULL); + if (jniError == JNI_OK) + return u.env; + else + fprintf(stderr, "%s: AttachCurrentThread failed, returned %ld\n", __PRETTY_FUNCTION__, (long)jniError); + return NULL; +} + +jmethodID getMethodID (jobject obj, const char *name, const char *sig) +{ + JNIEnv *env = getJNIEnv(); + jmethodID mid = 0; + + if ( env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + mid = env->GetMethodID(cls, name, sig); + if (!mid) { + env->ExceptionClear(); + mid = env->GetStaticMethodID(cls, name, sig); + if (!mid) { + env->ExceptionClear(); + } + } + } + env->DeleteLocalRef(cls); + } + return mid; +} + +const char *getCharactersFromJString (jstring aJString) +{ + return getCharactersFromJStringInEnv (getJNIEnv(), aJString); +} + +void releaseCharactersForJString (jstring aJString, const char *s) +{ + releaseCharactersForJStringInEnv (getJNIEnv(), aJString, s); +} + +const char *getCharactersFromJStringInEnv (JNIEnv *env, jstring aJString) +{ + jboolean isCopy; + const char *s = env->GetStringUTFChars((jstring)aJString, &isCopy); + if (!s) { + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + return s; +} + +void releaseCharactersForJStringInEnv (JNIEnv *env, jstring aJString, const char *s) +{ + env->ReleaseStringUTFChars (aJString, s); +} + +const jchar *getUCharactersFromJStringInEnv (JNIEnv *env, jstring aJString) +{ + jboolean isCopy; + const jchar *s = env->GetStringChars((jstring)aJString, &isCopy); + if (!s) { + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + return s; +} + +void releaseUCharactersForJStringInEnv (JNIEnv *env, jstring aJString, const jchar *s) +{ + env->ReleaseStringChars (aJString, s); +} + +JNIType JNITypeFromClassName(const char *name) +{ + JNIType type; + + if (strcmp("byte",name) == 0) + type = byte_type; + else if (strcmp("short",name) == 0) + type = short_type; + else if (strcmp("int",name) == 0) + type = int_type; + else if (strcmp("long",name) == 0) + type = long_type; + else if (strcmp("float",name) == 0) + type = float_type; + else if (strcmp("double",name) == 0) + type = double_type; + else if (strcmp("char",name) == 0) + type = char_type; + else if (strcmp("boolean",name) == 0) + type = boolean_type; + else if (strcmp("void",name) == 0) + type = void_type; + else if ('[' == name[0]) + type = array_type; + else + type = object_type; + + return type; +} + +const char *signatureFromPrimitiveType(JNIType type) +{ + switch (type){ + case void_type: + return "V"; + + case array_type: + return "["; + + case object_type: + return "L"; + + case boolean_type: + return "Z"; + + case byte_type: + return "B"; + + case char_type: + return "C"; + + case short_type: + return "S"; + + case int_type: + return "I"; + + case long_type: + return "J"; + + case float_type: + return "F"; + + case double_type: + return "D"; + + case invalid_type: + default: + break; + } + return ""; +} + +JNIType JNITypeFromPrimitiveType(char type) +{ + switch (type){ + case 'V': + return void_type; + + case 'L': + return object_type; + + case '[': + return array_type; + + case 'Z': + return boolean_type; + + case 'B': + return byte_type; + + case 'C': + return char_type; + + case 'S': + return short_type; + + case 'I': + return int_type; + + case 'J': + return long_type; + + case 'F': + return float_type; + + case 'D': + return double_type; + + default: + break; + } + return invalid_type; +} + +jvalue getJNIField( jobject obj, JNIType type, const char *name, const char *signature) +{ + JavaVM *jvm = getJavaVM(); + JNIEnv *env = getJNIEnv(); + jvalue result; + + bzero (&result, sizeof(jvalue)); + if ( obj != NULL && jvm != NULL && env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + jfieldID field = env->GetFieldID(cls, name, signature); + if ( field != NULL ) { + switch (type) { + case array_type: + case object_type: + result.l = env->functions->GetObjectField(env, obj, field); + break; + case boolean_type: + result.z = env->functions->GetBooleanField(env, obj, field); + break; + case byte_type: + result.b = env->functions->GetByteField(env, obj, field); + break; + case char_type: + result.c = env->functions->GetCharField(env, obj, field); + break; + case short_type: + result.s = env->functions->GetShortField(env, obj, field); + break; + case int_type: + result.i = env->functions->GetIntField(env, obj, field); + break; + case long_type: + result.j = env->functions->GetLongField(env, obj, field); + break; + case float_type: + result.f = env->functions->GetFloatField(env, obj, field); + break; + case double_type: + result.d = env->functions->GetDoubleField(env, obj, field); + break; + default: + fprintf(stderr, "%s: invalid field type (%d)\n", __PRETTY_FUNCTION__, (int)type); + } + } + else + { + fprintf(stderr, "%s: Could not find field: %s\n", __PRETTY_FUNCTION__, name); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + + env->DeleteLocalRef(cls); + } + else { + fprintf(stderr, "%s: Could not find class for object\n", __PRETTY_FUNCTION__); + } + } + + return result; +} + +static jobject convertArrayInstanceToJavaArray(ExecState* exec, JSArray* jsArray, const char* javaClassName) +{ + JNIEnv *env = getJNIEnv(); + // As JS Arrays can contain a mixture of objects, assume we can convert to + // the requested Java Array type requested, unless the array type is some object array + // other than a string. + unsigned length = jsArray->length(); + jobjectArray jarray = 0; + + // Build the correct array type + switch (JNITypeFromPrimitiveType(javaClassName[1])) { + case object_type: { + // Only support string object types + if (0 == strcmp("[Ljava.lang.String;", javaClassName)) { + jarray = (jobjectArray)env->NewObjectArray(length, + env->FindClass("java/lang/String"), + env->NewStringUTF("")); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + UString stringValue = item->toString(exec); + env->SetObjectArrayElement(jarray,i, + env->functions->NewString(env, (const jchar *)stringValue.data(), stringValue.size())); + } + } + break; + } + + case boolean_type: { + jarray = (jobjectArray)env->NewBooleanArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + jboolean value = (jboolean)item->toNumber(exec); + env->SetBooleanArrayRegion((jbooleanArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case byte_type: { + jarray = (jobjectArray)env->NewByteArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + jbyte value = (jbyte)item->toNumber(exec); + env->SetByteArrayRegion((jbyteArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case char_type: { + jarray = (jobjectArray)env->NewCharArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + UString stringValue = item->toString(exec); + jchar value = 0; + if (stringValue.size() > 0) + value = ((const jchar*)stringValue.data())[0]; + env->SetCharArrayRegion((jcharArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case short_type: { + jarray = (jobjectArray)env->NewShortArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + jshort value = (jshort)item->toNumber(exec); + env->SetShortArrayRegion((jshortArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case int_type: { + jarray = (jobjectArray)env->NewIntArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + jint value = (jint)item->toNumber(exec); + env->SetIntArrayRegion((jintArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case long_type: { + jarray = (jobjectArray)env->NewLongArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + jlong value = (jlong)item->toNumber(exec); + env->SetLongArrayRegion((jlongArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case float_type: { + jarray = (jobjectArray)env->NewFloatArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + jfloat value = (jfloat)item->toNumber(exec); + env->SetFloatArrayRegion((jfloatArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case double_type: { + jarray = (jobjectArray)env->NewDoubleArray(length); + for(unsigned i = 0; i < length; i++) { + JSValue* item = jsArray->get(exec, i); + jdouble value = (jdouble)item->toNumber(exec); + env->SetDoubleArrayRegion((jdoubleArray)jarray, (jsize)i, (jsize)1, &value); + } + break; + } + + case array_type: // don't handle embedded arrays + case void_type: // Don't expect arrays of void objects + case invalid_type: // Array of unknown objects + break; + } + + // if it was not one of the cases handled, then null is returned + return jarray; +} + + +jvalue convertValueToJValue(ExecState* exec, JSValue* value, JNIType _JNIType, const char* javaClassName) +{ + JSLock lock(false); + + jvalue result; + + switch (_JNIType){ + case array_type: + case object_type: { + result.l = (jobject)0; + + // First see if we have a Java instance. + if (value->isObject()){ + JSObject* objectImp = asObject(value); + if (objectImp->classInfo() == &RuntimeObjectImp::s_info) { + RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(objectImp); + JavaInstance *instance = static_cast<JavaInstance*>(imp->getInternalInstance()); + if (instance) + result.l = instance->javaInstance(); + } + else if (objectImp->classInfo() == &RuntimeArray::s_info) { + // Input is a JavaScript Array that was originally created from a Java Array + RuntimeArray* imp = static_cast<RuntimeArray*>(objectImp); + JavaArray *array = static_cast<JavaArray*>(imp->getConcreteArray()); + result.l = array->javaArray(); + } + else if (objectImp->classInfo() == &JSArray::info) { + // Input is a Javascript Array. We need to create it to a Java Array. + result.l = convertArrayInstanceToJavaArray(exec, asArray(value), javaClassName); + } + } + + // Now convert value to a string if the target type is a java.lang.string, and we're not + // converting from a Null. + if (result.l == 0 && strcmp(javaClassName, "java.lang.String") == 0) { +#ifdef CONVERT_NULL_TO_EMPTY_STRING + if (value->isNull()) { + JNIEnv *env = getJNIEnv(); + jchar buf[2]; + jobject javaString = env->functions->NewString (env, buf, 0); + result.l = javaString; + } + else +#else + if (!value->isNull()) +#endif + { + UString stringValue = value->toString(exec); + JNIEnv *env = getJNIEnv(); + jobject javaString = env->functions->NewString (env, (const jchar *)stringValue.data(), stringValue.size()); + result.l = javaString; + } + } else if (result.l == 0) + bzero (&result, sizeof(jvalue)); // Handle it the same as a void case + } + break; + + case boolean_type: { + result.z = (jboolean)value->toNumber(exec); + } + break; + + case byte_type: { + result.b = (jbyte)value->toNumber(exec); + } + break; + + case char_type: { + result.c = (jchar)value->toNumber(exec); + } + break; + + case short_type: { + result.s = (jshort)value->toNumber(exec); + } + break; + + case int_type: { + result.i = (jint)value->toNumber(exec); + } + break; + + case long_type: { + result.j = (jlong)value->toNumber(exec); + } + break; + + case float_type: { + result.f = (jfloat)value->toNumber(exec); + } + break; + + case double_type: { + result.d = (jdouble)value->toNumber(exec); + } + break; + + break; + + case invalid_type: + default: + case void_type: { + bzero (&result, sizeof(jvalue)); + } + break; + } + return result; +} + +} // end of namespace Bindings + +} // end of namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) diff --git a/WebCore/bridge/jni/jni_utility.h b/WebCore/bridge/jni/jni_utility.h new file mode 100644 index 0000000..b30c654 --- /dev/null +++ b/WebCore/bridge/jni/jni_utility.h @@ -0,0 +1,288 @@ +/* + * 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. + */ + +#ifndef _JNI_UTILITY_H_ +#define _JNI_UTILITY_H_ + +#if ENABLE(MAC_JAVA_BRIDGE) + +#include <runtime/JSValue.h> +#include <JavaVM/jni.h> + +// The order of these items can not be modified as they are tightly +// bound with the JVM on Mac OSX. If new types need to be added, they +// should be added to the end. It is used in jni_obc.mm when calling +// through to the JVM. Newly added items need to be made compatible +// in that file. +typedef enum { + invalid_type = 0, + void_type, + object_type, + boolean_type, + byte_type, + char_type, + short_type, + int_type, + long_type, + float_type, + double_type, + array_type +} JNIType; + +namespace JSC { + +class ExecState; +class JSObject; + +namespace Bindings { + +class JavaParameter; + +const char *getCharactersFromJString(jstring aJString); +void releaseCharactersForJString(jstring aJString, const char *s); + +const char *getCharactersFromJStringInEnv(JNIEnv *env, jstring aJString); +void releaseCharactersForJStringInEnv(JNIEnv *env, jstring aJString, const char *s); +const jchar *getUCharactersFromJStringInEnv(JNIEnv *env, jstring aJString); +void releaseUCharactersForJStringInEnv(JNIEnv *env, jstring aJString, const jchar *s); + +JNIType JNITypeFromClassName(const char *name); +JNIType JNITypeFromPrimitiveType(char type); +const char *signatureFromPrimitiveType(JNIType type); + +jvalue convertValueToJValue(ExecState*, JSValue*, JNIType, const char* javaClassName); + +jvalue getJNIField(jobject obj, JNIType type, const char *name, const char *signature); + +jmethodID getMethodID(jobject obj, const char *name, const char *sig); +JNIEnv* getJNIEnv(); +JavaVM* getJavaVM(); +void setJavaVM(JavaVM*); + + +template <typename T> struct JNICaller; + +template<> struct JNICaller<void> { + static void callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallVoidMethodA(obj, mid, args); + } + static void callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallVoidMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jobject> { + static jobject callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallObjectMethodA(obj, mid, args); + } + static jobject callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallObjectMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jboolean> { + static jboolean callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallBooleanMethodA(obj, mid, args); + } + static jboolean callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallBooleanMethodV(obj, mid, args); + } + static jboolean callStaticV(jclass cls, jmethodID mid, va_list args) + { + return getJNIEnv()->CallStaticBooleanMethod(cls, mid, args); + } + +}; + +template<> struct JNICaller<jbyte> { + static jbyte callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallByteMethodA(obj, mid, args); + } + static jbyte callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallByteMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jchar> { + static jchar callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallCharMethodA(obj, mid, args); + } + static jchar callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallCharMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jshort> { + static jshort callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallShortMethodA(obj, mid, args); + } + static jshort callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallShortMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jint> { + static jint callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallIntMethodA(obj, mid, args); + } + static jint callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallIntMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jlong> { + static jlong callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallLongMethodA(obj, mid, args); + } + static jlong callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallLongMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jfloat> { + static jfloat callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallFloatMethodA(obj, mid, args); + } + static jfloat callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallFloatMethodV(obj, mid, args); + } +}; + +template<> struct JNICaller<jdouble> { + static jdouble callA(jobject obj, jmethodID mid, jvalue* args) + { + return getJNIEnv()->CallDoubleMethodA(obj, mid, args); + } + static jdouble callV(jobject obj, jmethodID mid, va_list args) + { + return getJNIEnv()->CallDoubleMethodV(obj, mid, args); + } +}; + +template<typename T> T callJNIMethodIDA(jobject obj, jmethodID mid, jvalue *args) +{ + return JNICaller<T>::callA(obj, mid, args); +} + +template<typename T> +static T callJNIMethodV(jobject obj, const char *name, const char *sig, va_list args) +{ + JavaVM *jvm = getJavaVM(); + JNIEnv *env = getJNIEnv(); + + if ( obj != NULL && jvm != NULL && env != NULL) { + jclass cls = env->GetObjectClass(obj); + if ( cls != NULL ) { + jmethodID mid = env->GetMethodID(cls, name, sig); + if ( mid != NULL ) + { + return JNICaller<T>::callV(obj, mid, args); + } + else + { + fprintf(stderr, "%s: Could not find method: %s for %p\n", __PRETTY_FUNCTION__, name, obj); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + + env->DeleteLocalRef(cls); + } + else { + fprintf(stderr, "%s: Could not find class for %p\n", __PRETTY_FUNCTION__, obj); + } + } + + return 0; +} + +template<typename T> +T callJNIMethod(jobject obj, const char* methodName, const char* methodSignature, ...) +{ + va_list args; + va_start(args, methodSignature); + + T result= callJNIMethodV<T>(obj, methodName, methodSignature, args); + + va_end(args); + + return result; +} + +template<typename T> +T callJNIStaticMethod(jclass cls, const char* methodName, const char* methodSignature, ...) +{ + JavaVM *jvm = getJavaVM(); + JNIEnv *env = getJNIEnv(); + va_list args; + + va_start(args, methodSignature); + + T result = 0; + + if (cls != NULL && jvm != NULL && env != NULL) { + jmethodID mid = env->GetStaticMethodID(cls, methodName, methodSignature); + if (mid != NULL) + result = JNICaller<T>::callStaticV(cls, mid, args); + else { + fprintf(stderr, "%s: Could not find method: %s for %p\n", __PRETTY_FUNCTION__, methodName, cls); + env->ExceptionDescribe(); + env->ExceptionClear(); + fprintf (stderr, "\n"); + } + } + + va_end(args); + + return result; +} + +bool dispatchJNICall(ExecState*, const void* targetAppletView, jobject obj, bool isStatic, JNIType returnType, jmethodID methodID, jvalue* args, jvalue& result, const char* callingURL, JSValue*& exceptionDescription); + +} // namespace Bindings + +} // namespace JSC + +#endif // ENABLE(MAC_JAVA_BRIDGE) + +#endif // _JNI_UTILITY_H_ |