#if BINDINGS_JAVA
#include "config.h"
#include "identifier.h"
#include "internal.h"
#include "interpreter.h"
#include "list.h"
#include "jni_jsobject.h"
#include "jni_runtime.h"
#include "jni_utility.h"
#include "runtime_object.h"
#include "runtime_root.h"
#include <CoreFoundation/CoreFoundation.h>
#include <assert.h>
using namespace KJS::Bindings;
using namespace KJS;
#ifdef NDEBUG
#define JS_LOG(formatAndArgs...) ((void)0)
#else
#define JS_LOG(formatAndArgs...) { \
fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, RootObject::runLoop(), CFRunLoopGetCurrent()); \
fprintf(stderr, formatAndArgs); \
}
#endif
#define UndefinedHandle 1
static bool isJavaScriptThread()
{
return (RootObject::runLoop() == CFRunLoopGetCurrent());
}
jvalue JavaJSObject::invoke (JSObjectCallContext *context)
{
jvalue result;
bzero ((void *)&result, sizeof(jvalue));
if (!isJavaScriptThread()) {
RootObject::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 (!rootForImp(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: {
JSObject *imp = jlong_to_impptr(nativeHandle);
if (findReferenceDictionary(imp) == 0) {
JS_LOG ("finalize called on instance we have already removed.\n");
}
else {
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 != 0);
_root = rootForImp(_imp);
assert (_root != 0);
}
jobject JavaJSObject::call(jstring methodName, jobjectArray args) const
{
JS_LOG ("methodName = %s\n", JavaString(methodName).UTF8String());
ExecState *exec = _root->interpreter()->globalExec();
JSLock lock;
Identifier identifier(JavaString(methodName).ustring());
JSValue *func = _imp->get (exec, identifier);
if (func->isUndefinedOrNull()) {
return 0;
}
JSObject *funcImp = static_cast<JSObject*>(func);
JSObject *thisObj = const_cast<JSObject*>(_imp);
List argList = listFromJArray(args);
JSValue *result = funcImp->call(exec, thisObj, argList);
return convertValueToJObject(result);
}
jobject JavaJSObject::eval(jstring script) const
{
JS_LOG ("script = %s\n", JavaString(script).UTF8String());
JSObject *thisObj = const_cast<JSObject*>(_imp);
JSValue *result;
JSLock lock;
Completion completion = _root->interpreter()->evaluate(UString(), 0, JavaString(script).ustring(),thisObj);
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());
ExecState *exec = _root->interpreter()->globalExec();
JSLock lock;
JSValue *result = _imp->get (exec, Identifier (JavaString(memberName).ustring()));
return convertValueToJObject(result);
}
void JavaJSObject::setMember(jstring memberName, jobject value) const
{
JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName).UTF8String(), value);
ExecState *exec = _root->interpreter()->globalExec();
JSLock lock;
_imp->put(exec, Identifier (JavaString(memberName).ustring()), convertJObjectToValue(value));
}
void JavaJSObject::removeMember(jstring memberName) const
{
JS_LOG ("memberName = %s\n", JavaString(memberName).UTF8String());
ExecState *exec = _root->interpreter()->globalExec();
JSLock lock;
_imp->deleteProperty(exec, Identifier (JavaString(memberName).ustring()));
}
jobject JavaJSObject::getSlot(jint index) const
{
#if __LP64__
JS_LOG ("index = %d\n", index);
#else
JS_LOG ("index = %ld\n", index);
#endif
ExecState *exec = _root->interpreter()->globalExec();
JSLock lock;
JSValue *result = _imp->get (exec, (unsigned)index);
return convertValueToJObject(result);
}
void JavaJSObject::setSlot(jint index, jobject value) const
{
#if __LP64__
JS_LOG ("index = %d, value = %p\n", index, value);
#else
JS_LOG ("index = %ld, value = %p\n", index, value);
#endif
ExecState *exec = _root->interpreter()->globalExec();
JSLock lock;
_imp->put(exec, (unsigned)index, convertJObjectToValue(value));
}
jstring JavaJSObject::toString() const
{
JS_LOG ("\n");
JSLock lock;
JSObject *thisObj = const_cast<JSObject*>(_imp);
ExecState *exec = _root->interpreter()->globalExec();
return (jstring)convertValueToJValue (exec, thisObj, object_type, "java.lang.String").l;
}
void JavaJSObject::finalize() const
{
JS_LOG ("\n");
removeNativeReference (_imp);
}
jlong JavaJSObject::createNative(jlong nativeHandle)
{
JS_LOG ("nativeHandle = %d\n", (int)nativeHandle);
if (nativeHandle == UndefinedHandle)
return nativeHandle;
else if (rootForImp(jlong_to_impptr(nativeHandle))){
return nativeHandle;
}
FindRootObjectForNativeHandleFunctionPtr aFunc = RootObject::findRootObjectForNativeHandleFunction();
if (aFunc) {
Bindings::RootObject *root = aFunc(jlong_to_ptr(nativeHandle));
if (root) {
addNativeReference (root, root->rootObjectImp());
return ptr_to_jlong(root->rootObjectImp());
}
else {
return nativeHandle;
}
}
return ptr_to_jlong(0);
}
jobject JavaJSObject::convertValueToJObject (JSValue *value) const
{
ExecState *exec = _root->interpreter()->globalExec();
JNIEnv *env = getJNIEnv();
jobject result = 0;
JSType type = value->type();
if (type == NumberType) {
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 (type == StringType) {
UString stringValue = value->toString(exec);
JNIEnv *env = getJNIEnv();
result = env->NewString ((const jchar *)stringValue.data(), stringValue.size());
}
else if (type == BooleanType) {
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 {
jlong nativeHandle;
if (type == ObjectType){
JSObject *imp = static_cast<JSObject*>(value);
if (imp->classInfo() && strcmp(imp->classInfo()->className, "RuntimeObject") == 0) {
RuntimeObjectImp *runtimeImp = static_cast<RuntimeObjectImp*>(value);
Bindings::JavaInstance *runtimeInstance = static_cast<Bindings::JavaInstance *>(runtimeImp->getInternalInstance());
return runtimeInstance->javaInstance();
}
else {
nativeHandle = ptr_to_jlong(imp);
addNativeReference (_root, imp);
}
}
else {
nativeHandle = UndefinedHandle;
}
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 (jobject theObject) const
{
jobject classOfInstance = callJNIObjectMethod(theObject, "getClass", "()Ljava/lang/Class;");
jstring className = (jstring)callJNIObjectMethod(classOfInstance, "getName", "()Ljava/lang/String;");
if (strcmp(Bindings::JavaString(className).UTF8String(), "sun.plugin.javascript.webkit.JSObject") == 0) {
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;
RuntimeObjectImp *newImp = new RuntimeObjectImp(new Bindings::JavaInstance (theObject, _root));
return newImp;
}
List JavaJSObject::listFromJArray(jobjectArray jArray) const
{
JNIEnv *env = getJNIEnv();
int i, numObjects = jArray ? env->GetArrayLength (jArray) : 0;
List aList;
for (i = 0; i < numObjects; i++) {
jobject anObject = env->GetObjectArrayElement ((jobjectArray)jArray, i);
if (anObject) {
aList.append (convertJObjectToValue(anObject));
env->DeleteLocalRef (anObject);
}
else {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
return aList;
}
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 //BINDINGS_JAVA