Ecmascript Error objects have very few standard properties, so many Ecmascript implementations have added quite a few custom properties. Duktape uses standard Error properties but also borrows the most useful properties used by other implementations. The number of "own" properties of error objects is minimized to keep error objects as small as possible.
Error objects have the following properties (mostly inherited):
Property name | Compatibility | Description |
---|---|---|
name | standard | Name of error, e.g. TypeError , inherited |
message | standard | Optional message of error, own property, empty message inherited if absent |
fileName | Rhino | Filename related to error source, inherited accessor |
lineNumber | Rhino | Linenumber related to error source, inherited accessor |
stack | V8 | Traceback as a multi-line human redable string, inherited accessor |
If Duktape is compiled with traceback support:
stack
, fileName
, and lineNumber
are accessor
properties inherited from Error.prototype
.\xFFtracedata
) which is not normally accessible from
Ecmascript code.If Duktape is compiled without traceback support:
stack
accessor will be equivalent to
Error.prototype.toString()
, so that printing the stacktrace
always produces a useful result.fileName
and lineNumber
will be own properties of the
Error object.When error objects are created using the Duktape API from C code and the
caller does not give a format string for a message
, the message
property is set to a numeric error code given in the API call. The type of
message
will be number in this case; normally error messages are
strings. In minimized Duktape builds all errors generated internally by
Duktape use numeric error codes only.
An object is considered an "error object" if its internal prototype
chain contains the (original) Error.prototype
object. Only
objects matching this criteria get augmented with e.g. traceback data.
The stack
property is an accessor (setter/getter) property
which provides a printable traceback related to an error. The traceback
reflects the call stack when the error object was created (not thrown).
Traceback data is automatically collected and added to an object:
The data used to create the traceback is stored in an internal property
(\xFFtracedata
), in an internal and version-dependent format
described
error-objects.rst.
You shouldn't access the traceback data directly.
The printable traceback format is intended to be human readable only. You shouldn't rely on an exact traceback format as it may change between versions. As an example of the current traceback format, the program:
// shortened from ecmascript-testcases/test-dev-traceback-example.js try { decodeURIComponent('%e1%a9%01'); // invalid utf-8 } catch (e) { print(e.stack); }
would print something like:
URIError: invalid input duk_bi_global.c:316 decodeURIComponent native strict preventsyield global ecmascript-testcases/test-dev-traceback-example.js:3 preventsyield
In builds where tracebacks are disabled, the stack
accessor
will return the same value as calling toString()
on the error
would. This means you can always print e.stack
and get a useful
output.
The most portable traceback printing approach is something like:
try { decodeURIComponent('%e1%a9%01'); // invalid utf-8 } catch (e) { // Print stacktrace on at least Duktape and V8, or a standard error // string otherwise. print(e.stack || e); }
Attempt to write to stack
is silently ignored. You can still
override the accessor by defining an own property of the same name explicitly
with Object.defineProperty()
. This behavior differs from V8 where
stack
is an own property of the Error instance, and if you assign a
value to stack
, the value reads back as assigned.
If Duktape.errCreate
has been set, it is called right after
Duktape has added traceback information to an object, and can process the
error further or even replace the error value entirely. The error handler
only gets called with Error
instances, and its return value is
used as the final error value. If the error handler throws an error, that
error replaces the original error. The error handler is usually called only once
per error. However, in corner cases related to constructors, the error handler
can be called multiple times for a single error value.
An error handler should avoid overwriting any properties already
present in an object, as that would be quite confusing for other code.
In general, an error handler should always avoid throwing an error, as that
error replaces the original error and would also be confusing. As a specific
example, an error handler must not try to add a new property to a non-extensible
object, as that would cause a TypeError
.
Below is an example error handler for adding a creation timestamp to errors at their creation:
Duktape.errCreate = function (e) { if (!(e instanceof Error)) { // this check is not really needed because errCreate only gets // called with Error instances return e; } if ('created' in e) { // already augmented or conflicting property present return e; } if (!Object.isExtensible(e)) { // object not extensible, don't try to add a new property return e; } e.created = new Date(); return e; }
To remove the handler, delete the property (setting it to e.g. null
does not work and causes a TypeError
when Duktape attempts to
call the null
value):
// Remove error handler for error creation delete Duktape.errCreate;
Similarly, if Duktape.errThrow
has been set, it is called
right before an error is thrown, and can process or replace the error value.
Because Ecmascript allows any value type to be thrown, the error handler
may get called with arbitrary input values (not just Error
instances). It may also be called more than once for the same value because
an error can be re-thrown multiple times.
For example, to add a throw timestamp (recording the first time the object has been thrown) to errors:
Duktape.errCreate = function (e) { if (!(e instanceof Error)) { // refuse to touch anything but Error instances return e; } if ('thrown' in e) { // already augmented or conflicting property present return e; } if (!Object.isExtensible(e)) { // object not extensible, don't try to add a new property return e; } e.thrown = new Date(); return e; }
Again, to remove the handler, delete the property:
// Remove error handler for error throwing delete Duktape.errThrow;
MyError('msg')
instead of new MyError('msg')
)
it won't get augmented with custom fields such as traceback data. When
called as a constructor custom errors inheriting from Error
get
augmented normally. Built-in standard errors (like TypeError
)
always get augmented, even when created with a non-constructor function call
(the tracebacks look slightly different depending on how the error is
created, though).