blob: 195306c630c1e8bbe4145b7f384ce42154d50d8f [file] [log] [blame]
/*
* 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;
}