1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.struts.flow.core.javascript;
17
18 import java.util.ArrayList;
19 import java.util.List;
20
21 import org.apache.struts.flow.core.FlowException;
22 import org.apache.struts.flow.core.location.Location;
23 import org.apache.struts.flow.core.location.LocationImpl;
24 import org.apache.struts.flow.core.location.LocationUtils;
25 import org.apache.commons.lang.exception.ExceptionUtils;
26 import org.mozilla.javascript.Context;
27 import org.mozilla.javascript.EcmaError;
28 import org.mozilla.javascript.JavaScriptException;
29 import org.mozilla.javascript.Scriptable;
30 import org.mozilla.javascript.debug.DebugFrame;
31 import org.mozilla.javascript.debug.DebuggableScript;
32 import org.mozilla.javascript.debug.Debugger;
33
34 /***
35 * A Rhino debugger that tracks location information when an exception is raised in some JavaScript code.
36 * It's purpose is to build a {@link org.apache.flow.core.FlowException} that holds the stacktrace
37 * in the JavaScript code.
38 * <p>
39 * This debugger implementation is designed to be as lightweight and fast as possible, in order to have a
40 * negligible impact on the performances of the Rhino interpreter.
41 *
42 * @since 2.1.8
43 * @version $Id: LocationTrackingDebugger.java 280811 2005-09-14 09:53:32Z sylvain $
44 */
45 public class LocationTrackingDebugger implements Debugger {
46
47
48 private static final LocationUtils.LocationFinder rhinoLocFinder = new LocationUtils.LocationFinder() {
49
50 public Location getLocation(Object obj, String description) {
51 if (obj instanceof EcmaError) {
52 EcmaError ex = (EcmaError)obj;
53 if (ex.getSourceName() != null) {
54 return new LocationImpl(ex.getName(), ex.getSourceName(), ex.getLineNumber(), ex.getColumnNumber());
55 } else {
56 return Location.UNKNOWN;
57 }
58
59 } else if (obj instanceof JavaScriptException) {
60 JavaScriptException ex = (JavaScriptException)obj;
61 if (ex.sourceName() != null) {
62 return new LocationImpl(description, ex.sourceName(), ex.lineNumber(), -1);
63 } else {
64 return Location.UNKNOWN;
65 }
66 }
67
68 return null;
69 }
70 };
71
72
73 static {
74
75 ExceptionUtils.addCauseMethodName("getWrappedException");
76 LocationUtils.addFinder(rhinoLocFinder);
77 }
78
79 private List locations;
80 private Throwable throwable;
81
82 public void handleCompilationDone(Context cx, DebuggableScript fnOrScript, String source) {
83
84 }
85
86 public DebugFrame getFrame(Context cx, DebuggableScript fnOrScript) {
87 return new StackTrackingFrame(fnOrScript);
88 }
89
90 /***
91 * Get an exception that reflects the known location stack
92 *
93 * @param description a description for the exception
94 * @param originalException the original exception
95 *
96 * @return a suitable exception to throw
97 * @see ProcessingException#throwLocated(String, Throwable, Location)
98 */
99 public Exception getException(String description, Exception originalException) throws FlowException {
100 if (throwable == null || locations == null) {
101
102 return originalException;
103 }
104
105
106
107 Throwable cause = ExceptionUtils.getCause(throwable);
108 if (cause != null)
109 throwable = cause;
110
111 return FlowException.throwLocated(description, throwable, locations);
112 }
113
114 private class StackTrackingFrame implements DebugFrame {
115
116 DebuggableScript script;
117 int line;
118
119 public StackTrackingFrame(DebuggableScript script) {
120 this.script = script;
121 }
122
123 public void onEnter(Context cx, Scriptable activation, Scriptable thisObj, Object[] args) {
124
125 }
126
127 public void onLineChange(Context cx, int lineNumber) {
128 line = lineNumber;
129 }
130
131 public void onExceptionThrown(Context cx, Throwable ex) {
132 throwable = ex;
133 }
134
135 public void onExit(Context cx, boolean byThrow, Object resultOrException) {
136 if (byThrow) {
137 String name = null;
138 if (script.isFunction()) {
139 name = script.getFunctionName();
140 } else {
141 name = "[script]";
142 }
143
144 if (locations == null) {
145 locations = new ArrayList(1);
146 }
147
148 locations.add(new LocationImpl(name, script.getSourceName(), line, -1));
149
150 } else if (locations != null) {
151
152 locations = null;
153 }
154 }
155 }
156 }
157