jat@google.com | 64a55cb | 2009-10-16 14:16:57 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2007 Google Inc. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 5 | * use this file except in compliance with the License. You may obtain a copy of |
| 6 | * the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | * License for the specific language governing permissions and limitations under |
| 14 | * the License. |
| 15 | */ |
| 16 | |
| 17 | #include <jni.h> |
| 18 | #include "JsRootedValue.h" |
| 19 | #include "gwt-jni.h" |
| 20 | #include "ExternalWrapper.h" |
| 21 | #include "Tracer.h" |
| 22 | |
| 23 | // include javah-generated header to make sure things match |
| 24 | #include "JsValueMoz.h" |
| 25 | |
| 26 | /* |
| 27 | * Returns the value of a field on a Java object. |
| 28 | * |
| 29 | * context - JavaScript context |
| 30 | * clazz - class of obj |
| 31 | * obj - Java object to retreive field from |
| 32 | * fieldName - name of field on Java object to retrieve |
| 33 | * |
| 34 | * Returns null on failure. Caller is responsible for deleting |
| 35 | * returned JsRootedValue when done with it. |
| 36 | */ |
| 37 | static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz, |
| 38 | jobject obj, jstring fieldName) |
| 39 | { |
| 40 | Tracer tracer("GetFieldAsRootedValue"); |
| 41 | JsRootedValue::ContextManager context(cx); |
| 42 | jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField", |
| 43 | "(Ljava/lang/String;I)V"); |
| 44 | if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) { |
| 45 | return 0; |
| 46 | } |
| 47 | |
| 48 | JsRootedValue* jsRootedValue = new JsRootedValue(); |
| 49 | savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName, |
| 50 | reinterpret_cast<jint>(jsRootedValue)); |
| 51 | if (savedJNIEnv->ExceptionCheck()) { |
| 52 | delete jsRootedValue; |
| 53 | return 0; |
| 54 | } |
| 55 | return jsRootedValue; |
| 56 | } |
| 57 | |
| 58 | /* |
| 59 | * Sets the value of a field on a Java object. |
| 60 | * |
| 61 | * context - JavaScript context |
| 62 | * clazz - class of obj |
| 63 | * obj - Java object to store into field |
| 64 | * fieldName - name of field on Java object to store into |
| 65 | * jsRootedValue - the value to store in the field |
| 66 | * |
| 67 | * returns true on success, false on failure |
| 68 | */ |
| 69 | static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz, |
| 70 | jobject obj, jstring fieldName, JsRootedValue* jsRootedValue) |
| 71 | { |
| 72 | Tracer tracer("SetFieldAsRootedValue"); |
| 73 | JsRootedValue::ContextManager context(cx); |
| 74 | jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField", |
| 75 | "(Ljava/lang/String;I)V"); |
| 76 | if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) { |
| 77 | return false; |
| 78 | } |
| 79 | |
| 80 | savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName, |
| 81 | reinterpret_cast<jint>(jsRootedValue)); |
| 82 | if (savedJNIEnv->ExceptionCheck()) { |
| 83 | return false; |
| 84 | } |
| 85 | |
| 86 | return true; |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | * Throws a HostedModeException with the specified message. |
| 91 | */ |
| 92 | static void ThrowHostedModeException(JNIEnv* jniEnv, const char* msg) { |
| 93 | jclass exceptionClass |
| 94 | = jniEnv->FindClass("com/google/gwt/dev/shell/HostedModeException"); |
| 95 | jniEnv->ThrowNew(exceptionClass, msg); |
| 96 | } |
| 97 | |
| 98 | /* |
| 99 | * Types of jsvals. |
| 100 | */ |
| 101 | enum JsValueType { |
| 102 | JSVAL_TYPE_VOID=0, |
| 103 | JSVAL_TYPE_NULL, |
| 104 | JSVAL_TYPE_BOOLEAN, |
| 105 | JSVAL_TYPE_NUMBER, |
| 106 | JSVAL_TYPE_STRING, |
| 107 | JSVAL_TYPE_OBJECT, |
| 108 | JSVAL_TYPE_UNKNOWN, |
| 109 | }; |
| 110 | |
| 111 | /* |
| 112 | * Names of jsval types -- must match the order of the enum above. |
| 113 | */ |
| 114 | static const char* JsValueTypeStrings[]={ |
| 115 | "undefined", |
| 116 | "null", |
| 117 | "boolean", |
| 118 | "number", |
| 119 | "string", |
| 120 | "object", |
| 121 | "unknown", |
| 122 | }; |
| 123 | |
| 124 | /* |
| 125 | * Return the type of a jsval. |
| 126 | */ |
| 127 | static JsValueType GetValueType(jsval val) { |
| 128 | if(JSVAL_IS_VOID(val)) { |
| 129 | return JSVAL_TYPE_VOID; |
| 130 | } else if(JSVAL_IS_NULL(val)) { |
| 131 | return JSVAL_TYPE_NULL; |
| 132 | } else if(JSVAL_IS_BOOLEAN(val)) { |
| 133 | return JSVAL_TYPE_BOOLEAN; |
| 134 | } else if(JSVAL_IS_NUMBER(val)) { |
| 135 | return JSVAL_TYPE_NUMBER; |
| 136 | } else if(JSVAL_IS_STRING(val)) { |
| 137 | return JSVAL_TYPE_STRING; |
| 138 | } else if(JSVAL_IS_OBJECT(val)) { |
| 139 | return JSVAL_TYPE_OBJECT; |
| 140 | } else { |
| 141 | return JSVAL_TYPE_UNKNOWN; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /* |
| 146 | * Called from JavaScript to call a Java method that has previously been |
| 147 | * wrapped. See _setWrappedFunction for use. |
| 148 | */ |
| 149 | static JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc, |
| 150 | jsval *argv, jsval *rval) |
| 151 | { |
| 152 | Tracer tracer("invokeJavaMethod"); |
| 153 | JsRootedValue::ContextManager context(cx); |
| 154 | |
| 155 | // I kid you not; this is how XPConnect gets their function object so they can |
| 156 | // multiplex dispatch the call from a common site. See XPCDispObject.cpp(466) |
| 157 | // |
| 158 | // I now have a secondary confirmation that this trick is legit. |
| 159 | // brandon@mozilla.org writes: |
| 160 | // |
| 161 | // argv[-2] is part of the JS API, unabstracted. Just as argv[0] is the |
| 162 | // first argument (if argc != 0), argv[-1] is the |this| parameter (equal |
| 163 | // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj| |
| 164 | // second formal parameter name), and argv[-2] is the callee object, tagged |
| 165 | // as a jsval. |
| 166 | if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION) { |
| 167 | tracer.setFail("not a function type"); |
| 168 | return JS_FALSE; |
| 169 | } |
| 170 | JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]); |
| 171 | |
| 172 | // Pull the wrapper object out of the funObj's reserved slot |
| 173 | jsval jsCleanupObj; |
| 174 | if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) { |
| 175 | tracer.setFail("JS_GetReservedSlot failed"); |
| 176 | return JS_FALSE; |
| 177 | } |
| 178 | JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj); |
| 179 | if (!cleanupObj) { |
| 180 | tracer.setFail("cleanupObj is null"); |
| 181 | return JS_FALSE; |
| 182 | } |
| 183 | |
| 184 | // Get DispatchMethod instance out of the wrapper object |
| 185 | jobject dispMeth = |
| 186 | NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj)); |
| 187 | if (!dispMeth) { |
| 188 | tracer.setFail("dispMeth is null"); |
| 189 | return JS_FALSE; |
| 190 | } |
| 191 | jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth); |
| 192 | if (!dispClass || savedJNIEnv->ExceptionCheck()) { |
| 193 | tracer.setFail("GetObjectClass returns null"); |
| 194 | return JS_FALSE; |
| 195 | } |
| 196 | |
| 197 | // lookup the invoke method on the dispatch object |
| 198 | jmethodID invokeID = |
| 199 | savedJNIEnv->GetMethodID(dispClass, "invoke", "(I[II)V"); |
| 200 | if (!invokeID || savedJNIEnv->ExceptionCheck()) { |
| 201 | tracer.setFail("GetMethodID failed"); |
| 202 | return JS_FALSE; |
| 203 | } |
| 204 | |
| 205 | // create an array of integers to hold the JsRootedValue pointers passed |
| 206 | // to the invoke method |
| 207 | jintArray args = savedJNIEnv->NewIntArray(argc); |
| 208 | if (!args || savedJNIEnv->ExceptionCheck()) { |
| 209 | tracer.setFail("NewIntArray failed"); |
| 210 | return JS_FALSE; |
| 211 | } |
| 212 | |
| 213 | // these arguments are already rooted by the JS interpreter, but we |
| 214 | // can't easily take advantage of that without complicating the JsRootedValue |
| 215 | // interface. |
| 216 | |
| 217 | // argv[-1] is OBJECT_TO_JSVAL(this) |
| 218 | JsRootedValue* jsThis = new JsRootedValue(argv[-1]); |
| 219 | tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis)); |
| 220 | |
| 221 | // create JsRootedValues for arguments |
| 222 | JsRootedValue *jsArgs[argc]; |
| 223 | for (uintN i = 0; i < argc; ++i) { |
| 224 | jsArgs[i] = new JsRootedValue(argv[i]); |
| 225 | } |
| 226 | savedJNIEnv->SetIntArrayRegion(args, 0, argc, |
| 227 | reinterpret_cast<jint*>(jsArgs)); |
| 228 | if (savedJNIEnv->ExceptionCheck()) { |
| 229 | tracer.setFail("SetIntArrayRegion failed"); |
| 230 | return JS_FALSE; |
| 231 | } |
| 232 | |
| 233 | // slot for return value |
| 234 | JsRootedValue* jsReturnVal = new JsRootedValue(); |
| 235 | |
| 236 | // TODO(jat): small window here where invocation may fail before Java |
| 237 | // takes ownership of the JsRootedValue objects. One solution would be |
| 238 | // to reference-count them between Java and C++ (so the reference count |
| 239 | // would always be 0, 1, or 2). Also setField has a similar problem. |
| 240 | // I plan to fix this when switching away from Java holding pointers to |
| 241 | // C++ objects as part of the fix for 64-bit support (which we could |
| 242 | // accomplish inefficiently by changing int to long everywhere, but there |
| 243 | // are other 64-bit issues to resolve and we need to reduce the number of |
| 244 | // roots the JS interpreter has to search. |
| 245 | |
| 246 | // call Java method |
| 247 | savedJNIEnv->CallVoidMethod(dispMeth, invokeID, reinterpret_cast<int>(jsThis), |
| 248 | args, reinterpret_cast<int>(jsReturnVal)); |
| 249 | |
| 250 | JSBool returnValue = JS_TRUE; |
| 251 | |
| 252 | if (savedJNIEnv->ExceptionCheck()) { |
| 253 | tracer.log("dispMeth=%08x", unsigned(dispMeth)); |
| 254 | tracer.setFail("java exception is active:"); |
| 255 | jobject exception = savedJNIEnv->ExceptionOccurred(); |
| 256 | if (exception) { |
| 257 | fprintf(stderr, "Exception occurred in MethodDispatch.invoke:\n"); |
| 258 | savedJNIEnv->ExceptionDescribe(); |
| 259 | savedJNIEnv->DeleteLocalRef(exception); |
| 260 | } |
| 261 | returnValue = JS_FALSE; |
| 262 | } else if (JS_IsExceptionPending(cx)) { |
| 263 | tracer.setFail("js exception is active"); |
| 264 | returnValue = JS_FALSE; |
| 265 | } |
| 266 | |
| 267 | // extract return value |
| 268 | *rval = jsReturnVal->getValue(); |
| 269 | |
| 270 | #if 0 |
| 271 | // NOTE: C++ objects are not cleaned up here because Java now owns them. |
| 272 | // TODO(jat): if reference-counted, they *do* need to be Released here. |
| 273 | |
| 274 | // free JsRootedValues |
| 275 | for (uintN i = 0; i < argc; ++i) { |
| 276 | delete jsArgs[i]; |
| 277 | } |
| 278 | delete jsThis; |
| 279 | delete jsReturnVal; |
| 280 | #endif |
| 281 | |
| 282 | return returnValue; |
| 283 | } |
| 284 | |
| 285 | /* |
| 286 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 287 | * Method: _createJsRootedValue() |
| 288 | * Signature: (I)I |
| 289 | */ |
| 290 | extern "C" JNIEXPORT jint JNICALL |
| 291 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1createJsRootedValue |
| 292 | (JNIEnv* jniEnv, jclass, jint jsval) |
| 293 | { |
| 294 | Tracer tracer("JsValueMoz._createJsRootedValue"); |
| 295 | JsRootedValue* jsRootedValue = new JsRootedValue(jsval); |
| 296 | return NS_REINTERPRET_CAST(jint, jsRootedValue); |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 301 | * Method: _copyJsRootedValue() |
| 302 | * Signature: (I)I |
| 303 | */ |
| 304 | extern "C" JNIEXPORT jint JNICALL |
| 305 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1copyJsRootedValue |
| 306 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 307 | { |
| 308 | const JsRootedValue* jsRootedValue = reinterpret_cast<const JsRootedValue*> |
| 309 | (jsRootedValueInt); |
| 310 | Tracer tracer("JsValueMoz._copyJsRootedValue", jsRootedValue); |
| 311 | JsRootedValue* newRootedValue = new JsRootedValue(*jsRootedValue); |
| 312 | return NS_REINTERPRET_CAST(jint, newRootedValue); |
| 313 | } |
| 314 | |
| 315 | /* |
| 316 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 317 | * Method: _destroyJsRootedValue() |
| 318 | * Signature: (I)V |
| 319 | */ |
| 320 | extern "C" JNIEXPORT void JNICALL |
| 321 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1destroyJsRootedValue |
| 322 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 323 | { |
| 324 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 325 | (jsRootedValueInt); |
| 326 | Tracer tracer("JsValueMoz._destroyJsRootedValue", jsRootedValue); |
| 327 | delete jsRootedValue; |
| 328 | } |
| 329 | |
| 330 | /* |
| 331 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 332 | * Method: _getBoolean() |
| 333 | * Signature: (I)Z |
| 334 | * |
| 335 | * TODO(jat): unboxing Javascript Boolean type? |
| 336 | */ |
| 337 | extern "C" JNIEXPORT jboolean JNICALL |
| 338 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getBoolean |
| 339 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 340 | { |
| 341 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 342 | (jsRootedValueInt); |
| 343 | Tracer tracer("JsValueMoz._getBoolean", jsRootedValue); |
| 344 | return jsRootedValue->getBoolean(); |
| 345 | } |
| 346 | |
| 347 | /** |
| 348 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 349 | * Method: _getInt() |
| 350 | * Signature: (I)I |
| 351 | * |
| 352 | * @see com.google.gwt.dev.shell.moz.JsValueMoz#getInt() |
| 353 | */ |
| 354 | extern "C" JNIEXPORT jint JNICALL |
| 355 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getInt |
| 356 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 357 | { |
| 358 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 359 | (jsRootedValueInt); |
| 360 | Tracer tracer("JsValueMoz._getInt", jsRootedValue); |
| 361 | int val = jsRootedValue->getInt(); |
| 362 | tracer.log("value=%d", val); |
| 363 | return val; |
| 364 | } |
| 365 | |
| 366 | /** |
| 367 | * Return a Javascript number as a Java double. |
| 368 | * |
| 369 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 370 | * Method: _getNumber() |
| 371 | * Signature: (I)D |
| 372 | */ |
| 373 | extern "C" JNIEXPORT jdouble JNICALL |
| 374 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getNumber |
| 375 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 376 | { |
| 377 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 378 | (jsRootedValueInt); |
| 379 | Tracer tracer("JsValueMoz._getNumber", jsRootedValue); |
| 380 | return jsRootedValue->getDouble(); |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 385 | * Method: _getJsval() |
| 386 | * Signature: (I)I |
| 387 | */ |
| 388 | extern "C" JNIEXPORT jint JNICALL |
| 389 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getJsval |
| 390 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 391 | { |
| 392 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 393 | (jsRootedValueInt); |
| 394 | Tracer tracer("JsValueMoz._getObjectPointer", jsRootedValue); |
| 395 | int val = jsRootedValue->getValue(); |
| 396 | tracer.log("value=%d", val); |
| 397 | return val; |
| 398 | } |
| 399 | |
| 400 | /** |
| 401 | * Return a Javascript string as a Java string. |
| 402 | * |
| 403 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 404 | * Method: _getString() |
| 405 | * Signature: (I)Ljava/lang/String; |
| 406 | * |
| 407 | * Note that this relies on jschar being assignment compatible with jchar |
| 408 | */ |
| 409 | extern "C" JNIEXPORT jstring JNICALL |
| 410 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getString |
| 411 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 412 | { |
| 413 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 414 | (jsRootedValueInt); |
| 415 | Tracer tracer("JsValueMoz._getString", jsRootedValue); |
| 416 | const JSString* str = jsRootedValue->getString(); |
| 417 | int len = JS_GetStringLength(const_cast<JSString*>(str)); |
| 418 | jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>( |
| 419 | JS_GetStringChars(const_cast<JSString*>(str))), len); |
| 420 | return javaStr; |
| 421 | } |
| 422 | |
| 423 | /* |
| 424 | * Returns a human-readable Java string describing the type of a |
| 425 | * JavaScript object. |
| 426 | * |
| 427 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 428 | * Method: _getTypeString |
| 429 | * Signature: (I)Ljava/lang/String; |
| 430 | */ |
| 431 | extern "C" JNIEXPORT jstring JNICALL |
| 432 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getTypeString |
| 433 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 434 | { |
| 435 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 436 | (jsRootedValueInt); |
| 437 | Tracer tracer("JsValueMoz._getTypeString", jsRootedValue); |
| 438 | jsval val = jsRootedValue->getValue(); |
| 439 | JSContext* cx = JsRootedValue::currentContext(); |
| 440 | JsValueType valueType = GetValueType(val); |
| 441 | const char* typeString = 0; |
| 442 | char buf[256]; |
| 443 | if(valueType == JSVAL_TYPE_OBJECT) { |
| 444 | JSObject* jsObject = JSVAL_TO_OBJECT(val); |
| 445 | JSClass* objClass = JS_GET_CLASS(cx, jsObject); |
| 446 | if (JS_InstanceOf(cx, jsObject, |
| 447 | &gwt_nativewrapper_class, 0)) { |
| 448 | typeString = "Java object"; |
| 449 | } else { |
| 450 | snprintf(buf, sizeof(buf), "class %s", objClass->name); |
| 451 | typeString = buf; |
| 452 | } |
| 453 | } else { |
| 454 | typeString = JsValueTypeStrings[valueType]; |
| 455 | } |
| 456 | jstring returnValue = jniEnv->NewStringUTF(typeString); |
| 457 | return returnValue; |
| 458 | } |
| 459 | |
| 460 | /* |
| 461 | * Unwraps a wrapped Java object from a JS object. |
| 462 | * |
| 463 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 464 | * Method: _getWrappedJavaObject |
| 465 | * Signature: (I)Ljava/lang/Object; |
| 466 | */ |
| 467 | extern "C" JNIEXPORT jobject JNICALL |
| 468 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getWrappedJavaObject |
| 469 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 470 | { |
| 471 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 472 | (jsRootedValueInt); |
| 473 | Tracer tracer("JsValueMoz._getWrappedJavaObject", jsRootedValue); |
| 474 | JSObject* jsObject = jsRootedValue->getObject(); |
| 475 | if(!jsObject) { |
| 476 | tracer.throwHostedModeException(jniEnv, "Javascript value not an object"); |
| 477 | return 0; |
| 478 | } |
| 479 | JSContext* cx = JsRootedValue::currentContext(); |
| 480 | if(!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)) { |
| 481 | tracer.throwHostedModeException(jniEnv, |
| 482 | "Javascript object not a Java object"); |
| 483 | return 0; |
| 484 | } |
| 485 | jobject javaObject |
| 486 | = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, jsObject)); |
| 487 | return javaObject; |
| 488 | } |
| 489 | |
| 490 | /* |
| 491 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 492 | * Method: _isBoolean() |
| 493 | * Signature: (I)Z |
| 494 | */ |
| 495 | extern "C" JNIEXPORT jboolean JNICALL |
| 496 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isBoolean |
| 497 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 498 | { |
| 499 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 500 | (jsRootedValueInt); |
| 501 | Tracer tracer("JsValueMoz._isBoolean", jsRootedValue); |
| 502 | return jsRootedValue->isBoolean(); |
| 503 | } |
| 504 | |
| 505 | /* |
| 506 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 507 | * Method: _isInt() |
| 508 | * Signature: (I)Z |
| 509 | */ |
| 510 | extern "C" JNIEXPORT jboolean JNICALL |
| 511 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isInt |
| 512 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 513 | { |
| 514 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 515 | (jsRootedValueInt); |
| 516 | Tracer tracer("JsValueMoz._isBoolean", jsRootedValue); |
| 517 | return jsRootedValue->isInt(); |
| 518 | } |
| 519 | |
| 520 | /* |
| 521 | * Checks if a JS object is a JavaScript object. |
| 522 | * |
| 523 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 524 | * Method: _isJavaScriptObject |
| 525 | * Signature: (I)Z |
| 526 | */ |
| 527 | extern "C" JNIEXPORT jboolean JNICALL |
| 528 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptObject |
| 529 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 530 | { |
| 531 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 532 | (jsRootedValueInt); |
| 533 | Tracer tracer("JsValueMoz._isJavaScriptObject", jsRootedValue); |
| 534 | jsval val = jsRootedValue->getValue(); |
| 535 | bool returnValue = false; |
| 536 | if(JSVAL_IS_OBJECT(val)) { |
| 537 | JSObject* jsObject = JSVAL_TO_OBJECT(val); |
| 538 | returnValue = !JS_InstanceOf(JsRootedValue::currentContext(), jsObject, |
| 539 | &gwt_nativewrapper_class, 0); |
| 540 | tracer.log("jsobject=%08x, isJSObject=%s", unsigned(jsObject), |
| 541 | returnValue ? "true" : "false"); |
| 542 | } else { |
| 543 | tracer.log("not an object"); |
| 544 | } |
| 545 | return returnValue; |
| 546 | } |
| 547 | |
| 548 | /* |
| 549 | * Checks if a JS object is a JavaScript String object. |
| 550 | * |
| 551 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 552 | * Method: _isJavaScriptString |
| 553 | * Signature: (I)Z |
| 554 | */ |
| 555 | extern "C" JNIEXPORT jboolean JNICALL |
| 556 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptString |
| 557 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 558 | { |
| 559 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 560 | (jsRootedValueInt); |
| 561 | Tracer tracer("JsValueMoz._isJavaScriptString", jsRootedValue); |
| 562 | bool returnValue = jsRootedValue->isJavaScriptStringObject(); |
| 563 | tracer.log("value=%s", returnValue ? "true" : "false"); |
| 564 | return returnValue; |
| 565 | } |
| 566 | |
| 567 | /* |
| 568 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 569 | * Method: _isNull() |
| 570 | * Signature: (I)Z |
| 571 | */ |
| 572 | extern "C" JNIEXPORT jboolean JNICALL |
| 573 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNull |
| 574 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 575 | { |
| 576 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 577 | (jsRootedValueInt); |
| 578 | Tracer tracer("JsValueMoz._isNull", jsRootedValue); |
| 579 | return jsRootedValue->isNull(); |
| 580 | } |
| 581 | |
| 582 | /* |
| 583 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 584 | * Method: _isNumber() |
| 585 | * Signature: (I)Z |
| 586 | */ |
| 587 | extern "C" JNIEXPORT jboolean JNICALL |
| 588 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNumber |
| 589 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 590 | { |
| 591 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 592 | (jsRootedValueInt); |
| 593 | Tracer tracer("JsValueMoz._isNumber", jsRootedValue); |
| 594 | return jsRootedValue->isNumber(); |
| 595 | } |
| 596 | |
| 597 | /* |
| 598 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 599 | * Method: _isString() |
| 600 | * Signature: (I)Z |
| 601 | * |
| 602 | * Handles the case of JavaScript String objects as well |
| 603 | */ |
| 604 | extern "C" JNIEXPORT jboolean JNICALL |
| 605 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isString |
| 606 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 607 | { |
| 608 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 609 | (jsRootedValueInt); |
| 610 | Tracer tracer("JsValueMoz._isString", jsRootedValue); |
| 611 | return jsRootedValue->isString(); |
| 612 | } |
| 613 | |
| 614 | /* |
| 615 | * Checks if a JS object is undefined (void) |
| 616 | * |
| 617 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 618 | * Method: _isUndefined |
| 619 | * Signature: (I)Z |
| 620 | */ |
| 621 | extern "C" JNIEXPORT jboolean JNICALL |
| 622 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isUndefined |
| 623 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 624 | { |
| 625 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 626 | (jsRootedValueInt); |
| 627 | Tracer tracer("JsValueMoz._isUndefined", jsRootedValue); |
| 628 | return jsRootedValue->isUndefined(); |
| 629 | } |
| 630 | |
| 631 | /* |
| 632 | * Checks if a JS object is a wrapped Java object. |
| 633 | * |
| 634 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 635 | * Method: _isWrappedJavaObject |
| 636 | * Signature: (I)Z |
| 637 | */ |
| 638 | extern "C" JNIEXPORT jboolean JNICALL |
| 639 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isWrappedJavaObject |
| 640 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 641 | { |
| 642 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 643 | (jsRootedValueInt); |
| 644 | Tracer tracer("JsValueMoz._isWrappedJavaObject", jsRootedValue); |
| 645 | jsval val = jsRootedValue->getValue(); |
| 646 | bool returnValue = false; |
| 647 | if(JSVAL_IS_OBJECT(val)) { |
| 648 | JSObject* jsObject = JSVAL_TO_OBJECT(val); |
| 649 | returnValue = JS_InstanceOf(JsRootedValue::currentContext(), jsObject, |
| 650 | &gwt_nativewrapper_class, 0); |
| 651 | tracer.log("jsobject=%08x, wrappedJava=%s", unsigned(jsObject), |
| 652 | returnValue ? "true" : "false"); |
| 653 | } else { |
| 654 | tracer.log("not an object"); |
| 655 | } |
| 656 | return returnValue; |
| 657 | } |
| 658 | |
| 659 | /* |
| 660 | * Set the JavaScript value to be a boolean of the supplied value. |
| 661 | * |
| 662 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 663 | * Method: _setBoolean() |
| 664 | * Signature: (IZ)V |
| 665 | */ |
| 666 | extern "C" JNIEXPORT void JNICALL |
| 667 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setBoolean |
| 668 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jboolean val) |
| 669 | { |
| 670 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 671 | (jsRootedValueInt); |
| 672 | Tracer tracer("JsValueMoz._setBoolean", jsRootedValue); |
| 673 | jsRootedValue->setBoolean(val == JNI_TRUE); |
| 674 | return; |
| 675 | } |
| 676 | |
| 677 | /* |
| 678 | * Set the JavaScript value to be a double of the supplied value. |
| 679 | * |
| 680 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 681 | * Method: _setDouble() |
| 682 | * Signature: (ID)V |
| 683 | */ |
| 684 | extern "C" JNIEXPORT void |
| 685 | JNICALL Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setDouble |
| 686 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jdouble val) |
| 687 | { |
| 688 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 689 | (jsRootedValueInt); |
| 690 | Tracer tracer("JsValueMoz._setDouble", jsRootedValue); |
| 691 | if(!jsRootedValue->setDouble(val)) { |
| 692 | tracer.throwHostedModeException(jniEnv, "Unable to allocate JS double"); |
| 693 | return; |
| 694 | } |
| 695 | } |
| 696 | |
| 697 | /* |
| 698 | * Set the Javascript value to be an integer. |
| 699 | * |
| 700 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 701 | * Method: _setInt() |
| 702 | * Signature: (II)V |
| 703 | */ |
| 704 | extern "C" JNIEXPORT void JNICALL |
| 705 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setInt |
| 706 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint val) |
| 707 | { |
| 708 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 709 | (jsRootedValueInt); |
| 710 | Tracer tracer("JsValueMoz._setInt", jsRootedValue); |
| 711 | tracer.log("val=%d", val); |
| 712 | jsRootedValue->setInt(val); |
| 713 | } |
| 714 | |
| 715 | /* |
| 716 | * Set the Javascript value to be another JsRootedValue's value. |
| 717 | * |
| 718 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 719 | * Method: _setJsRootedValue() |
| 720 | * Signature: (II)V |
| 721 | */ |
| 722 | extern "C" JNIEXPORT void JNICALL |
| 723 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsRootedValue |
| 724 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsOtherRootedValueInt) |
| 725 | { |
| 726 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 727 | (jsRootedValueInt); |
| 728 | JsRootedValue* jsOtherRootedValue = reinterpret_cast<JsRootedValue*> |
| 729 | (jsOtherRootedValueInt); |
| 730 | Tracer tracer("JsValueMoz._setJsRootedValue", jsRootedValue); |
| 731 | jsRootedValue->setValue(jsOtherRootedValue->getValue()); |
| 732 | } |
| 733 | |
| 734 | /* |
| 735 | * Set the Javascript value to a specific jsval. |
| 736 | * |
| 737 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 738 | * Method: _setJsval() |
| 739 | * Signature: (II)V |
| 740 | */ |
| 741 | extern "C" JNIEXPORT void JNICALL |
| 742 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsval |
| 743 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsval) |
| 744 | { |
| 745 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 746 | (jsRootedValueInt); |
| 747 | Tracer tracer("JsValueMoz._setJsval", jsRootedValue); |
| 748 | jsRootedValue->setValue(jsval); |
| 749 | } |
| 750 | |
| 751 | /* |
| 752 | * Set the JavaScript value to be null. |
| 753 | * |
| 754 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 755 | * Method: _setNull() |
| 756 | * Signature: (I)V |
| 757 | */ |
| 758 | extern "C" JNIEXPORT void JNICALL |
| 759 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setNull |
| 760 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 761 | { |
| 762 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 763 | (jsRootedValueInt); |
| 764 | Tracer tracer("JsValueMoz._setNull", jsRootedValue); |
| 765 | jsRootedValue->setNull(); |
| 766 | } |
| 767 | |
| 768 | /* |
| 769 | * Set the JavaScript value to be a string of the supplied value. |
| 770 | * |
| 771 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 772 | * Method: _setString() |
| 773 | * Signature: (ILjava/lang/String;)V |
| 774 | */ |
| 775 | extern "C" JNIEXPORT void JNICALL |
| 776 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setString |
| 777 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring val) |
| 778 | { |
| 779 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 780 | (jsRootedValueInt); |
| 781 | Tracer tracer("JsValueMoz._setString", jsRootedValue); |
| 782 | JStringWrap strVal(jniEnv, val); |
| 783 | const jchar* stringUTF16 = strVal.jstr(); |
| 784 | if(!stringUTF16) { |
| 785 | tracer.throwHostedModeException(jniEnv, "Unable to retrieve Java string"); |
| 786 | return; |
| 787 | } |
| 788 | tracer.log("string=%s", strVal.str()); |
| 789 | if(!jsRootedValue->setString(reinterpret_cast<const wchar_t*>(stringUTF16), |
| 790 | strVal.length())) { |
| 791 | tracer.throwHostedModeException(jniEnv, "Unable to allocate JS string"); |
| 792 | return; |
| 793 | } |
| 794 | } |
| 795 | |
| 796 | /* |
| 797 | * Set the JavaScript value to be undefined (void). |
| 798 | * |
| 799 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 800 | * Method: _setUndefined() |
| 801 | * Signature: (I)V |
| 802 | */ |
| 803 | extern "C" JNIEXPORT void JNICALL |
| 804 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setUndefined |
| 805 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 806 | { |
| 807 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 808 | (jsRootedValueInt); |
| 809 | Tracer tracer("JsValueMoz._setUndefined", jsRootedValue); |
| 810 | jsRootedValue->setUndefined(); |
| 811 | } |
| 812 | |
| 813 | /* |
| 814 | * Wraps a Java object in a JS object. |
| 815 | * |
| 816 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 817 | * Method: _setWrappedJavaObject |
| 818 | * Signature: (ILjava/lang/Object)V |
| 819 | */ |
| 820 | extern "C" JNIEXPORT void JNICALL |
| 821 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedJavaObject |
| 822 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jobject obj) |
| 823 | { |
| 824 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 825 | (jsRootedValueInt); |
| 826 | Tracer tracer("JsValueMoz._setWrappedJavaObject", jsRootedValue); |
| 827 | JSContext* cx = JsRootedValue::currentContext(); |
| 828 | JSObject* scriptWindow = JS_GetGlobalObject(cx); |
| 829 | JSObject* newObj = JS_NewObject(cx, &gwt_nativewrapper_class, 0, |
| 830 | scriptWindow); |
| 831 | if (!newObj) { |
| 832 | tracer.throwHostedModeException(jniEnv, |
| 833 | "Unable to allocate JS object to wrap Java object"); |
| 834 | return; |
| 835 | } |
| 836 | // Save in output value so it won't get GCed. |
| 837 | jsRootedValue->setObject(newObj); |
| 838 | tracer.log("jsobject=%08x", unsigned(newObj)); |
| 839 | |
| 840 | // This is collected when the gwt_nativewrapper_class destructor runs. |
| 841 | jobject dispObjRef = jniEnv->NewGlobalRef(obj); |
| 842 | if (!dispObjRef || jniEnv->ExceptionCheck()) { |
| 843 | tracer.throwHostedModeException(jniEnv, |
| 844 | "Unable to allocate global reference for JS wrapper"); |
| 845 | return; |
| 846 | } |
| 847 | if (!JS_SetPrivate(cx, newObj, dispObjRef)) { |
| 848 | jniEnv->DeleteGlobalRef(dispObjRef); |
| 849 | tracer.throwHostedModeException(jniEnv, |
| 850 | "Unable to allocate global reference for JS wrapper"); |
| 851 | return; |
| 852 | } |
| 853 | // forcibly setup a "toString" method to override the default |
| 854 | jclass dispClass = jniEnv->GetObjectClass(obj); |
| 855 | if (jniEnv->ExceptionCheck()) { |
| 856 | tracer.throwHostedModeException(jniEnv, "Can't get object class"); |
| 857 | return; |
| 858 | } |
| 859 | jmethodID getFieldMeth = jniEnv->GetMethodID(dispClass, "getField", |
| 860 | "(Ljava/lang/String;I)V"); |
| 861 | if (!getFieldMeth || jniEnv->ExceptionCheck()) { |
| 862 | tracer.throwHostedModeException(jniEnv, "Can't get getField method"); |
| 863 | return; |
| 864 | } |
| 865 | jstring ident = jniEnv->NewStringUTF("@java.lang.Object::toString()"); |
| 866 | if (!ident || jniEnv->ExceptionCheck()) { |
| 867 | tracer.throwHostedModeException(jniEnv, |
| 868 | "Can't create Java string for toString method name"); |
| 869 | return; |
| 870 | } |
| 871 | // allocate a new root to hold the result of the getField call |
| 872 | JsRootedValue* toStringFunc = new JsRootedValue(); |
| 873 | jniEnv->CallVoidMethod(obj, getFieldMeth, ident, |
| 874 | NS_REINTERPRET_CAST(jint, toStringFunc)); |
| 875 | if (toStringFunc->isUndefined() || jniEnv->ExceptionCheck()) { |
| 876 | tracer.throwHostedModeException(jniEnv, "getField(toString) failed"); |
| 877 | return; |
| 878 | } |
| 879 | if (!JS_DefineProperty(cx, newObj, "toString", toStringFunc->getValue(), |
| 880 | JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) { |
| 881 | tracer.throwHostedModeException(jniEnv, "Can't define JS toString method"); |
| 882 | return; |
| 883 | } |
| 884 | } |
| 885 | |
| 886 | /* |
| 887 | * Wraps a Java function in a JS object. |
| 888 | * |
| 889 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 890 | * Method: _setWrappedFunction |
| 891 | * Signature: (ILjava/lang/String;Lcom/google/gwt/dev/shell/moz/DispatchMethod;)V |
| 892 | */ |
| 893 | extern "C" JNIEXPORT void JNICALL |
| 894 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedFunction |
| 895 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring methodName, |
| 896 | jobject dispatchMethod) |
| 897 | { |
| 898 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 899 | (jsRootedValueInt); |
| 900 | Tracer tracer("JsValueMoz._setWrappedFunction", jsRootedValue); |
| 901 | JSContext* cx = JsRootedValue::currentContext(); |
| 902 | JSObject* scriptWindow = JS_GetGlobalObject(cx); |
| 903 | JStringWrap nameStr(jniEnv, methodName); |
| 904 | if (!nameStr.str()) { |
| 905 | tracer.throwHostedModeException(jniEnv, |
| 906 | "null method name passed to setWrappedFunction"); |
| 907 | return; |
| 908 | } |
| 909 | tracer.log("JsRootedValue=%08x, method=%s, obj=%08x", jsRootedValueInt, |
| 910 | nameStr.str(), unsigned(dispatchMethod)); |
| 911 | JSFunction* function = JS_NewFunction(cx, invokeJavaMethod, 0, |
| 912 | JSFUN_LAMBDA, 0, nameStr.str()); |
| 913 | if (!function) { |
| 914 | tracer.throwHostedModeException(jniEnv, "JS_NewFunction failed"); |
| 915 | return; |
| 916 | } |
| 917 | JSObject* funObj = JS_GetFunctionObject(function); |
| 918 | if (!funObj) { |
| 919 | tracer.throwHostedModeException(jniEnv, "JS_GetFunctionObject failed"); |
| 920 | return; |
| 921 | } |
| 922 | // Save in output value so it won't get GCed. |
| 923 | jsRootedValue->setObject(funObj); |
| 924 | |
| 925 | // Create a cleanup object to hold and clean up dispMeth |
| 926 | JSObject* cleanupObj = JS_NewObject(cx, &gwt_functionwrapper_class, 0, |
| 927 | scriptWindow); |
| 928 | if (!cleanupObj) { |
| 929 | tracer.throwHostedModeException(jniEnv, "JS_NewObject failed"); |
| 930 | return; |
| 931 | } |
| 932 | tracer.log("funObj=%08x, cleanupObj=%08x", unsigned(funObj), |
| 933 | unsigned(cleanupObj)); |
| 934 | // Store the cleanup object in funObj's reserved slot; now GC protected. |
| 935 | if(!JS_SetReservedSlot(cx, funObj, 0, OBJECT_TO_JSVAL(cleanupObj))) { |
| 936 | tracer.throwHostedModeException(jniEnv, "JS_SetReservedSlot failed"); |
| 937 | return; |
| 938 | } |
| 939 | jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod); |
| 940 | if (!dispMethRef || jniEnv->ExceptionCheck()) { |
| 941 | tracer.throwHostedModeException(jniEnv, |
| 942 | "NewGlobalRef(dispatchMethod) failed"); |
| 943 | return; |
| 944 | } |
| 945 | // Store our global ref in the wrapper object |
| 946 | if (!JS_SetPrivate(cx, cleanupObj, dispMethRef)) { |
| 947 | jniEnv->DeleteGlobalRef(dispMethRef); |
| 948 | tracer.throwHostedModeException(jniEnv, "JS_SetPrivate(cleanupObj) failed"); |
| 949 | return; |
| 950 | } |
| 951 | } |
| 952 | |
| 953 | /* |
| 954 | * Returns a JavaScript value as a string. |
| 955 | * |
| 956 | * Class: com_google_gwt_dev_shell_moz_JsValueMoz |
| 957 | * Method: _toString |
| 958 | * Signature: (I)Ljava/lang/String; |
| 959 | */ |
| 960 | extern "C" JNIEXPORT jstring JNICALL |
| 961 | Java_com_google_gwt_dev_shell_moz_JsValueMoz__1toString |
| 962 | (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) |
| 963 | { |
| 964 | JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> |
| 965 | (jsRootedValueInt); |
| 966 | Tracer tracer("JsValueMoz._toString", jsRootedValue); |
| 967 | jsval val = jsRootedValue->getValue(); |
| 968 | JSContext* cx = JsRootedValue::currentContext(); |
| 969 | |
| 970 | // if it is a JavaScript object that has a toString member function |
| 971 | // call that, otherwise call JS_ValueToString |
| 972 | if(JSVAL_IS_OBJECT(val)) { |
| 973 | JSObject* jsObject = JSVAL_TO_OBJECT(val); |
| 974 | jsval fval; |
| 975 | jsval rval; |
| 976 | if (!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0) |
| 977 | && JS_GetProperty(cx, jsObject, "toString", &fval) |
| 978 | && JS_ValueToFunction(cx, fval) |
| 979 | && JS_CallFunctionValue(cx, jsObject, fval, 0, 0, &rval)) { |
| 980 | // all the steps succeeded, so use the result of toString() instead |
| 981 | // of the value for JS_ValueToString below |
| 982 | val = rval; |
| 983 | } |
| 984 | } |
| 985 | JSString* str = JS_ValueToString(cx, val); |
| 986 | if (!str) { |
| 987 | return 0; |
| 988 | } |
| 989 | int len = JS_GetStringLength(str); |
| 990 | jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>( |
| 991 | JS_GetStringChars(str)), len); |
| 992 | return javaStr; |
| 993 | } |