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