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 @Override
92 public Object getThrown() {
93 return thrown;
94 }
95
96 @Override
97 public String toString() {
98 final StringBuilder sb = new StringBuilder();
99 final String fileName = getFileName();
100 final int line = getLineNumber();
101 final int column = getColumnNumber();
102
103 if (fileName != null) {
104 sb.append(fileName);
105 if (line >= 0) {
106 sb.append(':');
107 sb.append(line);
108 }
109 if (column >= 0) {
110 sb.append(':');
111 sb.append(column);
112 }
113 sb.append(' ');
114 } else {
115 sb.append("ECMAScript Exception: ");
116 }
117
118 sb.append(getMessage());
119 return sb.toString();
120 }
121
122 /**
123 * Get the {@link ECMAException}, i.e. the underlying Java object for the
124 * JavaScript error object from a {@link ScriptObject} representing an error
125 *
126 * @param errObj the error object
127 * @return a {@link ECMAException}
128 */
129 public static Object getException(final ScriptObject errObj) {
130 return errObj.get(ECMAException.EXCEPTION_PROPERTY);
131 }
132
133 /**
134 * Print the stack trace for a {@code ScriptObject} representing an error
135 *
136 * @param errObj the error object
137 * @return undefined
138 */
139 public static Object printStackTrace(final ScriptObject errObj) {
140 final Object exception = getException(errObj);
141 if (exception instanceof Throwable) {
142 ((Throwable)exception).printStackTrace(Context.getCurrentErr());
143 } else {
144 Context.err("<stack trace not available>");
145 }
146 return UNDEFINED;
147 }
148
149 /**
150 * Get the line number for a {@code ScriptObject} representing an error
151 *
152 * @param errObj the error object
153 * @return the line number, or undefined if wrapped exception is not a ParserException
154 */
155 public static Object getLineNumber(final ScriptObject errObj) {
156 final Object e = getException(errObj);
157 if (e instanceof NashornException) {
158 return ((NashornException)e).getLineNumber();
159 } else if (e instanceof ScriptException) {
160 return ((ScriptException)e).getLineNumber();
161 }
162
163 return UNDEFINED;
164 }
165
166 /**
167 * Get the column number for a {@code ScriptObject} representing an error
168 *
169 * @param errObj the error object
170 * @return the column number, or undefined if wrapped exception is not a ParserException
171 */
172 public static Object getColumnNumber(final ScriptObject errObj) {
173 final Object e = getException(errObj);
174 if (e instanceof NashornException) {
175 return ((NashornException)e).getColumnNumber();
176 } else if (e instanceof ScriptException) {
177 return ((ScriptException)e).getColumnNumber();
178 }
179
180 return UNDEFINED;
181 }
182
183 /**
184 * Get the file name for a {@code ScriptObject} representing an error
185 *
186 * @param errObj the error object
187 * @return the file name, or undefined if wrapped exception is not a ParserException
188 */
189 public static Object getFileName(final ScriptObject errObj) {
190 final Object e = getException(errObj);
191 if (e instanceof NashornException) {
192 return ((NashornException)e).getFileName();
193 } else if (e instanceof ScriptException) {
194 return ((ScriptException)e).getFileName();
195 }
196
197 return UNDEFINED;
198 }
199
200 /**
201 * Stateless string conversion for an error object
202 *
203 * @param errObj the error object
204 * @return string representation of {@code errObj}
205 */
206 public static String safeToString(final ScriptObject errObj) {
207 Object name = UNDEFINED;
208 try {
209 name = errObj.get("name");
210 } catch (final Exception e) {
211 //ignored
212 }
213
214 if (name == UNDEFINED) {
215 name = "Error";
216 } else {
217 name = ScriptRuntime.safeToString(name);
218 }
219
220 Object msg = UNDEFINED;
221 try {
222 msg = errObj.get("message");
223 } catch (final Exception e) {
224 //ignored
225 }
226
227 if (msg == UNDEFINED) {
228 msg = "";
229 } else {
230 msg = ScriptRuntime.safeToString(msg);
231 }
232
233 if (((String)name).isEmpty()) {
234 return (String)msg;
235 }
236
237 if (((String)msg).isEmpty()) {
238 return (String)name;
239 }
240
241 return name + ": " + msg;
242 }
243
244 private static Throwable asThrowable(final Object obj) {
245 return (obj instanceof Throwable)? (Throwable)obj : null;
246 }
247
248 private void setExceptionToThrown() {
249 /*
250 * Nashorn extension: errorObject.nashornException
251 * Expose this exception via "nashornException" property of on the
252 * thrown object. This exception object can be used to print stack
253 * trace and fileName, line number etc. from script code.
254 */
255
256 if (thrown instanceof ScriptObject) {
257 final ScriptObject sobj = (ScriptObject)thrown;
258 if (!sobj.has(EXCEPTION_PROPERTY)) {
259 sobj.addOwnProperty(EXCEPTION_PROPERTY, Property.NOT_ENUMERABLE, this);
260 }
261 }
262 }
263 }
--- EOF ---