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