5 #include "duk_internal.h"
7 DUK_INTERNAL duk_ret_t
duk_bi_error_constructor_shared(duk_context
*ctx
) {
8 /* Behavior for constructor and non-constructor call is
9 * the same except for augmenting the created error. When
10 * called as a constructor, the caller (duk_new()) will handle
11 * augmentation; when called as normal function, we need to do
15 duk_hthread
*thr
= (duk_hthread
*) ctx
;
16 duk_small_int_t bidx_prototype
= duk_get_current_magic(ctx
);
18 /* same for both error and each subclass like TypeError */
19 duk_uint_t flags_and_class
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
20 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR
);
24 duk_push_object_helper(ctx
, flags_and_class
, bidx_prototype
);
26 /* If message is undefined, the own property 'message' is not set at
27 * all to save property space. An empty message is inherited anyway.
29 if (!duk_is_undefined(ctx
, 0)) {
30 duk_to_string(ctx
, 0);
31 duk_dup(ctx
, 0); /* [ message error message ] */
32 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_MESSAGE
, DUK_PROPDESC_FLAGS_WC
);
35 /* Augment the error if called as a normal function. __FILE__ and __LINE__
36 * are not desirable in this case.
39 #ifdef DUK_USE_AUGMENT_ERROR_CREATE
40 if (!duk_is_constructor_call(ctx
)) {
41 duk_err_augment_error_create(thr
, thr
, NULL
, 0, 1 /*noblame_fileline*/);
48 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_to_string(duk_context
*ctx
) {
49 /* XXX: optimize with more direct internal access */
52 (void) duk_require_hobject_or_lfunc_coerce(ctx
, -1);
56 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_NAME
);
57 if (duk_is_undefined(ctx
, -1)) {
59 duk_push_string(ctx
, "Error");
61 duk_to_string(ctx
, -1);
64 /* [ ... this name ] */
66 /* XXX: Are steps 6 and 7 in E5 Section 15.11.4.4 duplicated by
67 * accident or are they actually needed? The first ToString()
68 * could conceivably return 'undefined'.
70 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_MESSAGE
);
71 if (duk_is_undefined(ctx
, -1)) {
73 duk_push_string(ctx
, "");
75 duk_to_string(ctx
, -1);
78 /* [ ... this name message ] */
80 if (duk_get_length(ctx
, -2) == 0) {
81 /* name is empty -> return message */
84 if (duk_get_length(ctx
, -1) == 0) {
85 /* message is empty -> return name */
89 duk_push_string(ctx
, ": ");
90 duk_insert(ctx
, -2); /* ... name ': ' message */
96 #if defined(DUK_USE_TRACEBACKS)
101 * The unified helper decodes the traceback and produces various requested
102 * outputs. It should be optimized for size, and may leave garbage on stack,
103 * only the topmost return value matters. For instance, traceback separator
104 * and decoded strings are pushed even when looking for filename only.
106 * NOTE: although _Tracedata is an internal property, user code can currently
107 * write to the array (or replace it with something other than an array).
108 * The code below must tolerate arbitrary _Tracedata. It can throw errors
109 * etc, but cannot cause a segfault or memory unsafe behavior.
112 /* constants arbitrary, chosen for small loads */
113 #define DUK__OUTPUT_TYPE_TRACEBACK (-1)
114 #define DUK__OUTPUT_TYPE_FILENAME 0
115 #define DUK__OUTPUT_TYPE_LINENUMBER 1
117 DUK_LOCAL duk_ret_t
duk__error_getter_helper(duk_context
*ctx
, duk_small_int_t output_type
) {
118 duk_hthread
*thr
= (duk_hthread
*) ctx
;
120 duk_small_int_t i
; /* traceback depth fits into 16 bits */
121 duk_small_int_t t
; /* stack type fits into 16 bits */
122 duk_small_int_t count_func
= 0; /* traceback depth ensures fits into 16 bits */
123 const char *str_tailcall
= " tailcall";
124 const char *str_strict
= " strict";
125 const char *str_construct
= " construct";
126 const char *str_prevyield
= " preventsyield";
127 const char *str_directeval
= " directeval";
128 const char *str_empty
= "";
130 DUK_ASSERT_TOP(ctx
, 0); /* fixed arg count */
134 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_TRACEDATA
);
135 idx_td
= duk_get_top_index(ctx
);
137 duk_push_hstring_stridx(ctx
, DUK_STRIDX_NEWLINE_4SPACE
);
140 /* [ ... this tracedata sep this ] */
142 /* XXX: skip null filename? */
144 if (duk_check_type(ctx
, idx_td
, DUK_TYPE_OBJECT
)) {
145 /* Current tracedata contains 2 entries per callstack entry. */
146 for (i
= 0; ; i
+= 2) {
151 const char *funcname
;
152 const char *filename
;
156 duk_require_stack(ctx
, 5);
157 duk_get_prop_index(ctx
, idx_td
, i
);
158 duk_get_prop_index(ctx
, idx_td
, i
+ 1);
159 d
= duk_to_number(ctx
, -1);
160 pc
= (duk_int_t
) DUK_FMOD(d
, DUK_DOUBLE_2TO32
);
161 flags
= (duk_int_t
) DUK_FLOOR(d
/ DUK_DOUBLE_2TO32
);
162 t
= (duk_small_int_t
) duk_get_type(ctx
, -2);
164 if (t
== DUK_TYPE_OBJECT
|| t
== DUK_TYPE_LIGHTFUNC
) {
166 * Ecmascript/native function call or lightfunc call
171 /* [ ... v1(func) v2(pc+flags) ] */
173 h_func
= duk_get_hobject(ctx
, -2); /* NULL for lightfunc */
175 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_NAME
);
176 duk_get_prop_stridx(ctx
, -3, DUK_STRIDX_FILE_NAME
);
178 #if defined(DUK_USE_PC2LINE)
179 line
= duk_hobject_pc2line_query(ctx
, -4, (duk_uint_fast32_t
) pc
);
184 /* [ ... v1 v2 name filename ] */
186 /* When looking for .fileName/.lineNumber, blame first
187 * function which has a .fileName.
189 if (duk_is_string(ctx
, -1)) {
190 if (output_type
== DUK__OUTPUT_TYPE_FILENAME
) {
192 } else if (output_type
== DUK__OUTPUT_TYPE_LINENUMBER
) {
193 duk_push_int(ctx
, line
);
198 /* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */
199 /* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */
200 h_name
= duk_get_hstring(ctx
, -2); /* may be NULL */
201 funcname
= (h_name
== NULL
|| h_name
== DUK_HTHREAD_STRING_EMPTY_STRING(thr
)) ?
202 "[anon]" : (const char *) DUK_HSTRING_GET_DATA(h_name
);
203 filename
= duk_get_string(ctx
, -1);
204 filename
= filename
? filename
: "";
205 DUK_ASSERT(funcname
!= NULL
);
206 DUK_ASSERT(filename
!= NULL
);
208 if (h_func
== NULL
) {
209 duk_push_sprintf(ctx
, "at %s light%s%s%s%s%s",
210 (const char *) funcname
,
211 (const char *) ((flags
& DUK_ACT_FLAG_STRICT
) ? str_strict
: str_empty
),
212 (const char *) ((flags
& DUK_ACT_FLAG_TAILCALLED
) ? str_tailcall
: str_empty
),
213 (const char *) ((flags
& DUK_ACT_FLAG_CONSTRUCT
) ? str_construct
: str_empty
),
214 (const char *) ((flags
& DUK_ACT_FLAG_DIRECT_EVAL
) ? str_directeval
: str_empty
),
215 (const char *) ((flags
& DUK_ACT_FLAG_PREVENT_YIELD
) ? str_prevyield
: str_empty
));
216 } else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h_func
)) {
217 duk_push_sprintf(ctx
, "at %s (%s) native%s%s%s%s%s",
218 (const char *) funcname
,
219 (const char *) filename
,
220 (const char *) ((flags
& DUK_ACT_FLAG_STRICT
) ? str_strict
: str_empty
),
221 (const char *) ((flags
& DUK_ACT_FLAG_TAILCALLED
) ? str_tailcall
: str_empty
),
222 (const char *) ((flags
& DUK_ACT_FLAG_CONSTRUCT
) ? str_construct
: str_empty
),
223 (const char *) ((flags
& DUK_ACT_FLAG_DIRECT_EVAL
) ? str_directeval
: str_empty
),
224 (const char *) ((flags
& DUK_ACT_FLAG_PREVENT_YIELD
) ? str_prevyield
: str_empty
));
226 duk_push_sprintf(ctx
, "at %s (%s:%ld)%s%s%s%s%s",
227 (const char *) funcname
,
228 (const char *) filename
,
230 (const char *) ((flags
& DUK_ACT_FLAG_STRICT
) ? str_strict
: str_empty
),
231 (const char *) ((flags
& DUK_ACT_FLAG_TAILCALLED
) ? str_tailcall
: str_empty
),
232 (const char *) ((flags
& DUK_ACT_FLAG_CONSTRUCT
) ? str_construct
: str_empty
),
233 (const char *) ((flags
& DUK_ACT_FLAG_DIRECT_EVAL
) ? str_directeval
: str_empty
),
234 (const char *) ((flags
& DUK_ACT_FLAG_PREVENT_YIELD
) ? str_prevyield
: str_empty
));
236 duk_replace(ctx
, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */
237 duk_pop_n(ctx
, 3); /* -> [ ... str ] */
238 } else if (t
== DUK_TYPE_STRING
) {
240 * __FILE__ / __LINE__ entry, here 'pc' is line number directly.
241 * Sometimes __FILE__ / __LINE__ is reported as the source for
242 * the error (fileName, lineNumber), sometimes not.
245 /* [ ... v1(filename) v2(line+flags) ] */
247 /* When looking for .fileName/.lineNumber, blame compilation
248 * or C call site unless flagged not to do so.
250 if (!(flags
& DUK_TB_FLAG_NOBLAME_FILELINE
)) {
251 if (output_type
== DUK__OUTPUT_TYPE_FILENAME
) {
254 } else if (output_type
== DUK__OUTPUT_TYPE_LINENUMBER
) {
255 duk_push_int(ctx
, pc
);
260 duk_push_sprintf(ctx
, "at [anon] (%s:%ld) internal",
261 (const char *) duk_get_string(ctx
, -2), (long) pc
);
262 duk_replace(ctx
, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */
263 duk_pop(ctx
); /* -> [ ... str ] */
265 /* unknown, ignore */
271 if (count_func
>= DUK_USE_TRACEBACK_DEPTH
) {
272 /* Possibly truncated; there is no explicit truncation
273 * marker so this is the best we can do.
276 duk_push_hstring_stridx(ctx
, DUK_STRIDX_BRACKETED_ELLIPSIS
);
280 /* [ ... this tracedata sep this str1 ... strN ] */
282 if (output_type
!= DUK__OUTPUT_TYPE_TRACEBACK
) {
285 /* The 'this' after 'sep' will get ToString() coerced by
286 * duk_join() automatically. We don't want to do that
287 * coercion when providing .fileName or .lineNumber (GH-254).
289 duk_join(ctx
, duk_get_top(ctx
) - (idx_td
+ 2) /*count, not including sep*/);
294 /* XXX: Output type could be encoded into native function 'magic' value to
295 * save space. For setters the stridx could be encoded into 'magic'.
298 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_stack_getter(duk_context
*ctx
) {
299 return duk__error_getter_helper(ctx
, DUK__OUTPUT_TYPE_TRACEBACK
);
302 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_filename_getter(duk_context
*ctx
) {
303 return duk__error_getter_helper(ctx
, DUK__OUTPUT_TYPE_FILENAME
);
306 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_linenumber_getter(duk_context
*ctx
) {
307 return duk__error_getter_helper(ctx
, DUK__OUTPUT_TYPE_LINENUMBER
);
310 #undef DUK__OUTPUT_TYPE_TRACEBACK
311 #undef DUK__OUTPUT_TYPE_FILENAME
312 #undef DUK__OUTPUT_TYPE_LINENUMBER
314 #else /* DUK_USE_TRACEBACKS */
317 * Traceback handling when tracebacks disabled.
319 * The fileName / lineNumber stubs are now necessary because built-in
320 * data will include the accessor properties in Error.prototype. If those
321 * are removed for builds without tracebacks, these can also be removed.
322 * 'stack' should still be present and produce a ToString() equivalent:
323 * this is useful for user code which prints a stacktrace and expects to
324 * see something useful. A normal stacktrace also begins with a ToString()
325 * of the error so this makes sense.
328 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_stack_getter(duk_context
*ctx
) {
329 /* XXX: remove this native function and map 'stack' accessor
330 * to the toString() implementation directly.
332 return duk_bi_error_prototype_to_string(ctx
);
335 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_filename_getter(duk_context
*ctx
) {
340 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_linenumber_getter(duk_context
*ctx
) {
345 #endif /* DUK_USE_TRACEBACKS */
347 DUK_LOCAL duk_ret_t
duk__error_setter_helper(duk_context
*ctx
, duk_small_uint_t stridx_key
) {
348 /* Attempt to write 'stack', 'fileName', 'lineNumber' works as if
349 * user code called Object.defineProperty() to create an overriding
350 * own property. This allows user code to overwrite .fileName etc
351 * intuitively as e.g. "err.fileName = 'dummy'" as one might expect.
352 * See https://github.com/svaarala/duktape/issues/387.
355 DUK_ASSERT_TOP(ctx
, 1); /* fixed arg count: value */
358 duk_push_hstring_stridx(ctx
, (duk_small_int_t
) stridx_key
);
361 /* [ ... obj key value ] */
363 DUK_DD(DUK_DDPRINT("error setter: %!T %!T %!T",
364 duk_get_tval(ctx
, -3), duk_get_tval(ctx
, -2), duk_get_tval(ctx
, -1)));
366 duk_def_prop(ctx
, -3, DUK_DEFPROP_HAVE_VALUE
|
367 DUK_DEFPROP_HAVE_WRITABLE
| DUK_DEFPROP_WRITABLE
|
368 DUK_DEFPROP_HAVE_ENUMERABLE
| /*not enumerable*/
369 DUK_DEFPROP_HAVE_CONFIGURABLE
| DUK_DEFPROP_CONFIGURABLE
);
373 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_stack_setter(duk_context
*ctx
) {
374 return duk__error_setter_helper(ctx
, DUK_STRIDX_STACK
);
377 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_filename_setter(duk_context
*ctx
) {
378 return duk__error_setter_helper(ctx
, DUK_STRIDX_FILE_NAME
);
381 DUK_INTERNAL duk_ret_t
duk_bi_error_prototype_linenumber_setter(duk_context
*ctx
) {
382 return duk__error_setter_helper(ctx
, DUK_STRIDX_LINE_NUMBER
);