| /* |
| * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.nashorn.internal.objects; |
| |
| import static jdk.nashorn.internal.lookup.Lookup.MH; |
| import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import jdk.nashorn.api.scripting.NashornException; |
| import jdk.nashorn.internal.objects.annotations.Attribute; |
| import jdk.nashorn.internal.objects.annotations.Constructor; |
| import jdk.nashorn.internal.objects.annotations.Function; |
| import jdk.nashorn.internal.objects.annotations.Property; |
| import jdk.nashorn.internal.objects.annotations.ScriptClass; |
| import jdk.nashorn.internal.objects.annotations.Where; |
| import jdk.nashorn.internal.runtime.ECMAException; |
| import jdk.nashorn.internal.runtime.JSType; |
| import jdk.nashorn.internal.runtime.PropertyMap; |
| import jdk.nashorn.internal.runtime.ScriptFunction; |
| import jdk.nashorn.internal.runtime.ScriptObject; |
| import jdk.nashorn.internal.runtime.ScriptRuntime; |
| |
| /** |
| * ECMA 15.11 Error Objects |
| */ |
| @ScriptClass("Error") |
| public final class NativeError extends ScriptObject { |
| |
| static final MethodHandle GET_COLUMNNUMBER = findOwnMH("getColumnNumber", Object.class, Object.class); |
| static final MethodHandle SET_COLUMNNUMBER = findOwnMH("setColumnNumber", Object.class, Object.class, Object.class); |
| static final MethodHandle GET_LINENUMBER = findOwnMH("getLineNumber", Object.class, Object.class); |
| static final MethodHandle SET_LINENUMBER = findOwnMH("setLineNumber", Object.class, Object.class, Object.class); |
| static final MethodHandle GET_FILENAME = findOwnMH("getFileName", Object.class, Object.class); |
| static final MethodHandle SET_FILENAME = findOwnMH("setFileName", Object.class, Object.class, Object.class); |
| static final MethodHandle GET_STACK = findOwnMH("getStack", Object.class, Object.class); |
| static final MethodHandle SET_STACK = findOwnMH("setStack", Object.class, Object.class, Object.class); |
| |
| // message property name |
| static final String MESSAGE = "message"; |
| // name property name |
| static final String NAME = "name"; |
| // stack property name |
| static final String STACK = "__stack__"; |
| // lineNumber property name |
| static final String LINENUMBER = "__lineNumber__"; |
| // columnNumber property name |
| static final String COLUMNNUMBER = "__columnNumber__"; |
| // fileName property name |
| static final String FILENAME = "__fileName__"; |
| |
| /** Message property name */ |
| @Property(name = NativeError.MESSAGE, attributes = Attribute.NOT_ENUMERABLE) |
| public Object instMessage; |
| |
| /** ECMA 15.11.4.2 Error.prototype.name */ |
| @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) |
| public Object name; |
| |
| /** ECMA 15.11.4.3 Error.prototype.message */ |
| @Property(attributes = Attribute.NOT_ENUMERABLE, where = Where.PROTOTYPE) |
| public Object message; |
| |
| /** Nashorn extension: underlying exception */ |
| @Property(attributes = Attribute.NOT_ENUMERABLE) |
| public Object nashornException; |
| |
| // initialized by nasgen |
| private static PropertyMap $nasgenmap$; |
| |
| @SuppressWarnings("LeakingThisInConstructor") |
| private NativeError(final Object msg, final ScriptObject proto, final PropertyMap map) { |
| super(proto, map); |
| if (msg != UNDEFINED) { |
| this.instMessage = JSType.toString(msg); |
| } else { |
| this.delete(NativeError.MESSAGE, false); |
| } |
| initException(this); |
| } |
| |
| NativeError(final Object msg, final Global global) { |
| this(msg, global.getErrorPrototype(), $nasgenmap$); |
| } |
| |
| private NativeError(final Object msg) { |
| this(msg, Global.instance()); |
| } |
| |
| @Override |
| public String getClassName() { |
| return "Error"; |
| } |
| |
| /** |
| * ECMA 15.11.2 The Error Constructor |
| * |
| * @param newObj true if this is being instantiated with a new |
| * @param self self reference |
| * @param msg error message |
| * |
| * @return NativeError instance |
| */ |
| @Constructor |
| public static NativeError constructor(final boolean newObj, final Object self, final Object msg) { |
| return new NativeError(msg); |
| } |
| |
| // This is called NativeError, NativeTypeError etc. to |
| // associate a ECMAException with the ECMA Error object. |
| static void initException(final ScriptObject self) { |
| // ECMAException constructor has side effects |
| new ECMAException(self, null); |
| } |
| |
| /** |
| * Nashorn extension: Error.captureStackTrace. Capture stack trace at the point of call into the Error object provided. |
| * |
| * @param self self reference |
| * @param errorObj the error object |
| * @return undefined |
| */ |
| @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) |
| public static Object captureStackTrace(final Object self, final Object errorObj) { |
| final ScriptObject sobj = Global.checkObject(errorObj); |
| initException(sobj); |
| sobj.delete(STACK, false); |
| if (! sobj.has("stack")) { |
| final ScriptFunction getStack = ScriptFunction.createBuiltin("getStack", GET_STACK); |
| final ScriptFunction setStack = ScriptFunction.createBuiltin("setStack", SET_STACK); |
| sobj.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack); |
| } |
| return UNDEFINED; |
| } |
| |
| /** |
| * Nashorn extension: Error.dumpStack |
| * dumps the stack of the current thread. |
| * |
| * @param self self reference |
| * |
| * @return undefined |
| */ |
| @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) |
| public static Object dumpStack(final Object self) { |
| Thread.dumpStack(); |
| return UNDEFINED; |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.printStackTrace |
| * prints stack trace associated with the exception (if available). |
| * to the standard error stream. |
| * |
| * @param self self reference |
| * |
| * @return result of {@link ECMAException#printStackTrace(ScriptObject)}, which is typically undefined |
| */ |
| @Function(attributes = Attribute.NOT_ENUMERABLE) |
| public static Object printStackTrace(final Object self) { |
| return ECMAException.printStackTrace(Global.checkObject(self)); |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.getStackTrace() |
| * "stack" property is an array typed value containing {@link StackTraceElement} |
| * objects of JavaScript stack frames. |
| * |
| * @param self self reference |
| * |
| * @return stack trace as a script array. |
| */ |
| @Function(attributes = Attribute.NOT_ENUMERABLE) |
| public static Object getStackTrace(final Object self) { |
| final ScriptObject sobj = Global.checkObject(self); |
| final Object exception = ECMAException.getException(sobj); |
| Object[] res; |
| if (exception instanceof Throwable) { |
| res = NashornException.getScriptFrames((Throwable)exception); |
| } else { |
| res = ScriptRuntime.EMPTY_ARRAY; |
| } |
| |
| return new NativeArray(res); |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.lineNumber |
| * |
| * @param self self reference |
| * |
| * @return line number from which error was thrown |
| */ |
| public static Object getLineNumber(final Object self) { |
| final ScriptObject sobj = Global.checkObject(self); |
| return sobj.has(LINENUMBER) ? sobj.get(LINENUMBER) : ECMAException.getLineNumber(sobj); |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.lineNumber |
| * |
| * @param self self reference |
| * @param value value of line number |
| * |
| * @return value that was set |
| */ |
| public static Object setLineNumber(final Object self, final Object value) { |
| final ScriptObject sobj = Global.checkObject(self); |
| if (sobj.hasOwnProperty(LINENUMBER)) { |
| sobj.put(LINENUMBER, value, false); |
| } else { |
| sobj.addOwnProperty(LINENUMBER, Attribute.NOT_ENUMERABLE, value); |
| } |
| return value; |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.columnNumber |
| * |
| * @param self self reference |
| * |
| * @return column number from which error was thrown |
| */ |
| public static Object getColumnNumber(final Object self) { |
| final ScriptObject sobj = Global.checkObject(self); |
| return sobj.has(COLUMNNUMBER) ? sobj.get(COLUMNNUMBER) : ECMAException.getColumnNumber((ScriptObject)self); |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.columnNumber |
| * |
| * @param self self reference |
| * @param value value of column number |
| * |
| * @return value that was set |
| */ |
| public static Object setColumnNumber(final Object self, final Object value) { |
| final ScriptObject sobj = Global.checkObject(self); |
| if (sobj.hasOwnProperty(COLUMNNUMBER)) { |
| sobj.put(COLUMNNUMBER, value, false); |
| } else { |
| sobj.addOwnProperty(COLUMNNUMBER, Attribute.NOT_ENUMERABLE, value); |
| } |
| return value; |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.fileName |
| * |
| * @param self self reference |
| * |
| * @return file name from which error was thrown |
| */ |
| public static Object getFileName(final Object self) { |
| final ScriptObject sobj = Global.checkObject(self); |
| return sobj.has(FILENAME) ? sobj.get(FILENAME) : ECMAException.getFileName((ScriptObject)self); |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.fileName |
| * |
| * @param self self reference |
| * @param value value of file name |
| * |
| * @return value that was set |
| */ |
| public static Object setFileName(final Object self, final Object value) { |
| final ScriptObject sobj = Global.checkObject(self); |
| if (sobj.hasOwnProperty(FILENAME)) { |
| sobj.put(FILENAME, value, false); |
| } else { |
| sobj.addOwnProperty(FILENAME, Attribute.NOT_ENUMERABLE, value); |
| } |
| return value; |
| } |
| |
| /** |
| * Nashorn extension: Error.prototype.stack |
| * "stack" property is a string typed value containing JavaScript stack frames. |
| * Each frame information is separated bv "\n" character. |
| * |
| * @param self self reference |
| * |
| * @return value of "stack" property |
| */ |
| public static Object getStack(final Object self) { |
| final ScriptObject sobj = Global.checkObject(self); |
| if (sobj.has(STACK)) { |
| return sobj.get(STACK); |
| } |
| |
| final Object exception = ECMAException.getException(sobj); |
| if (exception instanceof Throwable) { |
| final Object value = getScriptStackString(sobj, (Throwable)exception); |
| if (sobj.hasOwnProperty(STACK)) { |
| sobj.put(STACK, value, false); |
| } else { |
| sobj.addOwnProperty(STACK, Attribute.NOT_ENUMERABLE, value); |
| } |
| |
| return value; |
| } |
| |
| return UNDEFINED; |
| } |
| |
| /** |
| * Nashorn extension |
| * Accessed from {@link Global} while setting up the Error.prototype |
| * |
| * @param self self reference |
| * @param value value to set "stack" property to, must be {@code ScriptObject} |
| * |
| * @return value that was set |
| */ |
| public static Object setStack(final Object self, final Object value) { |
| final ScriptObject sobj = Global.checkObject(self); |
| if (sobj.hasOwnProperty(STACK)) { |
| sobj.put(STACK, value, false); |
| } else { |
| sobj.addOwnProperty(STACK, Attribute.NOT_ENUMERABLE, value); |
| } |
| return value; |
| } |
| |
| /** |
| * ECMA 15.11.4.4 Error.prototype.toString ( ) |
| * |
| * @param self self reference |
| * |
| * @return this NativeError as a string |
| */ |
| @Function(attributes = Attribute.NOT_ENUMERABLE) |
| public static Object toString(final Object self) { |
| // Step 1 and 2 : check if 'self' is object it not throw TypeError |
| final ScriptObject sobj = Global.checkObject(self); |
| |
| // Step 3 & 4 : get "name" and convert to String. |
| // But if message is undefined make it "Error". |
| Object name = sobj.get("name"); |
| if (name == UNDEFINED) { |
| name = "Error"; |
| } else { |
| name = JSType.toString(name); |
| } |
| |
| // Steps 5, 6, & 7 : get "message" and convert to String. |
| // if 'message' is undefined make it "" (empty String). |
| Object msg = sobj.get("message"); |
| if (msg == UNDEFINED) { |
| msg = ""; |
| } else { |
| msg = JSType.toString(msg); |
| } |
| |
| // Step 8 : if name is empty, return msg |
| if (((String)name).isEmpty()) { |
| return msg; |
| } |
| |
| // Step 9 : if message is empty, return name |
| if (((String)msg).isEmpty()) { |
| return name; |
| } |
| // Step 10 : return name + ": " + msg |
| return name + ": " + msg; |
| } |
| |
| private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { |
| return MH.findStatic(MethodHandles.lookup(), NativeError.class, name, MH.type(rtype, types)); |
| } |
| |
| private static String getScriptStackString(final ScriptObject sobj, final Throwable exp) { |
| return JSType.toString(sobj) + "\n" + NashornException.getScriptStackString(exp); |
| } |
| } |