| /* | |
| * Copyright 2007 Google Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not | |
| * use this file except in compliance with the License. You may obtain a copy of | |
| * the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| * License for the specific language governing permissions and limitations under | |
| * the License. | |
| */ | |
| #include <jni.h> | |
| #include "JsRootedValue.h" | |
| #include "gwt-jni.h" | |
| #include "ExternalWrapper.h" | |
| #include "Tracer.h" | |
| // include javah-generated header to make sure things match | |
| #include "JsValueMoz.h" | |
| /* | |
| * Returns the value of a field on a Java object. | |
| * | |
| * context - JavaScript context | |
| * clazz - class of obj | |
| * obj - Java object to retreive field from | |
| * fieldName - name of field on Java object to retrieve | |
| * | |
| * Returns null on failure. Caller is responsible for deleting | |
| * returned JsRootedValue when done with it. | |
| */ | |
| static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz, | |
| jobject obj, jstring fieldName) | |
| { | |
| Tracer tracer("GetFieldAsRootedValue"); | |
| JsRootedValue::ContextManager context(cx); | |
| jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField", | |
| "(Ljava/lang/String;I)V"); | |
| if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) { | |
| return 0; | |
| } | |
| JsRootedValue* jsRootedValue = new JsRootedValue(); | |
| savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName, | |
| reinterpret_cast<jint>(jsRootedValue)); | |
| if (savedJNIEnv->ExceptionCheck()) { | |
| delete jsRootedValue; | |
| return 0; | |
| } | |
| return jsRootedValue; | |
| } | |
| /* | |
| * Sets the value of a field on a Java object. | |
| * | |
| * context - JavaScript context | |
| * clazz - class of obj | |
| * obj - Java object to store into field | |
| * fieldName - name of field on Java object to store into | |
| * jsRootedValue - the value to store in the field | |
| * | |
| * returns true on success, false on failure | |
| */ | |
| static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz, | |
| jobject obj, jstring fieldName, JsRootedValue* jsRootedValue) | |
| { | |
| Tracer tracer("SetFieldAsRootedValue"); | |
| JsRootedValue::ContextManager context(cx); | |
| jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField", | |
| "(Ljava/lang/String;I)V"); | |
| if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) { | |
| return false; | |
| } | |
| savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName, | |
| reinterpret_cast<jint>(jsRootedValue)); | |
| if (savedJNIEnv->ExceptionCheck()) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| /* | |
| * Throws a HostedModeException with the specified message. | |
| */ | |
| static void ThrowHostedModeException(JNIEnv* jniEnv, const char* msg) { | |
| jclass exceptionClass | |
| = jniEnv->FindClass("com/google/gwt/dev/shell/HostedModeException"); | |
| jniEnv->ThrowNew(exceptionClass, msg); | |
| } | |
| /* | |
| * Types of jsvals. | |
| */ | |
| enum JsValueType { | |
| JSVAL_TYPE_VOID=0, | |
| JSVAL_TYPE_NULL, | |
| JSVAL_TYPE_BOOLEAN, | |
| JSVAL_TYPE_NUMBER, | |
| JSVAL_TYPE_STRING, | |
| JSVAL_TYPE_OBJECT, | |
| JSVAL_TYPE_UNKNOWN, | |
| }; | |
| /* | |
| * Names of jsval types -- must match the order of the enum above. | |
| */ | |
| static const char* JsValueTypeStrings[]={ | |
| "undefined", | |
| "null", | |
| "boolean", | |
| "number", | |
| "string", | |
| "object", | |
| "unknown", | |
| }; | |
| /* | |
| * Return the type of a jsval. | |
| */ | |
| static JsValueType GetValueType(jsval val) { | |
| if(JSVAL_IS_VOID(val)) { | |
| return JSVAL_TYPE_VOID; | |
| } else if(JSVAL_IS_NULL(val)) { | |
| return JSVAL_TYPE_NULL; | |
| } else if(JSVAL_IS_BOOLEAN(val)) { | |
| return JSVAL_TYPE_BOOLEAN; | |
| } else if(JSVAL_IS_NUMBER(val)) { | |
| return JSVAL_TYPE_NUMBER; | |
| } else if(JSVAL_IS_STRING(val)) { | |
| return JSVAL_TYPE_STRING; | |
| } else if(JSVAL_IS_OBJECT(val)) { | |
| return JSVAL_TYPE_OBJECT; | |
| } else { | |
| return JSVAL_TYPE_UNKNOWN; | |
| } | |
| } | |
| /* | |
| * Called from JavaScript to call a Java method that has previously been | |
| * wrapped. See _setWrappedFunction for use. | |
| */ | |
| static JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc, | |
| jsval *argv, jsval *rval) | |
| { | |
| Tracer tracer("invokeJavaMethod"); | |
| JsRootedValue::ContextManager context(cx); | |
| // I kid you not; this is how XPConnect gets their function object so they can | |
| // multiplex dispatch the call from a common site. See XPCDispObject.cpp(466) | |
| // | |
| // I now have a secondary confirmation that this trick is legit. | |
| // brandon@mozilla.org writes: | |
| // | |
| // argv[-2] is part of the JS API, unabstracted. Just as argv[0] is the | |
| // first argument (if argc != 0), argv[-1] is the |this| parameter (equal | |
| // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj| | |
| // second formal parameter name), and argv[-2] is the callee object, tagged | |
| // as a jsval. | |
| if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION) { | |
| tracer.setFail("not a function type"); | |
| return JS_FALSE; | |
| } | |
| JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]); | |
| // Pull the wrapper object out of the funObj's reserved slot | |
| jsval jsCleanupObj; | |
| if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) { | |
| tracer.setFail("JS_GetReservedSlot failed"); | |
| return JS_FALSE; | |
| } | |
| JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj); | |
| if (!cleanupObj) { | |
| tracer.setFail("cleanupObj is null"); | |
| return JS_FALSE; | |
| } | |
| // Get DispatchMethod instance out of the wrapper object | |
| jobject dispMeth = | |
| NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj)); | |
| if (!dispMeth) { | |
| tracer.setFail("dispMeth is null"); | |
| return JS_FALSE; | |
| } | |
| jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth); | |
| if (!dispClass || savedJNIEnv->ExceptionCheck()) { | |
| tracer.setFail("GetObjectClass returns null"); | |
| return JS_FALSE; | |
| } | |
| // lookup the invoke method on the dispatch object | |
| jmethodID invokeID = | |
| savedJNIEnv->GetMethodID(dispClass, "invoke", "(I[II)V"); | |
| if (!invokeID || savedJNIEnv->ExceptionCheck()) { | |
| tracer.setFail("GetMethodID failed"); | |
| return JS_FALSE; | |
| } | |
| // create an array of integers to hold the JsRootedValue pointers passed | |
| // to the invoke method | |
| jintArray args = savedJNIEnv->NewIntArray(argc); | |
| if (!args || savedJNIEnv->ExceptionCheck()) { | |
| tracer.setFail("NewIntArray failed"); | |
| return JS_FALSE; | |
| } | |
| // these arguments are already rooted by the JS interpreter, but we | |
| // can't easily take advantage of that without complicating the JsRootedValue | |
| // interface. | |
| // argv[-1] is OBJECT_TO_JSVAL(this) | |
| JsRootedValue* jsThis = new JsRootedValue(argv[-1]); | |
| tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis)); | |
| // create JsRootedValues for arguments | |
| JsRootedValue *jsArgs[argc]; | |
| for (uintN i = 0; i < argc; ++i) { | |
| jsArgs[i] = new JsRootedValue(argv[i]); | |
| } | |
| savedJNIEnv->SetIntArrayRegion(args, 0, argc, | |
| reinterpret_cast<jint*>(jsArgs)); | |
| if (savedJNIEnv->ExceptionCheck()) { | |
| tracer.setFail("SetIntArrayRegion failed"); | |
| return JS_FALSE; | |
| } | |
| // slot for return value | |
| JsRootedValue* jsReturnVal = new JsRootedValue(); | |
| // TODO(jat): small window here where invocation may fail before Java | |
| // takes ownership of the JsRootedValue objects. One solution would be | |
| // to reference-count them between Java and C++ (so the reference count | |
| // would always be 0, 1, or 2). Also setField has a similar problem. | |
| // I plan to fix this when switching away from Java holding pointers to | |
| // C++ objects as part of the fix for 64-bit support (which we could | |
| // accomplish inefficiently by changing int to long everywhere, but there | |
| // are other 64-bit issues to resolve and we need to reduce the number of | |
| // roots the JS interpreter has to search. | |
| // call Java method | |
| savedJNIEnv->CallVoidMethod(dispMeth, invokeID, reinterpret_cast<int>(jsThis), | |
| args, reinterpret_cast<int>(jsReturnVal)); | |
| JSBool returnValue = JS_TRUE; | |
| if (savedJNIEnv->ExceptionCheck()) { | |
| tracer.log("dispMeth=%08x", unsigned(dispMeth)); | |
| tracer.setFail("java exception is active:"); | |
| jobject exception = savedJNIEnv->ExceptionOccurred(); | |
| if (exception) { | |
| fprintf(stderr, "Exception occurred in MethodDispatch.invoke:\n"); | |
| savedJNIEnv->ExceptionDescribe(); | |
| savedJNIEnv->DeleteLocalRef(exception); | |
| } | |
| returnValue = JS_FALSE; | |
| } else if (JS_IsExceptionPending(cx)) { | |
| tracer.setFail("js exception is active"); | |
| returnValue = JS_FALSE; | |
| } | |
| // extract return value | |
| *rval = jsReturnVal->getValue(); | |
| #if 0 | |
| // NOTE: C++ objects are not cleaned up here because Java now owns them. | |
| // TODO(jat): if reference-counted, they *do* need to be Released here. | |
| // free JsRootedValues | |
| for (uintN i = 0; i < argc; ++i) { | |
| delete jsArgs[i]; | |
| } | |
| delete jsThis; | |
| delete jsReturnVal; | |
| #endif | |
| return returnValue; | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _createJsRootedValue() | |
| * Signature: (I)I | |
| */ | |
| extern "C" JNIEXPORT jint JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1createJsRootedValue | |
| (JNIEnv* jniEnv, jclass, jint jsval) | |
| { | |
| Tracer tracer("JsValueMoz._createJsRootedValue"); | |
| JsRootedValue* jsRootedValue = new JsRootedValue(jsval); | |
| return NS_REINTERPRET_CAST(jint, jsRootedValue); | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _copyJsRootedValue() | |
| * Signature: (I)I | |
| */ | |
| extern "C" JNIEXPORT jint JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1copyJsRootedValue | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| const JsRootedValue* jsRootedValue = reinterpret_cast<const JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._copyJsRootedValue", jsRootedValue); | |
| JsRootedValue* newRootedValue = new JsRootedValue(*jsRootedValue); | |
| return NS_REINTERPRET_CAST(jint, newRootedValue); | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _destroyJsRootedValue() | |
| * Signature: (I)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1destroyJsRootedValue | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._destroyJsRootedValue", jsRootedValue); | |
| delete jsRootedValue; | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _getBoolean() | |
| * Signature: (I)Z | |
| * | |
| * TODO(jat): unboxing Javascript Boolean type? | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getBoolean | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._getBoolean", jsRootedValue); | |
| return jsRootedValue->getBoolean(); | |
| } | |
| /** | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _getInt() | |
| * Signature: (I)I | |
| * | |
| * @see com.google.gwt.dev.shell.moz.JsValueMoz#getInt() | |
| */ | |
| extern "C" JNIEXPORT jint JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getInt | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._getInt", jsRootedValue); | |
| int val = jsRootedValue->getInt(); | |
| tracer.log("value=%d", val); | |
| return val; | |
| } | |
| /** | |
| * Return a Javascript number as a Java double. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _getNumber() | |
| * Signature: (I)D | |
| */ | |
| extern "C" JNIEXPORT jdouble JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getNumber | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._getNumber", jsRootedValue); | |
| return jsRootedValue->getDouble(); | |
| } | |
| /** | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _getJsval() | |
| * Signature: (I)I | |
| */ | |
| extern "C" JNIEXPORT jint JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getJsval | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._getObjectPointer", jsRootedValue); | |
| int val = jsRootedValue->getValue(); | |
| tracer.log("value=%d", val); | |
| return val; | |
| } | |
| /** | |
| * Return a Javascript string as a Java string. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _getString() | |
| * Signature: (I)Ljava/lang/String; | |
| * | |
| * Note that this relies on jschar being assignment compatible with jchar | |
| */ | |
| extern "C" JNIEXPORT jstring JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getString | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._getString", jsRootedValue); | |
| const JSString* str = jsRootedValue->getString(); | |
| int len = JS_GetStringLength(const_cast<JSString*>(str)); | |
| jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>( | |
| JS_GetStringChars(const_cast<JSString*>(str))), len); | |
| return javaStr; | |
| } | |
| /* | |
| * Returns a human-readable Java string describing the type of a | |
| * JavaScript object. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _getTypeString | |
| * Signature: (I)Ljava/lang/String; | |
| */ | |
| extern "C" JNIEXPORT jstring JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getTypeString | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._getTypeString", jsRootedValue); | |
| jsval val = jsRootedValue->getValue(); | |
| JSContext* cx = JsRootedValue::currentContext(); | |
| JsValueType valueType = GetValueType(val); | |
| const char* typeString = 0; | |
| char buf[256]; | |
| if(valueType == JSVAL_TYPE_OBJECT) { | |
| JSObject* jsObject = JSVAL_TO_OBJECT(val); | |
| JSClass* objClass = JS_GET_CLASS(cx, jsObject); | |
| if (JS_InstanceOf(cx, jsObject, | |
| &gwt_nativewrapper_class, 0)) { | |
| typeString = "Java object"; | |
| } else { | |
| snprintf(buf, sizeof(buf), "class %s", objClass->name); | |
| typeString = buf; | |
| } | |
| } else { | |
| typeString = JsValueTypeStrings[valueType]; | |
| } | |
| jstring returnValue = jniEnv->NewStringUTF(typeString); | |
| return returnValue; | |
| } | |
| /* | |
| * Unwraps a wrapped Java object from a JS object. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _getWrappedJavaObject | |
| * Signature: (I)Ljava/lang/Object; | |
| */ | |
| extern "C" JNIEXPORT jobject JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getWrappedJavaObject | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._getWrappedJavaObject", jsRootedValue); | |
| JSObject* jsObject = jsRootedValue->getObject(); | |
| if(!jsObject) { | |
| tracer.throwHostedModeException(jniEnv, "Javascript value not an object"); | |
| return 0; | |
| } | |
| JSContext* cx = JsRootedValue::currentContext(); | |
| if(!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)) { | |
| tracer.throwHostedModeException(jniEnv, | |
| "Javascript object not a Java object"); | |
| return 0; | |
| } | |
| jobject javaObject | |
| = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, jsObject)); | |
| return javaObject; | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isBoolean() | |
| * Signature: (I)Z | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isBoolean | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isBoolean", jsRootedValue); | |
| return jsRootedValue->isBoolean(); | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isInt() | |
| * Signature: (I)Z | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isInt | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isBoolean", jsRootedValue); | |
| return jsRootedValue->isInt(); | |
| } | |
| /* | |
| * Checks if a JS object is a JavaScript object. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isJavaScriptObject | |
| * Signature: (I)Z | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptObject | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isJavaScriptObject", jsRootedValue); | |
| jsval val = jsRootedValue->getValue(); | |
| bool returnValue = false; | |
| if(JSVAL_IS_OBJECT(val)) { | |
| JSObject* jsObject = JSVAL_TO_OBJECT(val); | |
| returnValue = !JS_InstanceOf(JsRootedValue::currentContext(), jsObject, | |
| &gwt_nativewrapper_class, 0); | |
| tracer.log("jsobject=%08x, isJSObject=%s", unsigned(jsObject), | |
| returnValue ? "true" : "false"); | |
| } else { | |
| tracer.log("not an object"); | |
| } | |
| return returnValue; | |
| } | |
| /* | |
| * Checks if a JS object is a JavaScript String object. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isJavaScriptString | |
| * Signature: (I)Z | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptString | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isJavaScriptString", jsRootedValue); | |
| bool returnValue = jsRootedValue->isJavaScriptStringObject(); | |
| tracer.log("value=%s", returnValue ? "true" : "false"); | |
| return returnValue; | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isNull() | |
| * Signature: (I)Z | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNull | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isNull", jsRootedValue); | |
| return jsRootedValue->isNull(); | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isNumber() | |
| * Signature: (I)Z | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNumber | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isNumber", jsRootedValue); | |
| return jsRootedValue->isNumber(); | |
| } | |
| /* | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isString() | |
| * Signature: (I)Z | |
| * | |
| * Handles the case of JavaScript String objects as well | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isString | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isString", jsRootedValue); | |
| return jsRootedValue->isString(); | |
| } | |
| /* | |
| * Checks if a JS object is undefined (void) | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isUndefined | |
| * Signature: (I)Z | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isUndefined | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isUndefined", jsRootedValue); | |
| return jsRootedValue->isUndefined(); | |
| } | |
| /* | |
| * Checks if a JS object is a wrapped Java object. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _isWrappedJavaObject | |
| * Signature: (I)Z | |
| */ | |
| extern "C" JNIEXPORT jboolean JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isWrappedJavaObject | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._isWrappedJavaObject", jsRootedValue); | |
| jsval val = jsRootedValue->getValue(); | |
| bool returnValue = false; | |
| if(JSVAL_IS_OBJECT(val)) { | |
| JSObject* jsObject = JSVAL_TO_OBJECT(val); | |
| returnValue = JS_InstanceOf(JsRootedValue::currentContext(), jsObject, | |
| &gwt_nativewrapper_class, 0); | |
| tracer.log("jsobject=%08x, wrappedJava=%s", unsigned(jsObject), | |
| returnValue ? "true" : "false"); | |
| } else { | |
| tracer.log("not an object"); | |
| } | |
| return returnValue; | |
| } | |
| /* | |
| * Set the JavaScript value to be a boolean of the supplied value. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setBoolean() | |
| * Signature: (IZ)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setBoolean | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jboolean val) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setBoolean", jsRootedValue); | |
| jsRootedValue->setBoolean(val == JNI_TRUE); | |
| return; | |
| } | |
| /* | |
| * Set the JavaScript value to be a double of the supplied value. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setDouble() | |
| * Signature: (ID)V | |
| */ | |
| extern "C" JNIEXPORT void | |
| JNICALL Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setDouble | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jdouble val) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setDouble", jsRootedValue); | |
| if(!jsRootedValue->setDouble(val)) { | |
| tracer.throwHostedModeException(jniEnv, "Unable to allocate JS double"); | |
| return; | |
| } | |
| } | |
| /* | |
| * Set the Javascript value to be an integer. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setInt() | |
| * Signature: (II)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setInt | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint val) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setInt", jsRootedValue); | |
| tracer.log("val=%d", val); | |
| jsRootedValue->setInt(val); | |
| } | |
| /* | |
| * Set the Javascript value to be another JsRootedValue's value. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setJsRootedValue() | |
| * Signature: (II)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsRootedValue | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsOtherRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| JsRootedValue* jsOtherRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsOtherRootedValueInt); | |
| Tracer tracer("JsValueMoz._setJsRootedValue", jsRootedValue); | |
| jsRootedValue->setValue(jsOtherRootedValue->getValue()); | |
| } | |
| /* | |
| * Set the Javascript value to a specific jsval. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setJsval() | |
| * Signature: (II)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsval | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsval) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setJsval", jsRootedValue); | |
| jsRootedValue->setValue(jsval); | |
| } | |
| /* | |
| * Set the JavaScript value to be null. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setNull() | |
| * Signature: (I)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setNull | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setNull", jsRootedValue); | |
| jsRootedValue->setNull(); | |
| } | |
| /* | |
| * Set the JavaScript value to be a string of the supplied value. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setString() | |
| * Signature: (ILjava/lang/String;)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setString | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring val) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setString", jsRootedValue); | |
| JStringWrap strVal(jniEnv, val); | |
| const jchar* stringUTF16 = strVal.jstr(); | |
| if(!stringUTF16) { | |
| tracer.throwHostedModeException(jniEnv, "Unable to retrieve Java string"); | |
| return; | |
| } | |
| tracer.log("string=%s", strVal.str()); | |
| if(!jsRootedValue->setString(reinterpret_cast<const wchar_t*>(stringUTF16), | |
| strVal.length())) { | |
| tracer.throwHostedModeException(jniEnv, "Unable to allocate JS string"); | |
| return; | |
| } | |
| } | |
| /* | |
| * Set the JavaScript value to be undefined (void). | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setUndefined() | |
| * Signature: (I)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setUndefined | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setUndefined", jsRootedValue); | |
| jsRootedValue->setUndefined(); | |
| } | |
| /* | |
| * Wraps a Java object in a JS object. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setWrappedJavaObject | |
| * Signature: (ILjava/lang/Object)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedJavaObject | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jobject obj) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setWrappedJavaObject", jsRootedValue); | |
| JSContext* cx = JsRootedValue::currentContext(); | |
| JSObject* scriptWindow = JS_GetGlobalObject(cx); | |
| JSObject* newObj = JS_NewObject(cx, &gwt_nativewrapper_class, 0, | |
| scriptWindow); | |
| if (!newObj) { | |
| tracer.throwHostedModeException(jniEnv, | |
| "Unable to allocate JS object to wrap Java object"); | |
| return; | |
| } | |
| // Save in output value so it won't get GCed. | |
| jsRootedValue->setObject(newObj); | |
| tracer.log("jsobject=%08x", unsigned(newObj)); | |
| // This is collected when the gwt_nativewrapper_class destructor runs. | |
| jobject dispObjRef = jniEnv->NewGlobalRef(obj); | |
| if (!dispObjRef || jniEnv->ExceptionCheck()) { | |
| tracer.throwHostedModeException(jniEnv, | |
| "Unable to allocate global reference for JS wrapper"); | |
| return; | |
| } | |
| if (!JS_SetPrivate(cx, newObj, dispObjRef)) { | |
| jniEnv->DeleteGlobalRef(dispObjRef); | |
| tracer.throwHostedModeException(jniEnv, | |
| "Unable to allocate global reference for JS wrapper"); | |
| return; | |
| } | |
| // forcibly setup a "toString" method to override the default | |
| jclass dispClass = jniEnv->GetObjectClass(obj); | |
| if (jniEnv->ExceptionCheck()) { | |
| tracer.throwHostedModeException(jniEnv, "Can't get object class"); | |
| return; | |
| } | |
| jmethodID getFieldMeth = jniEnv->GetMethodID(dispClass, "getField", | |
| "(Ljava/lang/String;I)V"); | |
| if (!getFieldMeth || jniEnv->ExceptionCheck()) { | |
| tracer.throwHostedModeException(jniEnv, "Can't get getField method"); | |
| return; | |
| } | |
| jstring ident = jniEnv->NewStringUTF("@java.lang.Object::toString()"); | |
| if (!ident || jniEnv->ExceptionCheck()) { | |
| tracer.throwHostedModeException(jniEnv, | |
| "Can't create Java string for toString method name"); | |
| return; | |
| } | |
| // allocate a new root to hold the result of the getField call | |
| JsRootedValue* toStringFunc = new JsRootedValue(); | |
| jniEnv->CallVoidMethod(obj, getFieldMeth, ident, | |
| NS_REINTERPRET_CAST(jint, toStringFunc)); | |
| if (toStringFunc->isUndefined() || jniEnv->ExceptionCheck()) { | |
| tracer.throwHostedModeException(jniEnv, "getField(toString) failed"); | |
| return; | |
| } | |
| if (!JS_DefineProperty(cx, newObj, "toString", toStringFunc->getValue(), | |
| JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) { | |
| tracer.throwHostedModeException(jniEnv, "Can't define JS toString method"); | |
| return; | |
| } | |
| } | |
| /* | |
| * Wraps a Java function in a JS object. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _setWrappedFunction | |
| * Signature: (ILjava/lang/String;Lcom/google/gwt/dev/shell/moz/DispatchMethod;)V | |
| */ | |
| extern "C" JNIEXPORT void JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedFunction | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring methodName, | |
| jobject dispatchMethod) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._setWrappedFunction", jsRootedValue); | |
| JSContext* cx = JsRootedValue::currentContext(); | |
| JSObject* scriptWindow = JS_GetGlobalObject(cx); | |
| JStringWrap nameStr(jniEnv, methodName); | |
| if (!nameStr.str()) { | |
| tracer.throwHostedModeException(jniEnv, | |
| "null method name passed to setWrappedFunction"); | |
| return; | |
| } | |
| tracer.log("JsRootedValue=%08x, method=%s, obj=%08x", jsRootedValueInt, | |
| nameStr.str(), unsigned(dispatchMethod)); | |
| JSFunction* function = JS_NewFunction(cx, invokeJavaMethod, 0, | |
| JSFUN_LAMBDA, 0, nameStr.str()); | |
| if (!function) { | |
| tracer.throwHostedModeException(jniEnv, "JS_NewFunction failed"); | |
| return; | |
| } | |
| JSObject* funObj = JS_GetFunctionObject(function); | |
| if (!funObj) { | |
| tracer.throwHostedModeException(jniEnv, "JS_GetFunctionObject failed"); | |
| return; | |
| } | |
| // Save in output value so it won't get GCed. | |
| jsRootedValue->setObject(funObj); | |
| // Create a cleanup object to hold and clean up dispMeth | |
| JSObject* cleanupObj = JS_NewObject(cx, &gwt_functionwrapper_class, 0, | |
| scriptWindow); | |
| if (!cleanupObj) { | |
| tracer.throwHostedModeException(jniEnv, "JS_NewObject failed"); | |
| return; | |
| } | |
| tracer.log("funObj=%08x, cleanupObj=%08x", unsigned(funObj), | |
| unsigned(cleanupObj)); | |
| // Store the cleanup object in funObj's reserved slot; now GC protected. | |
| if(!JS_SetReservedSlot(cx, funObj, 0, OBJECT_TO_JSVAL(cleanupObj))) { | |
| tracer.throwHostedModeException(jniEnv, "JS_SetReservedSlot failed"); | |
| return; | |
| } | |
| jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod); | |
| if (!dispMethRef || jniEnv->ExceptionCheck()) { | |
| tracer.throwHostedModeException(jniEnv, | |
| "NewGlobalRef(dispatchMethod) failed"); | |
| return; | |
| } | |
| // Store our global ref in the wrapper object | |
| if (!JS_SetPrivate(cx, cleanupObj, dispMethRef)) { | |
| jniEnv->DeleteGlobalRef(dispMethRef); | |
| tracer.throwHostedModeException(jniEnv, "JS_SetPrivate(cleanupObj) failed"); | |
| return; | |
| } | |
| } | |
| /* | |
| * Returns a JavaScript value as a string. | |
| * | |
| * Class: com_google_gwt_dev_shell_moz_JsValueMoz | |
| * Method: _toString | |
| * Signature: (I)Ljava/lang/String; | |
| */ | |
| extern "C" JNIEXPORT jstring JNICALL | |
| Java_com_google_gwt_dev_shell_moz_JsValueMoz__1toString | |
| (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) | |
| { | |
| JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> | |
| (jsRootedValueInt); | |
| Tracer tracer("JsValueMoz._toString", jsRootedValue); | |
| jsval val = jsRootedValue->getValue(); | |
| JSContext* cx = JsRootedValue::currentContext(); | |
| // if it is a JavaScript object that has a toString member function | |
| // call that, otherwise call JS_ValueToString | |
| if(JSVAL_IS_OBJECT(val)) { | |
| JSObject* jsObject = JSVAL_TO_OBJECT(val); | |
| jsval fval; | |
| jsval rval; | |
| if (!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0) | |
| && JS_GetProperty(cx, jsObject, "toString", &fval) | |
| && JS_ValueToFunction(cx, fval) | |
| && JS_CallFunctionValue(cx, jsObject, fval, 0, 0, &rval)) { | |
| // all the steps succeeded, so use the result of toString() instead | |
| // of the value for JS_ValueToString below | |
| val = rval; | |
| } | |
| } | |
| JSString* str = JS_ValueToString(cx, val); | |
| if (!str) { | |
| return 0; | |
| } | |
| int len = JS_GetStringLength(str); | |
| jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>( | |
| JS_GetStringChars(str)), len); | |
| return javaStr; | |
| } |