NativeError.java revision 1416:a750a66640e0
1214152Sed/*
2214152Sed * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3214152Sed * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4214152Sed *
5214152Sed * This code is free software; you can redistribute it and/or modify it
6214152Sed * under the terms of the GNU General Public License version 2 only, as
7214152Sed * published by the Free Software Foundation.  Oracle designates this
8214152Sed * particular file as subject to the "Classpath" exception as provided
9214152Sed * by Oracle in the LICENSE file that accompanied this code.
10214152Sed *
11214152Sed * This code is distributed in the hope that it will be useful, but WITHOUT
12214152Sed * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13214152Sed * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14214152Sed * version 2 for more details (a copy is included in the LICENSE file that
15214152Sed * accompanied this code).
16214152Sed *
17214152Sed * You should have received a copy of the GNU General Public License version
18214152Sed * 2 along with this work; if not, write to the Free Software Foundation,
19214152Sed * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20214152Sed *
21214152Sed * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22222656Sed * or visit www.oracle.com if you need additional information or have any
23222656Sed * questions.
24222656Sed */
25
26package jdk.nashorn.internal.objects;
27
28import static jdk.nashorn.internal.lookup.Lookup.MH;
29import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
30import java.lang.invoke.MethodHandle;
31import java.lang.invoke.MethodHandles;
32import jdk.nashorn.api.scripting.NashornException;
33import jdk.nashorn.internal.objects.annotations.Attribute;
34import jdk.nashorn.internal.objects.annotations.Constructor;
35import jdk.nashorn.internal.objects.annotations.Function;
36import jdk.nashorn.internal.objects.annotations.Property;
37import jdk.nashorn.internal.objects.annotations.ScriptClass;
38import jdk.nashorn.internal.objects.annotations.Where;
39import jdk.nashorn.internal.runtime.ECMAException;
40import jdk.nashorn.internal.runtime.JSType;
41import jdk.nashorn.internal.runtime.PropertyMap;
42import jdk.nashorn.internal.runtime.ScriptFunction;
43import jdk.nashorn.internal.runtime.ScriptObject;
44import jdk.nashorn.internal.runtime.ScriptRuntime;
45
46/**
47 * ECMA 15.11 Error Objects
48 */
49@ScriptClass("Error")
50public 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}
399