Thu, 12 Oct 2017 19:52:15 +0800
merge
1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package jdk.nashorn.api.scripting;
28 import java.util.ArrayList;
29 import java.util.List;
30 import jdk.nashorn.internal.codegen.CompilerConstants;
31 import jdk.nashorn.internal.runtime.ECMAErrors;
32 import jdk.nashorn.internal.runtime.ScriptObject;
34 /**
35 * This is base exception for all Nashorn exceptions. These originate from
36 * user's ECMAScript code. Example: script parse errors, exceptions thrown from
37 * scripts. Note that ScriptEngine methods like "eval", "invokeMethod",
38 * "invokeFunction" will wrap this as ScriptException and throw it. But, there
39 * are cases where user may need to access this exception (or implementation
40 * defined subtype of this). For example, if java interface is implemented by a
41 * script object or Java access to script object properties via java.util.Map
42 * interface. In these cases, user code will get an instance of this or
43 * implementation defined subclass.
44 */
45 @SuppressWarnings("serial")
46 public abstract class NashornException extends RuntimeException {
47 // script file name
48 private String fileName;
49 // script line number
50 private int line;
51 // script column number
52 private int column;
53 // underlying ECMA error object - lazily initialized
54 private Object ecmaError;
56 /**
57 * Constructor
58 *
59 * @param msg exception message
60 * @param fileName file name
61 * @param line line number
62 * @param column column number
63 */
64 protected NashornException(final String msg, final String fileName, final int line, final int column) {
65 this(msg, null, fileName, line, column);
66 }
68 /**
69 * Constructor
70 *
71 * @param msg exception message
72 * @param cause exception cause
73 * @param fileName file name
74 * @param line line number
75 * @param column column number
76 */
77 protected NashornException(final String msg, final Throwable cause, final String fileName, final int line, final int column) {
78 super(msg, cause == null ? null : cause);
79 this.fileName = fileName;
80 this.line = line;
81 this.column = column;
82 }
84 /**
85 * Constructor
86 *
87 * @param msg exception message
88 * @param cause exception cause
89 */
90 protected NashornException(final String msg, final Throwable cause) {
91 super(msg, cause == null ? null : cause);
92 // This is not so pretty - but it gets the job done. Note that the stack
93 // trace has been already filled by "fillInStackTrace" call from
94 // Throwable
95 // constructor and so we don't pay additional cost for it.
97 // Hard luck - no column number info
98 this.column = -1;
100 // Find the first JavaScript frame by walking and set file, line from it
101 // Usually, we should be able to find it in just few frames depth.
102 for (final StackTraceElement ste : getStackTrace()) {
103 if (ECMAErrors.isScriptFrame(ste)) {
104 // Whatever here is compiled from JavaScript code
105 this.fileName = ste.getFileName();
106 this.line = ste.getLineNumber();
107 return;
108 }
109 }
111 this.fileName = null;
112 this.line = 0;
113 }
115 /**
116 * Get the source file name for this {@code NashornException}
117 *
118 * @return the file name
119 */
120 public final String getFileName() {
121 return fileName;
122 }
124 /**
125 * Set the source file name for this {@code NashornException}
126 *
127 * @param fileName the file name
128 */
129 public final void setFileName(final String fileName) {
130 this.fileName = fileName;
131 }
133 /**
134 * Get the line number for this {@code NashornException}
135 *
136 * @return the line number
137 */
138 public final int getLineNumber() {
139 return line;
140 }
142 /**
143 * Set the line number for this {@code NashornException}
144 *
145 * @param line the line number
146 */
147 public final void setLineNumber(final int line) {
148 this.line = line;
149 }
151 /**
152 * Get the column for this {@code NashornException}
153 *
154 * @return the column number
155 */
156 public final int getColumnNumber() {
157 return column;
158 }
160 /**
161 * Set the column for this {@code NashornException}
162 *
163 * @param column the column number
164 */
165 public final void setColumnNumber(final int column) {
166 this.column = column;
167 }
169 /**
170 * Returns array javascript stack frames from the given exception object.
171 *
172 * @param exception exception from which stack frames are retrieved and filtered
173 * @return array of javascript stack frames
174 */
175 public static StackTraceElement[] getScriptFrames(final Throwable exception) {
176 final StackTraceElement[] frames = exception.getStackTrace();
177 final List<StackTraceElement> filtered = new ArrayList<>();
178 for (final StackTraceElement st : frames) {
179 if (ECMAErrors.isScriptFrame(st)) {
180 final String className = "<" + st.getFileName() + ">";
181 String methodName = st.getMethodName();
182 if (methodName.equals(CompilerConstants.PROGRAM.symbolName())) {
183 methodName = "<program>";
184 }
186 if (methodName.contains(CompilerConstants.ANON_FUNCTION_PREFIX.symbolName())) {
187 methodName = "<anonymous>";
188 }
190 filtered.add(new StackTraceElement(className, methodName,
191 st.getFileName(), st.getLineNumber()));
192 }
193 }
194 return filtered.toArray(new StackTraceElement[filtered.size()]);
195 }
197 /**
198 * Return a formatted script stack trace string with frames information separated by '\n'
199 *
200 * @param exception exception for which script stack string is returned
201 * @return formatted stack trace string
202 */
203 public static String getScriptStackString(final Throwable exception) {
204 final StringBuilder buf = new StringBuilder();
205 final StackTraceElement[] frames = getScriptFrames(exception);
206 for (final StackTraceElement st : frames) {
207 buf.append("\tat ");
208 buf.append(st.getMethodName());
209 buf.append(" (");
210 buf.append(st.getFileName());
211 buf.append(':');
212 buf.append(st.getLineNumber());
213 buf.append(")\n");
214 }
215 final int len = buf.length();
216 // remove trailing '\n'
217 if (len > 0) {
218 assert buf.charAt(len - 1) == '\n';
219 buf.deleteCharAt(len - 1);
220 }
221 return buf.toString();
222 }
224 /**
225 * Get the thrown object. Subclass responsibility
226 * @return thrown object
227 */
228 protected Object getThrown() {
229 return null;
230 }
232 /**
233 * Initialization function for ECMA errors. Stores the error
234 * in the ecmaError field of this class. It is only initialized
235 * once, and then reused
236 *
237 * @param global the global
238 * @return initialized exception
239 */
240 protected NashornException initEcmaError(final ScriptObject global) {
241 if (ecmaError != null) {
242 return this; // initialized already!
243 }
245 final Object thrown = getThrown();
246 if (thrown instanceof ScriptObject) {
247 setEcmaError(ScriptObjectMirror.wrap(thrown, global));
248 } else {
249 setEcmaError(thrown);
250 }
252 return this;
253 }
255 /**
256 * Return the underlying ECMA error object, if available.
257 *
258 * @return underlying ECMA Error object's mirror or whatever was thrown
259 * from script such as a String, Number or a Boolean.
260 */
261 public Object getEcmaError() {
262 return ecmaError;
263 }
265 /**
266 * Return the underlying ECMA error object, if available.
267 *
268 * @param ecmaError underlying ECMA Error object's mirror or whatever was thrown
269 * from script such as a String, Number or a Boolean.
270 */
271 public void setEcmaError(final Object ecmaError) {
272 this.ecmaError = ecmaError;
273 }
274 }