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 ---