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.lineNumber 148 * 149 * @param self self reference 150 * 151 * @return line number from which error was thrown 152 */ 153 public static Object getLineNumber(final Object self) { 154 Global.checkObject(self); 155 final ScriptObject sobj = (ScriptObject)self; 156 return sobj.has(LINENUMBER) ? sobj.get(LINENUMBER) : ECMAException.getLineNumber(sobj); 157 } 158 159 /** 160 * Nashorn extension: Error.prototype.lineNumber 161 * 162 * @param self self reference 163 * @param value value of line number 164 * 165 * @return value that was set 166 */ 167 public static Object setLineNumber(final Object self, final Object value) { 168 Global.checkObject(self); 169 final ScriptObject sobj = (ScriptObject)self; 170 sobj.set(LINENUMBER, value, false); 171 return value; 172 } 173 174 /** 175 * Nashorn extension: Error.prototype.columnNumber 176 * 177 * @param self self reference 178 * 179 * @return column number from which error was thrown 180 */ 181 public static Object getColumnNumber(final Object self) { 182 Global.checkObject(self); 183 final ScriptObject sobj = (ScriptObject)self; 184 return sobj.has(COLUMNNUMBER) ? sobj.get(COLUMNNUMBER) : ECMAException.getColumnNumber((ScriptObject)self); 185 } 186 187 /** 188 * Nashorn extension: Error.prototype.columnNumber 189 * 190 * @param self self reference 191 * @param value value of column number 192 * 193 * @return value that was set 194 */ 195 public static Object setColumnNumber(final Object self, final Object value) { 196 Global.checkObject(self); 197 final ScriptObject sobj = (ScriptObject)self; 198 sobj.set(COLUMNNUMBER, value, false); 199 return value; 200 } 201 202 /** 203 * Nashorn extension: Error.prototype.fileName 204 * 205 * @param self self reference 206 * 207 * @return file name from which error was thrown 208 */ 209 public static Object getFileName(final Object self) { 210 Global.checkObject(self); 211 final ScriptObject sobj = (ScriptObject)self; 212 return sobj.has(FILENAME) ? sobj.get(FILENAME) : ECMAException.getFileName((ScriptObject)self); 213 } 214 215 /** 216 * Nashorn extension: Error.prototype.fileName 217 * 218 * @param self self reference 219 * @param value value of file name 220 * 221 * @return value that was set 222 */ 223 public static Object setFileName(final Object self, final Object value) { 224 Global.checkObject(self); 225 final ScriptObject sobj = (ScriptObject)self; 226 sobj.set(FILENAME, value, false); 227 return value; 228 } 229 230 /** 231 * Nashorn extension: Error.prototype.stack 232 * "stack" property is an array typed value containing {@link StackTraceElement} 233 * objects of JavaScript stack frames. 234 * 235 * @param self self reference 236 * 237 * @return value of "stack" property 238 */ 239 public static Object getStack(final Object self) { 240 Global.checkObject(self); 241 final ScriptObject sobj = (ScriptObject)self; 242 if (sobj.has(STACK)) { 243 return sobj.get(STACK); 244 } 245 246 final Object exception = ECMAException.getException(sobj); 247 Object[] res; 248 if (exception instanceof Throwable) { 249 final StackTraceElement[] frames = ((Throwable)exception).getStackTrace(); 250 final List<StackTraceElement> filtered = new ArrayList<>(); 251 for (final StackTraceElement st : frames) { 252 if (ECMAErrors.isScriptFrame(st)) { 253 final String className = "<" + st.getFileName() + ">"; 254 String methodName = st.getMethodName(); 255 if (methodName.equals(CompilerConstants.RUN_SCRIPT.symbolName())) { 256 methodName = "<program>"; 257 } 258 filtered.add(new StackTraceElement(className, methodName, 259 st.getFileName(), st.getLineNumber())); 260 } 261 } 262 res = filtered.toArray(); 263 } else { 264 res = ScriptRuntime.EMPTY_ARRAY; 265 } 266 267 return new NativeArray(res); 268 } 269 270 /** 271 * Nashorn extension 272 * Accessed from {@link Global} while setting up the Error.prototype 273 * 274 * @param self self reference 275 * @param value value to set "stack" property to, must be {@code ScriptObject} 276 * 277 * @return value that was set 278 */ 279 public static Object setStack(final Object self, final Object value) { 280 Global.checkObject(self); 281 final ScriptObject sobj = (ScriptObject)self; 282 sobj.set(STACK, value, false); 283 return value; 284 } 285 286 /** 287 * ECMA 15.11.4.4 Error.prototype.toString ( ) 288 * 289 * @param self self reference 290 * 291 * @return this NativeError as a string 292 */ 293 @Function(attributes = Attribute.NOT_ENUMERABLE) 294 public static Object toString(final Object self) { 295 // Step 1 and 2 : check if 'self' is object it not throw TypeError 296 Global.checkObject(self); 297 298 final ScriptObject sobj = (ScriptObject)self; 299 300 // Step 3 & 4 : get "name" and convert to String. 301 // But if message is undefined make it "Error". 302 Object name = sobj.get("name"); 303 if (name == UNDEFINED) { 304 name = "Error"; 305 } else { 306 name = JSType.toString(name); 307 } 308 309 // Steps 5, 6, & 7 : get "message" and convert to String. 310 // if 'message' is undefined make it "" (empty String). 311 Object msg = sobj.get("message"); 312 if (msg == UNDEFINED) { 313 msg = ""; 314 } else { 315 msg = JSType.toString(msg); 316 } 317 318 // Step 8 : if name is empty, return msg 319 if (((String)name).isEmpty()) { 320 return msg; 321 } 322 323 // Step 9 : if message is empty, return name 324 if (((String)msg).isEmpty()) { 325 return name; 326 } 327 // Step 10 : return name + ": " + msg 328 return name + ": " + msg; 329 } 330 331 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 332 try { 333 return MethodHandles.lookup().findStatic(NativeError.class, name, MH.type(rtype, types)); 334 } catch (final NoSuchMethodException | IllegalAccessException e) { 335 throw new MethodHandleFactory.LookupException(e); 336 } 337 } 338 }