001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.shell.moz;
017:
018: import com.google.gwt.dev.shell.CompilingClassLoader;
019: import com.google.gwt.dev.shell.JsValue;
020: import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchMethod;
021: import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchObject;
022:
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.Map;
026:
027: /**
028: * Represents a Mozilla JavaScript value.
029: *
030: * TODO(jat): 64-bit compatibility - currently underlying pointers are passed
031: * around in a Java int, which only works on standard 32-bit platforms where
032: * sizeof(void*)=4
033: */
034: public class JsValueMoz extends JsValue {
035:
036: /**
037: * Records debug information, only used when {@link JsValueMoz#debugFlag} is
038: * <code>true</code>.
039: */
040: private static class DebugLogging {
041: private Map<Integer, Throwable> alreadyCleanedJsRootedValues = Collections
042: .synchronizedMap(new HashMap<Integer, Throwable>());
043: private int maxActive = 0;
044: private int numActive = 0;
045: private Map<Integer, Throwable> seenJsRootedValues = Collections
046: .synchronizedMap(new HashMap<Integer, Throwable>());
047: private int totAlloc = 0;
048:
049: /**
050: * Count a JsValueMoz instance being created.
051: *
052: * Verify that the underlying JsRootedValue is not currently active, and
053: * mark that it is active.
054: *
055: * This is debug code that is only executed if debugFlag is true. Since this
056: * is a private static final field, the compiler should optimize out all
057: * this code. It is useful to have for tracking down problems, so it is
058: * being left in but disabled.
059: */
060: public void createInstance(int jsRootedValue) {
061: Integer jsrv = new Integer(jsRootedValue);
062: if (seenJsRootedValues.containsKey(jsrv)) {
063: Throwable t = seenJsRootedValues.get(jsrv);
064: String msg = hexString(jsRootedValue);
065: System.err.println(msg
066: + ", original caller stacktrace:");
067: t.printStackTrace();
068: throw new RuntimeException(msg);
069: }
070: Throwable t = new Throwable();
071: seenJsRootedValues.put(jsrv, t);
072: if (alreadyCleanedJsRootedValues.containsKey(jsrv)) {
073: alreadyCleanedJsRootedValues.remove(jsrv);
074: }
075: if (++numActive > maxActive) {
076: maxActive = numActive;
077: }
078: ++totAlloc;
079: }
080:
081: /**
082: * Count a JsValueMoz instance being destroyed.
083: *
084: * Verify that this instance hasn't already been destroyed, that it has
085: * previously been created, and that the underlying JsRootedValue is only
086: * being cleaned once.
087: *
088: * This is debug code that is only executed if debugFlag is true. Since this
089: * is a private static final field, the compiler should optimize out all
090: * this code. It is useful to have for tracking down problems, so it is
091: * being left in but disabled.
092: */
093: public void destroyInstance(int jsRootedValue) {
094: if (jsRootedValue == 0) {
095: throw new RuntimeException(
096: "Cleaning already-cleaned JsValueMoz");
097: }
098: Integer jsrv = new Integer(jsRootedValue);
099: if (!seenJsRootedValues.containsKey(jsrv)) {
100: throw new RuntimeException("cleaning up 0x"
101: + hexString(jsRootedValue) + ", not active");
102: }
103: if (alreadyCleanedJsRootedValues.containsKey(jsrv)) {
104: Throwable t = seenJsRootedValues.get(jsrv);
105: String msg = "Already cleaned 0x"
106: + hexString(jsRootedValue);
107: System.err.println(msg
108: + ", original allocator stacktrace:");
109: t.printStackTrace();
110: throw new RuntimeException(msg);
111: }
112: Throwable t = new Throwable();
113: alreadyCleanedJsRootedValues.put(jsrv, t);
114: seenJsRootedValues.remove(jsrv);
115: --numActive;
116: }
117:
118: /**
119: * Print collected statistics on JsValueMoz usage.
120: */
121: public void dumpStatistics() {
122: System.gc();
123: System.out.println("JsValueMoz usage:");
124: System.out.println(" " + totAlloc
125: + " total instances created");
126: System.out.println(" " + maxActive + " at any one time");
127: System.out.println(" " + seenJsRootedValues.size()
128: + " uncleaned entries");
129: }
130: }
131:
132: private static class JsCleanupMoz implements JsCleanup {
133: private final int jsRootedValue;
134:
135: public JsCleanupMoz(int jsRootedValue) {
136: this .jsRootedValue = jsRootedValue;
137: }
138:
139: public void doCleanup() {
140: _destroyJsRootedValue(jsRootedValue);
141: }
142: }
143:
144: /**
145: * Flag to enable debug checks on underlying JsRootedValues.
146: */
147: private static final boolean debugFlag = false;
148:
149: /**
150: * Flag to enable debug checks on underlying JsRootedValues.
151: */
152: private static final DebugLogging debugInfo = debugFlag ? new DebugLogging()
153: : null;
154:
155: /**
156: * This must match the value from jsapi.h.
157: */
158: private static final int JSVAL_VOID = 0x80000001;
159:
160: // CHECKSTYLE_NAMING_OFF -- native methods start with '_'
161: protected static native boolean _getBoolean(int jsRootedValue);
162:
163: protected static native int _getInt(int jsRootedValue);
164:
165: protected static native double _getNumber(int jsRootedValue);
166:
167: protected static native String _getString(int jsRootedValue);
168:
169: protected static native String _getTypeString(int jsRootedValue);
170:
171: protected static native DispatchObject _getWrappedJavaObject(
172: int jsRootedValue);
173:
174: protected static native boolean _isBoolean(int jsRootedValue);
175:
176: protected static native boolean _isInt(int jsRootedValue);
177:
178: protected static native boolean _isJavaScriptObject(
179: int jsRootedValue);
180:
181: protected static native boolean _isJavaScriptString(
182: int jsRootedValue);
183:
184: protected static native boolean _isNull(int jsRootedValue);
185:
186: protected static native boolean _isNumber(int jsRootedValue);
187:
188: protected static native boolean _isString(int jsRootedValue);
189:
190: protected static native boolean _isUndefined(int jsRootedValue);
191:
192: protected static native boolean _isWrappedJavaObject(
193: int jsRootedValue);
194:
195: protected static native void _setBoolean(int jsRootedValue,
196: boolean val);
197:
198: protected static native void _setDouble(int jsRootedValue,
199: double val);
200:
201: protected static native void _setInt(int jsRootedValue, int val);
202:
203: protected static native void _setJsRootedValue(int jsRootedValue,
204: int jsOtherRootedValue);
205:
206: protected static native void _setNull(int jsRootedValue);
207:
208: protected static native void _setString(int jsRootedValue,
209: String val);
210:
211: protected static native void _setUndefined(int jsRootedValue);
212:
213: protected static native void _setWrappedFunction(int jsRootedValue,
214: String methodName, DispatchMethod dispatchMethod);
215:
216: protected static native void _setWrappedJavaObject(
217: int jsRootedValue, DispatchObject val);
218:
219: private static native int _copyJsRootedValue(int jsRootedValue);
220:
221: /**
222: * Create a JsRootedValue and return a pointer to it as a Java int.
223: *
224: * @param jsval JavaScript jsval for initial value
225: * @return pointer to JsRootedValue object as an integer
226: */
227: private static native int _createJsRootedValue(int jsval);
228:
229: /**
230: * Destroy a JsRootedValue.
231: *
232: * @param jsRootedValue pointer to underlying JsRootedValue as an integer.
233: */
234: private static native void _destroyJsRootedValue(int jsRootedValue);
235:
236: // CHECKSTYLE_NAMING_ON
237:
238: /**
239: * Convert an address to a hex string.
240: *
241: * @param jsRootedValue underlying JavaScript value as an opaque integer
242: * @return a string with the JavaScript value represented as hex
243: */
244: private static String hexString(int jsRootedValue) {
245: long l = jsRootedValue;
246: l = l & 0xffffffffL;
247: return Long.toHexString(l);
248: }
249:
250: // pointer to underlying JsRootedValue object as an integer
251: private int jsRootedValue;
252:
253: /**
254: * Create a JsValueMoz object representing the undefined value.
255: */
256: public JsValueMoz() {
257: this .jsRootedValue = _createJsRootedValue(JSVAL_VOID);
258: if (debugFlag) {
259: debugInfo.createInstance(jsRootedValue);
260: }
261: }
262:
263: /**
264: * Create a JsValueMoz object wrapping a JsRootedValue object given the
265: * pointer to it as an integer.
266: *
267: * @param jsRootedValue pointer to underlying JsRootedValue as an integer.
268: */
269: public JsValueMoz(int jsRootedValue) {
270: this .jsRootedValue = jsRootedValue;
271: if (debugFlag) {
272: debugInfo.createInstance(jsRootedValue);
273: }
274: }
275:
276: /**
277: * Copy constructor.
278: *
279: * @param other JsValueMoz instance to copy
280: */
281: public JsValueMoz(JsValueMoz other) {
282: jsRootedValue = _copyJsRootedValue(other.jsRootedValue);
283: if (debugFlag) {
284: debugInfo.createInstance(jsRootedValue);
285: }
286: }
287:
288: /*
289: * (non-Javadoc)
290: *
291: * @see com.google.gwt.dev.shell.JsValue#getBoolean()
292: */
293: @Override
294: public boolean getBoolean() {
295: return _getBoolean(jsRootedValue);
296: }
297:
298: /*
299: * (non-Javadoc)
300: *
301: * @see com.google.gwt.dev.shell.JsValue#getInt()
302: */
303: @Override
304: public int getInt() {
305: return _getInt(jsRootedValue);
306: }
307:
308: /**
309: * Returns the underlying JavaScript object pointer as an integer.
310: */
311: public int getJsRootedValue() {
312: return jsRootedValue;
313: }
314:
315: /*
316: * (non-Javadoc)
317: *
318: * @see com.google.gwt.dev.shell.JsValue#getNumber()
319: */
320: @Override
321: public double getNumber() {
322: return _getNumber(jsRootedValue);
323: }
324:
325: /*
326: * (non-Javadoc)
327: *
328: * @see com.google.gwt.dev.shell.JsValue#getString()
329: */
330: @Override
331: public String getString() {
332: return _getString(jsRootedValue);
333: }
334:
335: /*
336: * (non-Javadoc)
337: *
338: * @see com.google.gwt.dev.shell.JsValue#getTypeString()
339: */
340: @Override
341: public String getTypeString() {
342: return _getTypeString(jsRootedValue);
343: }
344:
345: /*
346: * (non-Javadoc)
347: *
348: * @see com.google.gwt.dev.shell.JsValue#getWrappedJavaObject()
349: */
350: @Override
351: public Object getWrappedJavaObject() {
352: DispatchObject obj = _getWrappedJavaObject(jsRootedValue);
353: return obj.getTarget();
354: }
355:
356: /*
357: * (non-Javadoc)
358: *
359: * @see com.google.gwt.dev.shell.JsValue#isBoolean()
360: */
361: @Override
362: public boolean isBoolean() {
363: return _isBoolean(jsRootedValue);
364: }
365:
366: /*
367: * (non-Javadoc)
368: *
369: * @see com.google.gwt.dev.shell.JsValue#isInt()
370: */
371: @Override
372: public boolean isInt() {
373: return _isInt(jsRootedValue);
374: }
375:
376: /*
377: * (non-Javadoc)
378: *
379: * @see com.google.gwt.dev.shell.JsValue#isJavaScriptObject()
380: */
381: @Override
382: public boolean isJavaScriptObject() {
383: return _isJavaScriptObject(jsRootedValue);
384: }
385:
386: /*
387: * (non-Javadoc)
388: *
389: * @see com.google.gwt.dev.shell.JsValue#isNull()
390: */
391: @Override
392: public boolean isNull() {
393: return _isNull(jsRootedValue);
394: }
395:
396: /*
397: * (non-Javadoc)
398: *
399: * @see com.google.gwt.dev.shell.JsValue#isNumber()
400: */
401: @Override
402: public boolean isNumber() {
403: return _isNumber(jsRootedValue);
404: }
405:
406: /*
407: * (non-Javadoc)
408: *
409: * @see com.google.gwt.dev.shell.JsValue#isString()
410: */
411: @Override
412: public boolean isString() {
413: // String objects are acceptable for String value returns
414: return _isString(jsRootedValue)
415: || _isJavaScriptString(jsRootedValue);
416: }
417:
418: /*
419: * (non-Javadoc)
420: *
421: * @see com.google.gwt.dev.shell.JsValue#isUndefined()
422: */
423: @Override
424: public boolean isUndefined() {
425: return _isUndefined(jsRootedValue);
426: }
427:
428: /*
429: * (non-Javadoc)
430: *
431: * @see com.google.gwt.dev.shell.JsValue#isWrappedJavaObject()
432: */
433: @Override
434: public boolean isWrappedJavaObject() {
435: return _isWrappedJavaObject(jsRootedValue);
436: }
437:
438: /*
439: * (non-Javadoc)
440: *
441: * @see com.google.gwt.dev.shell.JsValue#setBoolean(boolean)
442: */
443: @Override
444: public void setBoolean(boolean val) {
445: _setBoolean(jsRootedValue, val);
446: }
447:
448: /*
449: * (non-Javadoc)
450: *
451: * @see com.google.gwt.dev.shell.JsValue#setByte(byte)
452: *
453: * TODO(jat): remove this method
454: */
455: @Override
456: public void setByte(byte val) {
457: _setInt(jsRootedValue, val);
458: }
459:
460: /*
461: * (non-Javadoc)
462: *
463: * @see com.google.gwt.dev.shell.JsValue#setChar(char)
464: *
465: * TODO(jat): remove this method
466: */
467: @Override
468: public void setChar(char val) {
469: _setInt(jsRootedValue, val);
470: }
471:
472: /*
473: * (non-Javadoc)
474: *
475: * @see com.google.gwt.dev.shell.JsValue#setDouble(double)
476: */
477: @Override
478: public void setDouble(double val) {
479: _setDouble(jsRootedValue, val);
480: }
481:
482: /*
483: * (non-Javadoc)
484: *
485: * @see com.google.gwt.dev.shell.JsValue#setInt(int)
486: */
487: @Override
488: public void setInt(int val) {
489: _setInt(jsRootedValue, val);
490: }
491:
492: /*
493: * (non-Javadoc)
494: *
495: * @see com.google.gwt.dev.shell.JsValue#setNull()
496: */
497: @Override
498: public void setNull() {
499: _setNull(jsRootedValue);
500: }
501:
502: /*
503: * (non-Javadoc)
504: *
505: * @see com.google.gwt.dev.shell.JsValue#setShort(short)
506: *
507: * TODO(jat): remove this method
508: */
509: @Override
510: public void setShort(short val) {
511: _setInt(jsRootedValue, val);
512: }
513:
514: /*
515: * (non-Javadoc)
516: *
517: * @see com.google.gwt.dev.shell.JsValue#setString(java.lang.String)
518: */
519: @Override
520: public void setString(String val) {
521: _setString(jsRootedValue, val);
522: }
523:
524: /*
525: * (non-Javadoc)
526: *
527: * @see com.google.gwt.dev.shell.JsValue#setUndefined()
528: */
529: @Override
530: public void setUndefined() {
531: _setUndefined(jsRootedValue);
532: }
533:
534: /*
535: * (non-Javadoc)
536: *
537: * @see com.google.gwt.dev.shell.JsValue#setValue(com.google.gwt.dev.shell.JsValue)
538: */
539: @Override
540: public void setValue(JsValue other) {
541: _setJsRootedValue(jsRootedValue,
542: ((JsValueMoz) other).jsRootedValue);
543: }
544:
545: /**
546: * Wrap a function call to a Java method in this JavaScript value.
547: *
548: * @param methodName the name of the method to invoke
549: * @param dispatchMethod the wrapper object
550: */
551: public void setWrappedFunction(String methodName,
552: DispatchMethod dispatchMethod) {
553: _setWrappedFunction(jsRootedValue, methodName, dispatchMethod);
554: }
555:
556: /*
557: * (non-Javadoc)
558: *
559: * @see com.google.gwt.dev.shell.JsValue#setWrappedJavaObject(com.google.gwt.dev.shell.CompilingClassLoader,
560: * java.lang.Object)
561: */
562: @Override
563: public void setWrappedJavaObject(CompilingClassLoader cl, Object val) {
564: if (val == null) {
565: setNull();
566: return;
567: }
568: DispatchObject dispObj;
569: if (val instanceof DispatchObject) {
570: dispObj = (DispatchObject) val;
571: } else {
572: dispObj = new GeckoDispatchAdapter(cl, val);
573: }
574: _setWrappedJavaObject(jsRootedValue, dispObj);
575: }
576:
577: /**
578: * Create a cleanup object that will free the underlying JsRootedValue object.
579: */
580: @Override
581: protected JsCleanup createCleanupObject() {
582: JsCleanup cleanup = new JsCleanupMoz(jsRootedValue);
583: if (debugFlag) {
584: debugInfo.destroyInstance(jsRootedValue);
585: jsRootedValue = 0;
586: }
587: return cleanup;
588: }
589:
590: }
|