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