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