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.mac;
017:
018: import com.google.gwt.dev.shell.CompilingClassLoader;
019: import com.google.gwt.dev.shell.JsValue;
020: import com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject;
021:
022: /**
023: * Represents a Safari JavaScript value.
024: *
025: * The basic rule is that any JSValue passed to Java code from native code will
026: * always be GC-protected in the native code and Java will always unprotect it
027: * when the value is finalized. It should always be stored in a JsValue object
028: * immediately to make sure it is cleaned up properly when it is no longer
029: * needed. This approach is required to avoid a race condition where the value
030: * is allocated in JNI code but could be garbage collected before Java takes
031: * ownership of the value. Java values passed into JavaScript store a GlobalRef
032: * of a WebKitDispatchAdapter or MethodDispatch objects, which are freed when
033: * the JS value is finalized.
034: */
035: public class JsValueSaf extends JsValue {
036:
037: private static class JsCleanupSaf implements JsCleanup {
038: private final int jsval;
039: private final Throwable creationStackTrace;
040:
041: /**
042: * Create a cleanup object which takes care of cleaning up the underlying JS
043: * object.
044: *
045: * @param jsval JSValue pointer as an integer
046: */
047: public JsCleanupSaf(int jsval, Throwable creationStackTrace) {
048: this .jsval = jsval;
049: this .creationStackTrace = creationStackTrace;
050: }
051:
052: /*
053: * (non-Javadoc)
054: *
055: * @see com.google.gwt.dev.shell.JsValue.JsCleanup#doCleanup()
056: */
057: public void doCleanup() {
058: LowLevelSaf.gcUnlock(jsval, creationStackTrace);
059: }
060: }
061:
062: /*
063: * Underlying JSValue* as an integer.
064: */
065: private int jsval;
066:
067: /*
068: * Stores a stack trace of the creation site for debugging.
069: */
070: private Throwable creationStackTrace;
071:
072: /**
073: * Create a Java wrapper around an undefined JSValue.
074: */
075: public JsValueSaf() {
076: init(LowLevelSaf.jsUndefined());
077: }
078:
079: /**
080: * Create a Java wrapper around the underlying JSValue.
081: *
082: * @param jsval a pointer to the underlying JSValue object as an integer
083: */
084: public JsValueSaf(int jsval) {
085: init(jsval);
086: }
087:
088: @Override
089: public boolean getBoolean() {
090: int curExecState = LowLevelSaf.getExecState();
091: return LowLevelSaf.coerceToBoolean(curExecState, jsval);
092: }
093:
094: @Override
095: public int getInt() {
096: int curExecState = LowLevelSaf.getExecState();
097: return LowLevelSaf.coerceToInt(curExecState, jsval);
098: }
099:
100: public int getJsValue() {
101: return jsval;
102: }
103:
104: @Override
105: public double getNumber() {
106: int curExecState = LowLevelSaf.getExecState();
107: return LowLevelSaf.coerceToDouble(curExecState, jsval);
108: }
109:
110: @Override
111: public String getString() {
112: int curExecState = LowLevelSaf.getExecState();
113: return LowLevelSaf.coerceToString(curExecState, jsval);
114: }
115:
116: @Override
117: public String getTypeString() {
118: return LowLevelSaf.getTypeString(jsval);
119: }
120:
121: @Override
122: public Object getWrappedJavaObject() {
123: DispatchObject obj = LowLevelSaf.unwrapDispatch(jsval);
124: return obj.getTarget();
125: }
126:
127: @Override
128: public boolean isBoolean() {
129: return LowLevelSaf.isBoolean(jsval);
130: }
131:
132: @Override
133: public boolean isInt() {
134: // Safari doesn't have integers, so this is always false
135: return false;
136: }
137:
138: @Override
139: public boolean isJavaScriptObject() {
140: return LowLevelSaf.isObject(jsval)
141: && !LowLevelSaf.isWrappedDispatch(jsval);
142: }
143:
144: @Override
145: public boolean isNull() {
146: return LowLevelSaf.isNull(jsval);
147: }
148:
149: @Override
150: public boolean isNumber() {
151: return LowLevelSaf.isNumber(jsval);
152: }
153:
154: public boolean isObject() {
155: return LowLevelSaf.isObject(jsval);
156: }
157:
158: @Override
159: public boolean isString() {
160: return LowLevelSaf.isString(jsval);
161: }
162:
163: @Override
164: public boolean isUndefined() {
165: return LowLevelSaf.isUndefined(jsval);
166: }
167:
168: @Override
169: public boolean isWrappedJavaObject() {
170: return LowLevelSaf.isWrappedDispatch(jsval);
171: }
172:
173: @Override
174: public void setBoolean(boolean val) {
175: setJsVal(LowLevelSaf.convertBoolean(val));
176: }
177:
178: @Override
179: public void setByte(byte val) {
180: setJsVal(LowLevelSaf.convertDouble(val));
181: }
182:
183: @Override
184: public void setChar(char val) {
185: setJsVal(LowLevelSaf.convertDouble(val));
186: }
187:
188: @Override
189: public void setDouble(double val) {
190: setJsVal(LowLevelSaf.convertDouble(val));
191: }
192:
193: @Override
194: public void setInt(int val) {
195: setJsVal(LowLevelSaf.convertDouble(val));
196: }
197:
198: /**
199: * Set a new value. Unlock the previous value, but do *not* lock the new
200: * value (see class comment).
201: *
202: * @param jsval the new value to set
203: */
204: public void setJsVal(int jsval) {
205: LowLevelSaf.gcUnlock(this .jsval, creationStackTrace);
206: init(jsval);
207: }
208:
209: @Override
210: public void setNull() {
211: setJsVal(LowLevelSaf.jsNull());
212: }
213:
214: @Override
215: public void setShort(short val) {
216: setJsVal(LowLevelSaf.convertDouble(val));
217: }
218:
219: @Override
220: public void setString(String val) {
221: setJsVal(LowLevelSaf.convertString(val));
222: }
223:
224: @Override
225: public void setUndefined() {
226: setJsVal(LowLevelSaf.jsUndefined());
227: }
228:
229: @Override
230: public void setValue(JsValue other) {
231: int jsvalOther = ((JsValueSaf) other).jsval;
232: /*
233: * Add another lock to this jsval, since both the other object and this
234: * one will eventually release it.
235: */
236: LowLevelSaf.gcLock(jsvalOther);
237: setJsVal(jsvalOther);
238: }
239:
240: @Override
241: public <T> void setWrappedJavaObject(CompilingClassLoader cl, T val) {
242: DispatchObject dispObj;
243: if (val == null) {
244: setNull();
245: return;
246: } else if (val instanceof DispatchObject) {
247: dispObj = (DispatchObject) val;
248: } else {
249: dispObj = new WebKitDispatchAdapter(cl, val);
250: }
251: setJsVal(LowLevelSaf.wrapDispatch(dispObj));
252: }
253:
254: @Override
255: protected JsCleanup createCleanupObject() {
256: return new JsCleanupSaf(jsval, creationStackTrace);
257: }
258:
259: /**
260: * Initialization helper method.
261: * @param jsval underlying JSValue*
262: */
263: private void init(int jsval) {
264: this .jsval = jsval;
265:
266: // only create and fill in the stack trace if we are debugging
267: if (LowLevelSaf.debugObjectCreation) {
268: this .creationStackTrace = new Throwable();
269: this.creationStackTrace.fillInStackTrace();
270: } else {
271: this.creationStackTrace = null;
272: }
273: }
274:
275: }
|