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.objects; 27 28 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 29 import static jdk.nashorn.internal.lookup.Lookup.MH; 30 31 import java.lang.invoke.MethodHandle; 32 import java.lang.invoke.MethodHandles; 33 34 import jdk.nashorn.api.scripting.NashornException; 35 import jdk.nashorn.internal.objects.annotations.Attribute; 36 import jdk.nashorn.internal.objects.annotations.Constructor; 37 import jdk.nashorn.internal.objects.annotations.Function; 38 import jdk.nashorn.internal.objects.annotations.Property; 39 import jdk.nashorn.internal.objects.annotations.ScriptClass; 40 import jdk.nashorn.internal.objects.annotations.Where; 41 import jdk.nashorn.internal.runtime.ECMAException; 42 import jdk.nashorn.internal.runtime.JSType; 43 import jdk.nashorn.internal.runtime.PropertyMap; 44 import jdk.nashorn.internal.runtime.ScriptObject; 45 import jdk.nashorn.internal.runtime.ScriptFunction; 46 import jdk.nashorn.internal.runtime.ScriptRuntime; 47 48 /** 49 * ECMA 15.11 Error Objects 50 */ 51 @ScriptClass("Error") 52 public final class NativeError extends ScriptObject { 53 54 static final MethodHandle GET_COLUMNNUMBER = findOwnMH("getColumnNumber", Object.class, Object.class); 55 static final MethodHandle SET_COLUMNNUMBER = findOwnMH("setColumnNumber", Object.class, Object.class, Object.class); 56 static final MethodHandle GET_LINENUMBER = findOwnMH("getLineNumber", Object.class, Object.class); 57 static final MethodHandle SET_LINENUMBER = findOwnMH("setLineNumber", Object.class, Object.class, Object.class); 58 static final MethodHandle GET_FILENAME = findOwnMH("getFileName", Object.class, Object.class); 59 static final MethodHandle SET_FILENAME = findOwnMH("setFileName", Object.class, Object.class, Object.class); 60 static final MethodHandle GET_STACK = findOwnMH("getStack", Object.class, Object.class); 61 static final MethodHandle SET_STACK = findOwnMH("setStack", Object.class, Object.class, Object.class); 62 63 // message property name 64 static final String MESSAGE = "message"; 65 // name property name 66 static final String NAME = "name"; 67 // stack property name 68 static final String STACK = "__stack__"; 69 // lineNumber property name 70 static final String LINENUMBER = "__lineNumber__"; 71 // columnNumber property name 72 static final String COLUMNNUMBER = "__columnNumber__"; 73 // fileName property name 74 static final String FILENAME = "__fileName__"; 75 76 /** Message property name */ 77 @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) 78 public Object instMessage; 79 80 /** ECMA 15.11.4.2 Error.prototype.name */ 81 @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) 82 public Object name; 83 84 /** ECMA 15.11.4.3 Error.prototype.message */ 85 @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) 86 public Object message; 87 88 /** Nashorn extension: underlying exception */ 89 @Property(attributes = Attribute.NOT_ENUMERABLE) 90 public Object nashornException; 91 92 // initialized by nasgen 93 private static PropertyMap $nasgenmap$; 94 95 @SuppressWarnings("LeakingThisInConstructor") 96 private NativeError(final Object msg, final ScriptObject proto, final PropertyMap map) { 97 super(proto, map); 98 if (msg != UNDEFINED) { 99 this.instMessage = JSType.toString(msg); 100 } else { 101 this.delete(NativeError.MESSAGE, false); 102 } 103 initException(this); 104 } 105 106 NativeError(final Object msg, final Global global) { 107 this(msg, global.getErrorPrototype(), $nasgenmap$); 108 } 109 110 private NativeError(final Object msg) { 111 this(msg, Global.instance()); 112 } 113 114 @Override 115 public String getClassName() { 116 return "Error"; 117 } 118 119 /** 120 * ECMA 15.11.2 The Error Constructor 121 * 122 * @param newObj true if this is being instantiated with a new 123 * @param self self reference 124 * @param msg error message 125 * 126 * @return NativeError instance 127 */ 128 @Constructor 129 public static Object constructor(final boolean newObj, final Object self, final Object msg) { 130 return new NativeError(msg); 131 } 132 133 // This is called NativeError, NativeTypeError etc. to 134 // associate a ECMAException with the ECMA Error object. 135 @SuppressWarnings("unused") 136 static void initException(final ScriptObject self) { 137 // ECMAException constructor has side effects 138 new ECMAException(self, null); 139 } 140 141 /** 142 * Nashorn extension: Error.captureStackTrace. Capture stack trace at the point of call into the Error object provided. 143 * 144 * @param self self reference 145 * @param errorObj the error object 146 * @return undefined 147 */ 148 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 149 public static Object captureStackTrace(final Object self, final Object errorObj) { 150 Global.checkObject(errorObj); 151 final ScriptObject sobj = (ScriptObject)errorObj; 152 initException(sobj); 153 sobj.delete(STACK, false); 154 if (! sobj.has("stack")) { 155 final ScriptFunction getStack = ScriptFunctionImpl.makeFunction("getStack", GET_STACK); 156 final ScriptFunction setStack = ScriptFunctionImpl.makeFunction("setStack", SET_STACK); 157 sobj.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); 158 } 159 return UNDEFINED; 160 } 161 162 /** 163 * Nashorn extension: Error.dumpStack 164 * dumps the stack of the current thread. 165 * 166 * @param self self reference 167 * 168 * @return undefined 169 */ 170 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 171 public static Object dumpStack(final Object self) { 172 Thread.dumpStack(); 173 return UNDEFINED; 174 } 175 176 /** 177 * Nashorn extension: Error.prototype.printStackTrace 178 * prints stack trace associated with the exception (if available). 179 * to the standard error stream. 180 * 181 * @param self self reference 182 * 183 * @return result of {@link ECMAException#printStackTrace(ScriptObject)}, which is typically undefined 184 */ 185 @Function(attributes = Attribute.NOT_ENUMERABLE) 186 public static Object printStackTrace(final Object self) { 187 Global.checkObject(self); 188 return ECMAException.printStackTrace((ScriptObject)self); 189 } 190 191 /** 192 * Nashorn extension: Error.prototype.getStackTrace() 193 * "stack" property is an array typed value containing {@link StackTraceElement} 194 * objects of JavaScript stack frames. 195 * 196 * @param self self reference 197 * 198 * @return stack trace as a script array. 199 */ 200 @Function(attributes = Attribute.NOT_ENUMERABLE) 201 public static Object getStackTrace(final Object self) { 202 Global.checkObject(self); 203 final ScriptObject sobj = (ScriptObject)self; 204 final Object exception = ECMAException.getException(sobj); 205 Object[] res; 206 if (exception instanceof Throwable) { 207 res = NashornException.getScriptFrames((Throwable)exception); 208 } else { 209 res = ScriptRuntime.EMPTY_ARRAY; 210 } 211 212 return new NativeArray(res); 213 } 214 215 /** 216 * Nashorn extension: Error.prototype.lineNumber 217 * 218 * @param self self reference 219 * 220 * @return line number from which error was thrown 221 */ 222 public static Object getLineNumber(final Object self) { 223 Global.checkObject(self); 224 final ScriptObject sobj = (ScriptObject)self; 225 return sobj.has(LINENUMBER) ? sobj.get(LINENUMBER) : ECMAException.getLineNumber(sobj); 226 } 227 228 /** 229 * Nashorn extension: Error.prototype.lineNumber 230 * 231 * @param self self reference 232 * @param value value of line number 233 * 234 * @return value that was set 235 */ 236 public static Object setLineNumber(final Object self, final Object value) { 237 Global.checkObject(self); 238 final ScriptObject sobj = (ScriptObject)self; 239 if (sobj.hasOwnProperty(LINENUMBER)) { 240 sobj.put(LINENUMBER, value, false); 241 } else { 242 sobj.addOwnProperty(LINENUMBER, Attribute.NOT_ENUMERABLE, value); 243 } 244 return value; 245 } 246 247 /** 248 * Nashorn extension: Error.prototype.columnNumber 249 * 250 * @param self self reference 251 * 252 * @return column number from which error was thrown 253 */ 254 public static Object getColumnNumber(final Object self) { 255 Global.checkObject(self); 256 final ScriptObject sobj = (ScriptObject)self; 257 return sobj.has(COLUMNNUMBER) ? sobj.get(COLUMNNUMBER) : ECMAException.getColumnNumber((ScriptObject)self); 258 } 259 260 /** 261 * Nashorn extension: Error.prototype.columnNumber 262 * 263 * @param self self reference 264 * @param value value of column number 265 * 266 * @return value that was set 267 */ 268 public static Object setColumnNumber(final Object self, final Object value) { 269 Global.checkObject(self); 270 final ScriptObject sobj = (ScriptObject)self; 271 if (sobj.hasOwnProperty(COLUMNNUMBER)) { 272 sobj.put(COLUMNNUMBER, value, false); 273 } else { 274 sobj.addOwnProperty(COLUMNNUMBER, Attribute.NOT_ENUMERABLE, value); 275 } 276 return value; 277 } 278 279 /** 280 * Nashorn extension: Error.prototype.fileName 281 * 282 * @param self self reference 283 * 284 * @return file name from which error was thrown 285 */ 286 public static Object getFileName(final Object self) { 287 Global.checkObject(self); 288 final ScriptObject sobj = (ScriptObject)self; 289 return sobj.has(FILENAME) ? sobj.get(FILENAME) : ECMAException.getFileName((ScriptObject)self); 290 } 291 292 /** 293 * Nashorn extension: Error.prototype.fileName 294 * 295 * @param self self reference 296 * @param value value of file name 297 * 298 * @return value that was set 299 */ 300 public static Object setFileName(final Object self, final Object value) { 301 Global.checkObject(self); 302 final ScriptObject sobj = (ScriptObject)self; 303 if (sobj.hasOwnProperty(FILENAME)) { 304 sobj.put(FILENAME, value, false); 305 } else { 306 sobj.addOwnProperty(FILENAME, Attribute.NOT_ENUMERABLE, value); 307 } 308 return value; 309 } 310 311 /** 312 * Nashorn extension: Error.prototype.stack 313 * "stack" property is a string typed value containing JavaScript stack frames. 314 * Each frame information is separated bv "\n" character. 315 * 316 * @param self self reference 317 * 318 * @return value of "stack" property 319 */ 320 public static Object getStack(final Object self) { 321 Global.checkObject(self); 322 final ScriptObject sobj = (ScriptObject)self; 323 if (sobj.has(STACK)) { 324 return sobj.get(STACK); 325 } 326 327 final Object exception = ECMAException.getException(sobj); 328 if (exception instanceof Throwable) { 329 Object value = getScriptStackString(sobj, (Throwable)exception); 330 sobj.put(STACK, value, false); 331 return value; 332 } 333 334 return UNDEFINED; 335 } 336 337 /** 338 * Nashorn extension 339 * Accessed from {@link Global} while setting up the Error.prototype 340 * 341 * @param self self reference 342 * @param value value to set "stack" property to, must be {@code ScriptObject} 343 * 344 * @return value that was set 345 */ 346 public static Object setStack(final Object self, final Object value) { 347 Global.checkObject(self); 348 final ScriptObject sobj = (ScriptObject)self; 349 sobj.set(STACK, value, false); 350 return value; 351 } 352 353 /** 354 * ECMA 15.11.4.4 Error.prototype.toString ( ) 355 * 356 * @param self self reference 357 * 358 * @return this NativeError as a string 359 */ 360 @Function(attributes = Attribute.NOT_ENUMERABLE) 361 public static Object toString(final Object self) { 362 // Step 1 and 2 : check if 'self' is object it not throw TypeError 363 Global.checkObject(self); 364 365 final ScriptObject sobj = (ScriptObject)self; 366 367 // Step 3 & 4 : get "name" and convert to String. 368 // But if message is undefined make it "Error". 369 Object name = sobj.get("name"); 370 if (name == UNDEFINED) { 371 name = "Error"; 372 } else { 373 name = JSType.toString(name); 374 } 375 376 // Steps 5, 6, & 7 : get "message" and convert to String. 377 // if 'message' is undefined make it "" (empty String). 378 Object msg = sobj.get("message"); 379 if (msg == UNDEFINED) { 380 msg = ""; 381 } else { 382 msg = JSType.toString(msg); 383 } 384 385 // Step 8 : if name is empty, return msg 386 if (((String)name).isEmpty()) { 387 return msg; 388 } 389 390 // Step 9 : if message is empty, return name 391 if (((String)msg).isEmpty()) { 392 return name; 393 } 394 // Step 10 : return name + ": " + msg 395 return name + ": " + msg; 396 } 397 398 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 399 return MH.findStatic(MethodHandles.lookup(), NativeError.class, name, MH.type(rtype, types)); 400 } 401 402 private static String getScriptStackString(final ScriptObject sobj, final Throwable exp) { 403 return JSType.toString(sobj) + "\n" + NashornException.getScriptStackString(exp); 404 } 405 }