001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Norris Boyd
026: * Igor Bukanov
027: *
028: * Alternatively, the contents of this file may be used under the terms of
029: * the GNU General Public License Version 2 or later (the "GPL"), in which
030: * case the provisions of the GPL are applicable instead of those above. If
031: * you wish to allow use of your version of this file only under the terms of
032: * the GPL and not to allow others to use your version of this file under the
033: * MPL, indicate your decision by deleting the provisions above and replacing
034: * them with the notice and other provisions required by the GPL. If you do
035: * not delete the provisions above, a recipient may use your version of this
036: * file under either the MPL or the GPL.
037: *
038: * ***** END LICENSE BLOCK ***** */
039:
040: package org.mozilla.javascript;
041:
042: import java.io.CharArrayWriter;
043: import java.io.File;
044: import java.io.FilenameFilter;
045: import java.io.PrintStream;
046: import java.io.PrintWriter;
047: import java.util.List;
048: import java.util.ArrayList;
049:
050: /**
051: * The class of exceptions thrown by the JavaScript engine.
052: */
053: public abstract class RhinoException extends RuntimeException {
054: RhinoException() {
055: Evaluator e = Context.createInterpreter();
056: if (e != null)
057: e.captureStackInfo(this );
058: }
059:
060: RhinoException(String details) {
061: super (details);
062: Evaluator e = Context.createInterpreter();
063: if (e != null)
064: e.captureStackInfo(this );
065: }
066:
067: public final String getMessage() {
068: String details = details();
069: if (sourceName == null || lineNumber <= 0) {
070: return details;
071: }
072: StringBuffer buf = new StringBuffer(details);
073: buf.append(" (");
074: if (sourceName != null) {
075: buf.append(sourceName);
076: }
077: if (lineNumber > 0) {
078: buf.append('#');
079: buf.append(lineNumber);
080: }
081: buf.append(')');
082: return buf.toString();
083: }
084:
085: public String details() {
086: return super .getMessage();
087: }
088:
089: /**
090: * Get the uri of the script source containing the error, or null
091: * if that information is not available.
092: */
093: public final String sourceName() {
094: return sourceName;
095: }
096:
097: /**
098: * Initialize the uri of the script source containing the error.
099: *
100: * @param sourceName the uri of the script source responsible for the error.
101: * It should not be <tt>null</tt>.
102: *
103: * @throws IllegalStateException if the method is called more then once.
104: */
105: public final void initSourceName(String sourceName) {
106: if (sourceName == null)
107: throw new IllegalArgumentException();
108: if (this .sourceName != null)
109: throw new IllegalStateException();
110: this .sourceName = sourceName;
111: }
112:
113: /**
114: * Returns the line number of the statement causing the error,
115: * or zero if not available.
116: */
117: public final int lineNumber() {
118: return lineNumber;
119: }
120:
121: /**
122: * Initialize the line number of the script statement causing the error.
123: *
124: * @param lineNumber the line number in the script source.
125: * It should be positive number.
126: *
127: * @throws IllegalStateException if the method is called more then once.
128: */
129: public final void initLineNumber(int lineNumber) {
130: if (lineNumber <= 0)
131: throw new IllegalArgumentException(String
132: .valueOf(lineNumber));
133: if (this .lineNumber > 0)
134: throw new IllegalStateException();
135: this .lineNumber = lineNumber;
136: }
137:
138: /**
139: * The column number of the location of the error, or zero if unknown.
140: */
141: public final int columnNumber() {
142: return columnNumber;
143: }
144:
145: /**
146: * Initialize the column number of the script statement causing the error.
147: *
148: * @param columnNumber the column number in the script source.
149: * It should be positive number.
150: *
151: * @throws IllegalStateException if the method is called more then once.
152: */
153: public final void initColumnNumber(int columnNumber) {
154: if (columnNumber <= 0)
155: throw new IllegalArgumentException(String
156: .valueOf(columnNumber));
157: if (this .columnNumber > 0)
158: throw new IllegalStateException();
159: this .columnNumber = columnNumber;
160: }
161:
162: /**
163: * The source text of the line causing the error, or null if unknown.
164: */
165: public final String lineSource() {
166: return lineSource;
167: }
168:
169: /**
170: * Initialize the text of the source line containing the error.
171: *
172: * @param lineSource the text of the source line responsible for the error.
173: * It should not be <tt>null</tt>.
174: *
175: * @throws IllegalStateException if the method is called more then once.
176: */
177: public final void initLineSource(String lineSource) {
178: if (lineSource == null)
179: throw new IllegalArgumentException();
180: if (this .lineSource != null)
181: throw new IllegalStateException();
182: this .lineSource = lineSource;
183: }
184:
185: final void recordErrorOrigin(String sourceName, int lineNumber,
186: String lineSource, int columnNumber) {
187: // XXX: for compatibility allow for now -1 to mean 0
188: if (lineNumber == -1) {
189: lineNumber = 0;
190: }
191:
192: if (sourceName != null) {
193: initSourceName(sourceName);
194: }
195: if (lineNumber != 0) {
196: initLineNumber(lineNumber);
197: }
198: if (lineSource != null) {
199: initLineSource(lineSource);
200: }
201: if (columnNumber != 0) {
202: initColumnNumber(columnNumber);
203: }
204: }
205:
206: private String generateStackTrace() {
207: // Get stable reference to work properly with concurrent access
208: CharArrayWriter writer = new CharArrayWriter();
209: super .printStackTrace(new PrintWriter(writer));
210: String origStackTrace = writer.toString();
211: Evaluator e = Context.createInterpreter();
212: if (e != null)
213: return e.getPatchedStack(this , origStackTrace);
214: return null;
215: }
216:
217: /**
218: * Get a string representing the script stack of this exception.
219: * If optimization is enabled, this corresponds to all java stack elements
220: * with a source name ending with ".js".
221: * @return a script stack dump
222: * @since 1.6R6
223: */
224: public String getScriptStackTrace() {
225: return getScriptStackTrace(new FilenameFilter() {
226: public boolean accept(File dir, String name) {
227: return name.endsWith(".js");
228: }
229: });
230: }
231:
232: /**
233: * Get a string representing the script stack of this exception.
234: * If optimization is enabled, this corresponds to all java stack elements
235: * with a source name matching the <code>filter</code>.
236: * @param filter the file name filter to determine whether a file is a
237: * script file
238: * @return a script stack dump
239: * @since 1.6R6
240: */
241: public String getScriptStackTrace(FilenameFilter filter) {
242: List interpreterStack;
243: Evaluator interpreter = Context.createInterpreter();
244: if (interpreter != null)
245: interpreterStack = interpreter.getScriptStack(this );
246: else
247: interpreterStack = new ArrayList();
248: int interpreterStackIndex = 0;
249: StringBuffer buffer = new StringBuffer();
250: String lineSeparator = SecurityUtilities
251: .getSystemProperty("line.separator");
252: StackTraceElement[] stack = getStackTrace();
253: for (int i = 0; i < stack.length; i++) {
254: StackTraceElement e = stack[i];
255: String name = e.getFileName();
256: if (e.getLineNumber() > -1 && name != null
257: && filter.accept(null, name)) {
258: buffer.append("\tat ");
259: buffer.append(e.getFileName());
260: buffer.append(':');
261: buffer.append(e.getLineNumber());
262: buffer.append(lineSeparator);
263: } else if (interpreterStack != null
264: && "org.mozilla.javascript.Interpreter".equals(e
265: .getClassName())
266: && "interpretLoop".equals(e.getMethodName())) {
267: buffer.append(interpreterStack
268: .get(interpreterStackIndex++));
269: }
270: }
271: return buffer.toString();
272: }
273:
274: public void printStackTrace(PrintWriter s) {
275: if (interpreterStackInfo == null) {
276: super .printStackTrace(s);
277: } else {
278: s.print(generateStackTrace());
279: }
280: }
281:
282: public void printStackTrace(PrintStream s) {
283: if (interpreterStackInfo == null) {
284: super .printStackTrace(s);
285: } else {
286: s.print(generateStackTrace());
287: }
288: }
289:
290: private String sourceName;
291: private int lineNumber;
292: private String lineSource;
293: private int columnNumber;
294:
295: Object interpreterStackInfo;
296: int[] interpreterLineData;
297: }
|