blob: 036c8d8e7931eabf6b4e8041e23e29378de11fbc [file] [log] [blame]
/*
* 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.
@SuppressWarnings("unused")
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);
}
}