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