001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit.javascript;
039:
040: import org.apache.commons.logging.Log;
041: import org.apache.commons.logging.LogFactory;
042: import org.mozilla.javascript.Context;
043: import org.mozilla.javascript.NativeFunction;
044: import org.mozilla.javascript.Scriptable;
045: import org.mozilla.javascript.debug.DebugFrame;
046: import org.mozilla.javascript.debug.DebuggableScript;
047:
048: /**
049: * <p>
050: * HtmlUnit's implementation of the {@link DebugFrame} interface, which logs stack entries as well
051: * as exceptions. All logging is done at the <tt>TRACE</tt> level. This class does a fairly good
052: * job of guessing names for anonymous functions when they are referenced by name from an existing
053: * object. See <a href="http://www.mozilla.org/rhino/rhino15R4-debugger.html">the Rhino
054: * documentation</a> or <a
055: * href="http://lxr.mozilla.org/mozilla/source/js/rhino/src/org/mozilla/javascript/debug/DebugFrame.java">the
056: * interface source code</a> for more information on the {@link DebugFrame} interface and its uses.
057: * </p>
058: *
059: * <p>
060: * Please note that this class is intended mainly to aid in the debugging and development of
061: * HtmlUnit itself, rather than the debugging and development of web applications.
062: * </p>
063: *
064: * @version $Revision: 2132 $
065: * @author Daniel Gredler
066: * @see DebuggerImpl
067: */
068: public class DebugFrameImpl implements DebugFrame {
069:
070: private static final Log LOG = LogFactory
071: .getLog(DebugFrameImpl.class);
072:
073: private final DebuggableScript functionOrScript_;
074:
075: /**
076: * Creates a new debug frame.
077: *
078: * @param functionOrScript the function or script to which this frame corresponds
079: */
080: public DebugFrameImpl(final DebuggableScript functionOrScript) {
081: this .functionOrScript_ = functionOrScript;
082: }
083:
084: /**
085: * {@inheritDoc}
086: */
087: public void onEnter(final Context cx, final Scriptable activation,
088: final Scriptable this Obj, final Object[] args) {
089: if (LOG.isTraceEnabled()) {
090: final StringBuffer sb = new StringBuffer();
091: Scriptable parent = activation.getParentScope();
092: while (parent != null) {
093: sb.append(" ");
094: parent = parent.getParentScope();
095: }
096: sb.append(this .getFunctionName(this Obj)).append("(");
097: for (int i = 0; i < args.length; i++) {
098: sb.append(this .getParamName(i)).append(" : ").append(
099: args[i]);
100: if (i < args.length - 1) {
101: sb.append(", ");
102: }
103: }
104: sb.append(") @ line ").append(this .getFirstLine());
105: sb.append(" of ").append(this .getSourceName());
106: LOG.trace(sb);
107: }
108: }
109:
110: /**
111: * {@inheritDoc}
112: */
113: public void onExceptionThrown(final Context cx, final Throwable t) {
114: if (LOG.isTraceEnabled()) {
115: LOG.trace("Throwing exception: " + t.getMessage(), t);
116: }
117: }
118:
119: /**
120: * {@inheritDoc}
121: */
122: public void onExit(final Context cx, final boolean byThrow,
123: final Object resultOrException) {
124: // Ignore.
125: }
126:
127: /**
128: * {@inheritDoc}
129: */
130: public void onLineChange(final Context cx, final int lineNumber) {
131: // Ignore.
132: }
133:
134: /**
135: * Returns the name of the function corresponding to this frame, if it is a function and it has
136: * a name. If the function does not have a name, this method will try to return the name under
137: * which it was referenced. See <a
138: * href="http://www.digital-web.com/articles/scope_in_javascript/">this page</a> for a good
139: * explanation of how the <tt>thisObj</tt> plays into this guess.
140: *
141: * @param thisObj the object via which the function was referenced, used to try to guess a
142: * function name if the function is anonymous
143: * @return the name of the function corresponding to this frame
144: */
145: private String getFunctionName(final Scriptable this Obj) {
146: if (this .functionOrScript_.isFunction()) {
147: final String name = this .functionOrScript_
148: .getFunctionName();
149: if (name != null && name.length() > 0) {
150: // A named function -- we can just return the name.
151: return name;
152: } else {
153: // An anonymous function -- try to figure out how it was referenced.
154: // For example, someone may have set foo.prototype.bar = function() { ... };
155: // And then called fooInstance.bar() -- in which case it's "named" bar.
156: final Object[] ids = this Obj.getIds();
157: for (int i = 0; i < ids.length; i++) {
158: final Object id = ids[i];
159: if (id instanceof String) {
160: final String s = (String) id;
161: final Object o = this Obj.get(s, this Obj);
162: if (o instanceof NativeFunction) {
163: final NativeFunction f = (NativeFunction) o;
164: if (f.getDebuggableView() == this .functionOrScript_) {
165: return s;
166: }
167: }
168: }
169: }
170: // Unable to intuit a name -- doh!
171: return "[anonymous]";
172: }
173: } else {
174: // Just a script -- no function name.
175: return "[script]";
176: }
177: }
178:
179: /**
180: * Returns the name of the parameter at the specified index, or <tt>???</tt> if there is no
181: * corresponding name.
182: *
183: * @param index the index of the parameter whose name is to be returned
184: * @return the name of the parameter at the specified index, or <tt>???</tt> if there is no corresponding name
185: */
186: private String getParamName(final int index) {
187: if (index >= 0
188: && this .functionOrScript_.getParamCount() > index) {
189: return this .functionOrScript_.getParamOrVarName(index);
190: } else {
191: return "???";
192: }
193: }
194:
195: /**
196: * Returns the name of this frame's source.
197: *
198: * @return the name of this frame's source
199: */
200: private String getSourceName() {
201: return this .functionOrScript_.getSourceName().trim();
202: }
203:
204: /**
205: * Returns the line number of the first line in this frame's function or script, or <tt>???</tt>
206: * if it cannot be determined. This is necessary because the line numbers provided by Rhino are unordered.
207: *
208: * @return the line number of the first line in this frame's function or script, or <tt>???</tt>
209: * if it cannot be determined
210: */
211: private String getFirstLine() {
212: int first = Integer.MAX_VALUE;
213: final int[] lines = this .functionOrScript_.getLineNumbers();
214: for (int i = 0; i < lines.length; i++) {
215: final int current = lines[i];
216: if (current < first) {
217: first = current;
218: }
219: }
220: if (first != Integer.MAX_VALUE) {
221: return String.valueOf(first);
222: } else {
223: return "???";
224: }
225: }
226:
227: }
|