blob: 195306c630c1e8bbe4145b7f384ce42154d50d8f [file] [log] [blame]
jat@google.com64a55cb2009-10-16 14:16:57 +00001/*
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 */
37static 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 */
69static 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 */
92static 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 */
101enum 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 */
114static 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 */
127static 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 */
149static 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 */
290extern "C" JNIEXPORT jint JNICALL
291Java_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 */
304extern "C" JNIEXPORT jint JNICALL
305Java_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 */
320extern "C" JNIEXPORT void JNICALL
321Java_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 */
337extern "C" JNIEXPORT jboolean JNICALL
338Java_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 */
354extern "C" JNIEXPORT jint JNICALL
355Java_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 */
373extern "C" JNIEXPORT jdouble JNICALL
374Java_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 */
388extern "C" JNIEXPORT jint JNICALL
389Java_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 */
409extern "C" JNIEXPORT jstring JNICALL
410Java_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 */
431extern "C" JNIEXPORT jstring JNICALL
432Java_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 */
467extern "C" JNIEXPORT jobject JNICALL
468Java_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 */
495extern "C" JNIEXPORT jboolean JNICALL
496Java_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 */
510extern "C" JNIEXPORT jboolean JNICALL
511Java_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 */
527extern "C" JNIEXPORT jboolean JNICALL
528Java_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 */
555extern "C" JNIEXPORT jboolean JNICALL
556Java_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 */
572extern "C" JNIEXPORT jboolean JNICALL
573Java_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 */
587extern "C" JNIEXPORT jboolean JNICALL
588Java_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 */
604extern "C" JNIEXPORT jboolean JNICALL
605Java_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 */
621extern "C" JNIEXPORT jboolean JNICALL
622Java_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 */
638extern "C" JNIEXPORT jboolean JNICALL
639Java_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 */
666extern "C" JNIEXPORT void JNICALL
667Java_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 */
684extern "C" JNIEXPORT void
685JNICALL 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 */
704extern "C" JNIEXPORT void JNICALL
705Java_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 */
722extern "C" JNIEXPORT void JNICALL
723Java_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 */
741extern "C" JNIEXPORT void JNICALL
742Java_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 */
758extern "C" JNIEXPORT void JNICALL
759Java_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 */
775extern "C" JNIEXPORT void JNICALL
776Java_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 */
803extern "C" JNIEXPORT void JNICALL
804Java_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 */
820extern "C" JNIEXPORT void JNICALL
821Java_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 */
893extern "C" JNIEXPORT void JNICALL
894Java_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 */
960extern "C" JNIEXPORT jstring JNICALL
961Java_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}