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 */
25
26 package jdk.nashorn.internal.runtime;
27
28 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualField;
30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
31
32 import javax.script.ScriptException;
33 import jdk.nashorn.api.scripting.NashornException;
34 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
35 import jdk.nashorn.internal.codegen.CompilerConstants.FieldAccess;
36 import jdk.nashorn.internal.objects.NativeError;
37
38 /**
39 * Exception used to implement ECMAScript "throw" from scripts. The actual thrown
40 * object from script need not be a Java exception and so it is wrapped as an
41 * instance field called "thrown" here. This exception class is also used to
42 * represent ECMA errors thrown from runtime code (for example, TypeError,
43 * ReferenceError thrown from Nashorn engine runtime).
44 */
45 @SuppressWarnings("serial")
46 public final class ECMAException extends NashornException {
47 /**
48 * Method handle pointing to the constructor {@link ECMAException#create(Object, String, int, int)},
49 */
50 public static final Call CREATE = staticCallNoLookup(ECMAException.class, "create", ECMAException.class, Object.class, String.class, int.class, int.class);
51
52 /** Field handle to the{@link ECMAException#thrown} field, so that it can be accessed from generated code */
53 public static final FieldAccess THROWN = virtualField(ECMAException.class, "thrown", Object.class);
54
55 private static final String EXCEPTION_PROPERTY = "nashornException";
56
57 /** Object thrown. */
58 public final Object thrown;
59
60 /**
61 * Constructor. Called from the factory method 'create'.
62 *
63 * @param thrown object to be thrown
64 * @param fileName script file name
65 * @param line line number of throw
66 * @param column column number of throw
67 */
68 private ECMAException(final Object thrown, final String fileName, final int line, final int column) {
69 super(ScriptRuntime.safeToString(thrown), asThrowable(thrown), fileName, line, column);
70 this.thrown = thrown;
71 setExceptionToThrown();
72 }
73
74 /**
75 * Constructor. This is called from the runtime code.
76 *
77 * @param thrown object to be thrown
78 * @param cause Java exception that triggered this throw
79 */
80 public ECMAException(final Object thrown, final Throwable cause) {
81 super(ScriptRuntime.safeToString(thrown), cause);
82 this.thrown = thrown;
83 setExceptionToThrown();
84 }
85
86 /**
87 * Factory method to retrieve the underlying exception or create an exception.
88 * This method is called from the generated code.
89 *
90 * @param thrown object to be thrown
91 * @param fileName script file name
92 * @param line line number of throw
93 * @param column column number of throw
94 * @return ECMAException object
95 */
96 public static ECMAException create(final Object thrown, final String fileName, final int line, final int column) {
97 // If thrown object is an Error or sub-object like TypeError, then
98 // an ECMAException object has been already initialized at constructor.
99 if (thrown instanceof ScriptObject) {
100 ScriptObject sobj = (ScriptObject)thrown;
101 Object exception = getException(sobj);
102 if (exception instanceof ECMAException) {
103 // copy over file name, line number and column number.
104 final ECMAException ee = (ECMAException)exception;
105 ee.setFileName(fileName);
106 ee.setLineNumber(line);
107 ee.setColumnNumber(column);
108 return ee;
109 }
110 }
111
112 return new ECMAException(thrown, fileName, line, column);
113 }
114
115 /**
116 * Get the thrown object
117 * @return thrown object
118 */
119 @Override
120 public Object getThrown() {
121 return thrown;
122 }
123
124 @Override
125 public String toString() {
126 final StringBuilder sb = new StringBuilder();
127 final String fileName = getFileName();
128 final int line = getLineNumber();
129 final int column = getColumnNumber();
130
131 if (fileName != null) {
132 sb.append(fileName);
133 if (line >= 0) {
134 sb.append(':');
135 sb.append(line);
136 }
137 if (column >= 0) {
138 sb.append(':');
139 sb.append(column);
140 }
141 sb.append(' ');
142 } else {
143 sb.append("ECMAScript Exception: ");
144 }
145
146 sb.append(getMessage());
147 return sb.toString();
148 }
149
150 /**
151 * Get the {@link ECMAException}, i.e. the underlying Java object for the
152 * JavaScript error object from a {@link ScriptObject} representing an error
153 *
154 * @param errObj the error object
155 * @return a {@link ECMAException}
156 */
157 public static Object getException(final ScriptObject errObj) {
158 return errObj.get(ECMAException.EXCEPTION_PROPERTY);
159 }
160
161 /**
162 * Print the stack trace for a {@code ScriptObject} representing an error
163 *
164 * @param errObj the error object
165 * @return undefined
166 */
167 public static Object printStackTrace(final ScriptObject errObj) {
168 final Object exception = getException(errObj);
169 if (exception instanceof Throwable) {
170 ((Throwable)exception).printStackTrace(Context.getCurrentErr());
171 } else {
172 Context.err("<stack trace not available>");
173 }
174 return UNDEFINED;
175 }
176
177 /**
178 * Get the line number for a {@code ScriptObject} representing an error
179 *
180 * @param errObj the error object
181 * @return the line number, or undefined if wrapped exception is not a ParserException
182 */
183 public static Object getLineNumber(final ScriptObject errObj) {
184 final Object e = getException(errObj);
185 if (e instanceof NashornException) {
186 return ((NashornException)e).getLineNumber();
187 } else if (e instanceof ScriptException) {
188 return ((ScriptException)e).getLineNumber();
189 }
190
191 return UNDEFINED;
192 }
193
194 /**
195 * Get the column number for a {@code ScriptObject} representing an error
196 *
197 * @param errObj the error object
198 * @return the column number, or undefined if wrapped exception is not a ParserException
199 */
200 public static Object getColumnNumber(final ScriptObject errObj) {
201 final Object e = getException(errObj);
202 if (e instanceof NashornException) {
203 return ((NashornException)e).getColumnNumber();
204 } else if (e instanceof ScriptException) {
205 return ((ScriptException)e).getColumnNumber();
206 }
207
208 return UNDEFINED;
209 }
210
211 /**
212 * Get the file name for a {@code ScriptObject} representing an error
213 *
214 * @param errObj the error object
215 * @return the file name, or undefined if wrapped exception is not a ParserException
216 */
217 public static Object getFileName(final ScriptObject errObj) {
218 final Object e = getException(errObj);
219 if (e instanceof NashornException) {
220 return ((NashornException)e).getFileName();
221 } else if (e instanceof ScriptException) {
222 return ((ScriptException)e).getFileName();
223 }
224
225 return UNDEFINED;
226 }
227
228 /**
229 * Stateless string conversion for an error object
230 *
231 * @param errObj the error object
232 * @return string representation of {@code errObj}
233 */
234 public static String safeToString(final ScriptObject errObj) {
235 Object name = UNDEFINED;
236 try {
237 name = errObj.get("name");
238 } catch (final Exception e) {
239 //ignored
240 }
241
242 if (name == UNDEFINED) {
243 name = "Error";
244 } else {
245 name = ScriptRuntime.safeToString(name);
246 }
247
248 Object msg = UNDEFINED;
249 try {
250 msg = errObj.get("message");
251 } catch (final Exception e) {
252 //ignored
253 }
254
255 if (msg == UNDEFINED) {
256 msg = "";
257 } else {
258 msg = ScriptRuntime.safeToString(msg);
259 }
260
261 if (((String)name).isEmpty()) {
262 return (String)msg;
263 }
264
265 if (((String)msg).isEmpty()) {
266 return (String)name;
267 }
268
269 return name + ": " + msg;
270 }
271
272 private static Throwable asThrowable(final Object obj) {
273 return (obj instanceof Throwable)? (Throwable)obj : null;
274 }
275
276 private void setExceptionToThrown() {
277 /*
278 * Nashorn extension: errorObject.nashornException
279 * Expose this exception via "nashornException" property of on the
280 * thrown object. This exception object can be used to print stack
281 * trace and fileName, line number etc. from script code.
282 */
283
284 if (thrown instanceof ScriptObject) {
285 final ScriptObject sobj = (ScriptObject)thrown;
286 if (!sobj.has(EXCEPTION_PROPERTY)) {
287 sobj.addOwnProperty(EXCEPTION_PROPERTY, Property.NOT_ENUMERABLE, this);
288 } else {
289 sobj.set(EXCEPTION_PROPERTY, this, false);
290 }
291 }
292 }
293 }
--- EOF ---