NashornException.java revision 1612:0da44ab8c417
1/* 2 * Copyright (c) 2010, 2016, 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 26package jdk.nashorn.api.scripting; 27 28import java.util.ArrayList; 29import java.util.List; 30import jdk.nashorn.internal.codegen.CompilerConstants; 31import jdk.nashorn.internal.runtime.ECMAErrors; 32import jdk.nashorn.internal.runtime.ScriptObject; 33 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 * @since 1.8u40 46 */ 47@SuppressWarnings("serial") 48public abstract class NashornException extends RuntimeException { 49 // script file name 50 private String fileName; 51 // script line number 52 private int line; 53 // are the line and fileName unknown? 54 private boolean lineAndFileNameUnknown; 55 // script column number 56 private int column; 57 // underlying ECMA error object - lazily initialized 58 private Object ecmaError; 59 60 /** 61 * Constructor 62 * 63 * @param msg exception message 64 * @param fileName file name 65 * @param line line number 66 * @param column column number 67 */ 68 protected NashornException(final String msg, final String fileName, final int line, final int column) { 69 this(msg, null, fileName, line, column); 70 } 71 72 /** 73 * Constructor 74 * 75 * @param msg exception message 76 * @param cause exception cause 77 * @param fileName file name 78 * @param line line number 79 * @param column column number 80 */ 81 protected NashornException(final String msg, final Throwable cause, final String fileName, final int line, final int column) { 82 super(msg, cause == null ? null : cause); 83 this.fileName = fileName; 84 this.line = line; 85 this.column = column; 86 } 87 88 /** 89 * Constructor 90 * 91 * @param msg exception message 92 * @param cause exception cause 93 */ 94 protected NashornException(final String msg, final Throwable cause) { 95 super(msg, cause == null ? null : cause); 96 // Hard luck - no column number info 97 this.column = -1; 98 // We can retrieve the line number and file name from the stack trace if needed 99 this.lineAndFileNameUnknown = true; 100 } 101 102 /** 103 * Get the source file name for this {@code NashornException} 104 * 105 * @return the file name 106 */ 107 public final String getFileName() { 108 ensureLineAndFileName(); 109 return fileName; 110 } 111 112 /** 113 * Set the source file name for this {@code NashornException} 114 * 115 * @param fileName the file name 116 */ 117 public final void setFileName(final String fileName) { 118 this.fileName = fileName; 119 lineAndFileNameUnknown = false; 120 } 121 122 /** 123 * Get the line number for this {@code NashornException} 124 * 125 * @return the line number 126 */ 127 public final int getLineNumber() { 128 ensureLineAndFileName(); 129 return line; 130 } 131 132 /** 133 * Set the line number for this {@code NashornException} 134 * 135 * @param line the line number 136 */ 137 public final void setLineNumber(final int line) { 138 lineAndFileNameUnknown = false; 139 this.line = line; 140 } 141 142 /** 143 * Get the column for this {@code NashornException} 144 * 145 * @return the column number 146 */ 147 public final int getColumnNumber() { 148 return column; 149 } 150 151 /** 152 * Set the column for this {@code NashornException} 153 * 154 * @param column the column number 155 */ 156 public final void setColumnNumber(final int column) { 157 this.column = column; 158 } 159 160 /** 161 * Returns array javascript stack frames from the given exception object. 162 * 163 * @param exception exception from which stack frames are retrieved and filtered 164 * @return array of javascript stack frames 165 */ 166 public static StackTraceElement[] getScriptFrames(final Throwable exception) { 167 final StackTraceElement[] frames = exception.getStackTrace(); 168 final List<StackTraceElement> filtered = new ArrayList<>(); 169 for (final StackTraceElement st : frames) { 170 if (ECMAErrors.isScriptFrame(st)) { 171 final String className = "<" + st.getFileName() + ">"; 172 String methodName = st.getMethodName(); 173 if (methodName.equals(CompilerConstants.PROGRAM.symbolName())) { 174 methodName = "<program>"; 175 } 176 177 if (methodName.contains(CompilerConstants.ANON_FUNCTION_PREFIX.symbolName())) { 178 methodName = "<anonymous>"; 179 } 180 181 filtered.add(new StackTraceElement(className, methodName, 182 st.getFileName(), st.getLineNumber())); 183 } 184 } 185 return filtered.toArray(new StackTraceElement[0]); 186 } 187 188 /** 189 * Return a formatted script stack trace string with frames information separated by '\n' 190 * 191 * @param exception exception for which script stack string is returned 192 * @return formatted stack trace string 193 */ 194 public static String getScriptStackString(final Throwable exception) { 195 final StringBuilder buf = new StringBuilder(); 196 final StackTraceElement[] frames = getScriptFrames(exception); 197 for (final StackTraceElement st : frames) { 198 buf.append("\tat "); 199 buf.append(st.getMethodName()); 200 buf.append(" ("); 201 buf.append(st.getFileName()); 202 buf.append(':'); 203 buf.append(st.getLineNumber()); 204 buf.append(")\n"); 205 } 206 final int len = buf.length(); 207 // remove trailing '\n' 208 if (len > 0) { 209 assert buf.charAt(len - 1) == '\n'; 210 buf.deleteCharAt(len - 1); 211 } 212 return buf.toString(); 213 } 214 215 /** 216 * Get the thrown object. Subclass responsibility 217 * @return thrown object 218 */ 219 protected Object getThrown() { 220 return null; 221 } 222 223 /** 224 * Initialization function for ECMA errors. Stores the error 225 * in the ecmaError field of this class. It is only initialized 226 * once, and then reused 227 * 228 * @param global the global 229 * @return initialized exception 230 */ 231 protected NashornException initEcmaError(final ScriptObject global) { 232 if (ecmaError != null) { 233 return this; // initialized already! 234 } 235 236 final Object thrown = getThrown(); 237 if (thrown instanceof ScriptObject) { 238 setEcmaError(ScriptObjectMirror.wrap(thrown, global)); 239 } else { 240 setEcmaError(thrown); 241 } 242 243 return this; 244 } 245 246 /** 247 * Return the underlying ECMA error object, if available. 248 * 249 * @return underlying ECMA Error object's mirror or whatever was thrown 250 * from script such as a String, Number or a Boolean. 251 */ 252 public Object getEcmaError() { 253 return ecmaError; 254 } 255 256 /** 257 * Return the underlying ECMA error object, if available. 258 * 259 * @param ecmaError underlying ECMA Error object's mirror or whatever was thrown 260 * from script such as a String, Number or a Boolean. 261 */ 262 public void setEcmaError(final Object ecmaError) { 263 this.ecmaError = ecmaError; 264 } 265 266 private void ensureLineAndFileName() { 267 if (lineAndFileNameUnknown) { 268 for (final StackTraceElement ste : getStackTrace()) { 269 if (ECMAErrors.isScriptFrame(ste)) { 270 // Whatever here is compiled from JavaScript code 271 fileName = ste.getFileName(); 272 line = ste.getLineNumber(); 273 return; 274 } 275 } 276 277 lineAndFileNameUnknown = false; 278 } 279 } 280} 281