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