1 /*
2 * Augmenting errors at their creation site and their throw site.
3 *
4 * When errors are created, traceback data is added by built-in code
5 * and a user error handler (if defined) can process or replace the
6 * error. Similarly, when errors are thrown, a user error handler
7 * (if defined) can process or replace the error.
8 *
9 * Augmentation and other processing at error creation time is nice
10 * because an error is only created once, but it may be thrown and
11 * rethrown multiple times. User error handler registered for processing
12 * an error at its throw site must be careful to handle rethrowing in
13 * a useful manner.
14 *
15 * Error augmentation may throw an internal error (e.g. alloc error).
16 *
17 * ECMAScript allows throwing any values, so all values cannot be
18 * augmented. Currently, the built-in augmentation at error creation
19 * only augments error values which are Error instances (= have the
20 * built-in Error.prototype in their prototype chain) and are also
21 * extensible. User error handlers have no limitations in this respect.
22 */
23
24 #include "duk_internal.h"
25
26 /*
27 * Helper for calling a user error handler.
28 *
29 * 'thr' must be the currently active thread; the error handler is called
30 * in its context. The valstack of 'thr' must have the error value on
31 * top, and will be replaced by another error value based on the return
32 * value of the error handler.
33 *
34 * The helper calls duk_handle_call() recursively in protected mode.
35 * Before that call happens, no longjmps should happen; as a consequence,
36 * we must assume that the valstack contains enough temporary space for
37 * arguments and such.
38 *
39 * While the error handler runs, any errors thrown will not trigger a
40 * recursive error handler call (this is implemented using a heap level
41 * flag which will "follow" through any coroutines resumed inside the
42 * error handler). If the error handler is not callable or throws an
43 * error, the resulting error replaces the original error (for Duktape
44 * internal errors, duk_error_throw.c further substitutes this error with
45 * a DoubleError which is not ideal). This would be easy to change and
46 * even signal to the caller.
47 *
48 * The user error handler is stored in 'Duktape.errCreate' or
49 * 'Duktape.errThrow' depending on whether we're augmenting the error at
50 * creation or throw time. There are several alternatives to this approach,
51 * see doc/error-objects.rst for discussion.
52 *
53 * Note: since further longjmp()s may occur while calling the error handler
54 * (for many reasons, e.g. a labeled 'break' inside the handler), the
55 * caller can make no assumptions on the thr->heap->lj state after the
56 * call (this affects especially duk_error_throw.c). This is not an issue
57 * as long as the caller writes to the lj state only after the error handler
58 * finishes.
59 */
60
61 #if defined(DUK_USE_ERRTHROW) || defined(DUK_USE_ERRCREATE)
duk__err_augment_user(duk_hthread * thr,duk_small_uint_t stridx_cb)62 DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_cb) {
63 duk_tval *tv_hnd;
64 duk_int_t rc;
65
66 DUK_ASSERT(thr != NULL);
67 DUK_ASSERT(thr->heap != NULL);
68 DUK_ASSERT_STRIDX_VALID(stridx_cb);
69
70 if (thr->heap->augmenting_error) {
71 DUK_D(DUK_DPRINT("recursive call to error augmentation, ignore"));
72 return;
73 }
74
75 /*
76 * Check whether or not we have an error handler.
77 *
78 * We must be careful of not triggering an error when looking up the
79 * property. For instance, if the property is a getter, we don't want
80 * to call it, only plain values are allowed. The value, if it exists,
81 * is not checked. If the value is not a function, a TypeError happens
82 * when it is called and that error replaces the original one.
83 */
84
85 DUK_ASSERT_VALSTACK_SPACE(thr, 4); /* 3 entries actually needed below */
86
87 /* [ ... errval ] */
88
89 if (thr->builtins[DUK_BIDX_DUKTAPE] == NULL) {
90 /* When creating built-ins, some of the built-ins may not be set
91 * and we want to tolerate that when throwing errors.
92 */
93 DUK_DD(DUK_DDPRINT("error occurred when DUK_BIDX_DUKTAPE is NULL, ignoring"));
94 return;
95 }
96 tv_hnd = duk_hobject_find_entry_tval_ptr_stridx(thr->heap,
97 thr->builtins[DUK_BIDX_DUKTAPE],
98 stridx_cb);
99 if (tv_hnd == NULL) {
100 DUK_DD(DUK_DDPRINT("error handler does not exist or is not a plain value: %!T",
101 (duk_tval *) tv_hnd));
102 return;
103 }
104 DUK_DDD(DUK_DDDPRINT("error handler dump (callability not checked): %!T",
105 (duk_tval *) tv_hnd));
106 duk_push_tval(thr, tv_hnd);
107
108 /* [ ... errval errhandler ] */
109
110 duk_insert(thr, -2); /* -> [ ... errhandler errval ] */
111 duk_push_undefined(thr);
112 duk_insert(thr, -2); /* -> [ ... errhandler undefined(= this) errval ] */
113
114 /* [ ... errhandler undefined errval ] */
115
116 /*
117 * heap->augmenting_error prevents recursive re-entry and also causes
118 * call handling to use a larger (but not unbounded) call stack limit
119 * for the duration of error augmentation.
120 *
121 * We ignore errors now: a success return and an error value both
122 * replace the original error value. (This would be easy to change.)
123 */
124
125 DUK_ASSERT(thr->heap->augmenting_error == 0);
126 thr->heap->augmenting_error = 1;
127
128 rc = duk_pcall_method(thr, 1);
129 DUK_UNREF(rc); /* no need to check now: both success and error are OK */
130
131 DUK_ASSERT(thr->heap->augmenting_error == 1);
132 thr->heap->augmenting_error = 0;
133
134 /* [ ... errval ] */
135 }
136 #endif /* DUK_USE_ERRTHROW || DUK_USE_ERRCREATE */
137
138 /*
139 * Add ._Tracedata to an error on the stack top.
140 */
141
142 #if defined(DUK_USE_TRACEBACKS)
duk__add_traceback(duk_hthread * thr,duk_hthread * thr_callstack,const char * c_filename,duk_int_t c_line,duk_small_uint_t flags)143 DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
144 duk_activation *act;
145 duk_int_t depth;
146 duk_int_t arr_size;
147 duk_tval *tv;
148 duk_hstring *s;
149 duk_uint32_t u32;
150 duk_double_t d;
151
152 DUK_ASSERT(thr != NULL);
153 DUK_ASSERT(thr_callstack != NULL);
154
155 /* [ ... error ] */
156
157 /*
158 * The traceback format is pretty arcane in an attempt to keep it compact
159 * and cheap to create. It may change arbitrarily from version to version.
160 * It should be decoded/accessed through version specific accessors only.
161 *
162 * See doc/error-objects.rst.
163 */
164
165 DUK_DDD(DUK_DDDPRINT("adding traceback to object: %!T",
166 (duk_tval *) duk_get_tval(thr, -1)));
167
168 /* Preallocate array to correct size, so that we can just write out
169 * the _Tracedata values into the array part.
170 */
171 act = thr->callstack_curr;
172 depth = DUK_USE_TRACEBACK_DEPTH;
173 DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX); /* callstack limits */
174 if (depth > (duk_int_t) thr_callstack->callstack_top) {
175 depth = (duk_int_t) thr_callstack->callstack_top;
176 }
177 if (depth > 0) {
178 if (flags & DUK_AUGMENT_FLAG_SKIP_ONE) {
179 DUK_ASSERT(act != NULL);
180 act = act->parent;
181 depth--;
182 }
183 }
184 arr_size = depth * 2;
185 if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
186 arr_size += 2;
187 }
188 if (c_filename) {
189 /* We need the C filename to be interned before getting the
190 * array part pointer to avoid any GC interference while the
191 * array part is populated.
192 */
193 duk_push_string(thr, c_filename);
194 arr_size += 2;
195 }
196
197 /* XXX: Uninitialized would be OK. Maybe add internal primitive to
198 * push bare duk_harray with size?
199 */
200 DUK_D(DUK_DPRINT("preallocated _Tracedata to %ld items", (long) arr_size));
201 tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) arr_size);
202 duk_clear_prototype(thr, -1);
203 DUK_ASSERT(duk_is_bare_object(thr, -1));
204 DUK_ASSERT(arr_size == 0 || tv != NULL);
205
206 /* Compiler SyntaxErrors (and other errors) come first, and are
207 * blamed by default (not flagged "noblame").
208 */
209 if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
210 s = thr->compile_ctx->h_filename;
211 DUK_TVAL_SET_STRING(tv, s);
212 DUK_HSTRING_INCREF(thr, s);
213 tv++;
214
215 u32 = (duk_uint32_t) thr->compile_ctx->curr_token.start_line; /* (flags<<32) + (line), flags = 0 */
216 DUK_TVAL_SET_U32(tv, u32);
217 tv++;
218 }
219
220 /* Filename/line from C macros (__FILE__, __LINE__) are added as an
221 * entry with a special format: (string, number). The number contains
222 * the line and flags.
223 */
224
225 /* [ ... error c_filename? arr ] */
226
227 if (c_filename) {
228 DUK_ASSERT(DUK_TVAL_IS_STRING(thr->valstack_top - 2));
229 s = DUK_TVAL_GET_STRING(thr->valstack_top - 2); /* interned c_filename */
230 DUK_ASSERT(s != NULL);
231 DUK_TVAL_SET_STRING(tv, s);
232 DUK_HSTRING_INCREF(thr, s);
233 tv++;
234
235 d = ((flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE) ? ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) +
236 (duk_double_t) c_line;
237 DUK_TVAL_SET_DOUBLE(tv, d);
238 tv++;
239 }
240
241 /* Traceback depth doesn't take into account the filename/line
242 * special handling above (intentional).
243 */
244 for (; depth-- > 0; act = act->parent) {
245 duk_uint32_t pc;
246 duk_tval *tv_src;
247
248 /* [... arr] */
249
250 DUK_ASSERT(act != NULL); /* depth check above, assumes book-keeping is correct */
251 DUK_ASSERT_DISABLE(act->pc >= 0); /* unsigned */
252
253 /* Add function object. */
254 tv_src = &act->tv_func; /* object (function) or lightfunc */
255 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_src) || DUK_TVAL_IS_LIGHTFUNC(tv_src));
256 DUK_TVAL_SET_TVAL(tv, tv_src);
257 DUK_TVAL_INCREF(thr, tv);
258 tv++;
259
260 /* Add a number containing: pc, activation flags.
261 *
262 * PC points to next instruction, find offending PC. Note that
263 * PC == 0 for native code.
264 */
265 pc = (duk_uint32_t) duk_hthread_get_act_prev_pc(thr_callstack, act);
266 DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */
267 DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */
268 d = ((duk_double_t) act->flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc;
269 DUK_TVAL_SET_DOUBLE(tv, d);
270 tv++;
271 }
272
273 #if defined(DUK_USE_ASSERTIONS)
274 {
275 duk_harray *a;
276 a = (duk_harray *) duk_known_hobject(thr, -1);
277 DUK_ASSERT(a != NULL);
278 DUK_ASSERT((duk_uint32_t) (tv - DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a)) == a->length);
279 DUK_ASSERT(a->length == (duk_uint32_t) arr_size);
280 DUK_ASSERT(duk_is_bare_object(thr, -1));
281 }
282 #endif
283
284 /* [ ... error c_filename? arr ] */
285
286 if (c_filename) {
287 duk_remove_m2(thr);
288 }
289
290 /* [ ... error arr ] */
291
292 duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INT_TRACEDATA); /* -> [ ... error ] */
293 }
294 #endif /* DUK_USE_TRACEBACKS */
295
296 /*
297 * Add .fileName and .lineNumber to an error on the stack top.
298 */
299
300 #if defined(DUK_USE_AUGMENT_ERROR_CREATE) && !defined(DUK_USE_TRACEBACKS)
duk__add_fileline(duk_hthread * thr,duk_hthread * thr_callstack,const char * c_filename,duk_int_t c_line,duk_small_uint_t flags)301 DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
302 #if defined(DUK_USE_ASSERTIONS)
303 duk_int_t entry_top;
304 #endif
305
306 #if defined(DUK_USE_ASSERTIONS)
307 entry_top = duk_get_top(thr);
308 #endif
309
310 /*
311 * If tracebacks are disabled, 'fileName' and 'lineNumber' are added
312 * as plain own properties. Since Error.prototype has accessors of
313 * the same name, we need to define own properties directly (cannot
314 * just use e.g. duk_put_prop_stridx). Existing properties are not
315 * overwritten in case they already exist.
316 */
317
318 if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
319 /* Compiler SyntaxError (or other error) gets the primary blame.
320 * Currently no flag to prevent blaming.
321 */
322 duk_push_uint(thr, (duk_uint_t) thr->compile_ctx->curr_token.start_line);
323 duk_push_hstring(thr, thr->compile_ctx->h_filename);
324 } else if (c_filename && (flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE) == 0) {
325 /* C call site gets blamed next, unless flagged not to do so.
326 * XXX: file/line is disabled in minimal builds, so disable this
327 * too when appropriate.
328 */
329 duk_push_int(thr, c_line);
330 duk_push_string(thr, c_filename);
331 } else {
332 /* Finally, blame the innermost callstack entry which has a
333 * .fileName property.
334 */
335 duk_small_uint_t depth;
336 duk_uint32_t ecma_line;
337 duk_activation *act;
338
339 DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX); /* callstack limits */
340 depth = DUK_USE_TRACEBACK_DEPTH;
341 if (depth > thr_callstack->callstack_top) {
342 depth = thr_callstack->callstack_top;
343 }
344 for (act = thr_callstack->callstack_curr; depth-- > 0; act = act->parent) {
345 duk_hobject *func;
346 duk_uint32_t pc;
347
348 DUK_ASSERT(act != NULL);
349 func = DUK_ACT_GET_FUNC(act);
350 if (func == NULL) {
351 /* Lightfunc, not blamed now. */
352 continue;
353 }
354
355 /* PC points to next instruction, find offending PC,
356 * PC == 0 for native code.
357 */
358 pc = duk_hthread_get_act_prev_pc(thr, act); /* thr argument only used for thr->heap, so specific thread doesn't matter */
359 DUK_UNREF(pc);
360 DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */
361 DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */
362
363 duk_push_hobject(thr, func);
364
365 /* [ ... error func ] */
366
367 duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
368 if (!duk_is_string_notsymbol(thr, -1)) {
369 duk_pop_2(thr);
370 continue;
371 }
372
373 /* [ ... error func fileName ] */
374
375 ecma_line = 0;
376 #if defined(DUK_USE_PC2LINE)
377 if (DUK_HOBJECT_IS_COMPFUNC(func)) {
378 ecma_line = duk_hobject_pc2line_query(thr, -2, (duk_uint_fast32_t) pc);
379 } else {
380 /* Native function, no relevant lineNumber. */
381 }
382 #endif /* DUK_USE_PC2LINE */
383 duk_push_u32(thr, ecma_line);
384
385 /* [ ... error func fileName lineNumber ] */
386
387 duk_replace(thr, -3);
388
389 /* [ ... error lineNumber fileName ] */
390 goto define_props;
391 }
392
393 /* No activation matches, use undefined for both .fileName and
394 * .lineNumber (matches what we do with a _Tracedata based
395 * no-match lookup.
396 */
397 duk_push_undefined(thr);
398 duk_push_undefined(thr);
399 }
400
401 define_props:
402 /* [ ... error lineNumber fileName ] */
403 #if defined(DUK_USE_ASSERTIONS)
404 DUK_ASSERT(duk_get_top(thr) == entry_top + 2);
405 #endif
406 duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE);
407 duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE);
408 }
409 #endif /* DUK_USE_AUGMENT_ERROR_CREATE && !DUK_USE_TRACEBACKS */
410
411 /*
412 * Add line number to a compiler error.
413 */
414
415 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
duk__add_compiler_error_line(duk_hthread * thr)416 DUK_LOCAL void duk__add_compiler_error_line(duk_hthread *thr) {
417
418 /* Append a "(line NNN)" to the "message" property of any error
419 * thrown during compilation. Usually compilation errors are
420 * SyntaxErrors but they can also be out-of-memory errors and
421 * the like.
422 */
423
424 /* [ ... error ] */
425
426 DUK_ASSERT(duk_is_object(thr, -1));
427
428 if (!(thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL)) {
429 return;
430 }
431
432 DUK_DDD(DUK_DDDPRINT("compile error, before adding line info: %!T",
433 (duk_tval *) duk_get_tval(thr, -1)));
434
435 if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_MESSAGE)) {
436 duk_bool_t at_end;
437
438 /* Best guesstimate that error occurred at end of input, token
439 * truncated by end of input, etc.
440 */
441 #if 0
442 at_end = (thr->compile_ctx->curr_token.start_offset + 1 >= thr->compile_ctx->lex.input_length);
443 at_end = (thr->compile_ctx->lex.window[0].codepoint < 0 || thr->compile_ctx->lex.window[1].codepoint < 0);
444 #endif
445 at_end = (thr->compile_ctx->lex.window[0].codepoint < 0);
446
447 DUK_D(DUK_DPRINT("syntax error, determined at_end=%ld; curr_token.start_offset=%ld, "
448 "lex.input_length=%ld, window[0].codepoint=%ld, window[1].codepoint=%ld",
449 (long) at_end,
450 (long) thr->compile_ctx->curr_token.start_offset,
451 (long) thr->compile_ctx->lex.input_length,
452 (long) thr->compile_ctx->lex.window[0].codepoint,
453 (long) thr->compile_ctx->lex.window[1].codepoint));
454
455 duk_push_sprintf(thr, " (line %ld%s)",
456 (long) thr->compile_ctx->curr_token.start_line,
457 at_end ? ", end of input" : "");
458 duk_concat(thr, 2);
459 duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE);
460 } else {
461 duk_pop(thr);
462 }
463
464 DUK_DDD(DUK_DDDPRINT("compile error, after adding line info: %!T",
465 (duk_tval *) duk_get_tval(thr, -1)));
466 }
467 #endif /* DUK_USE_AUGMENT_ERROR_CREATE */
468
469 /*
470 * Augment an error being created using Duktape specific properties
471 * like _Tracedata or .fileName/.lineNumber.
472 */
473
474 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
duk__err_augment_builtin_create(duk_hthread * thr,duk_hthread * thr_callstack,const char * c_filename,duk_int_t c_line,duk_hobject * obj,duk_small_uint_t flags)475 DUK_LOCAL void duk__err_augment_builtin_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_hobject *obj, duk_small_uint_t flags) {
476 #if defined(DUK_USE_ASSERTIONS)
477 duk_int_t entry_top;
478 #endif
479
480 #if defined(DUK_USE_ASSERTIONS)
481 entry_top = duk_get_top(thr);
482 #endif
483 DUK_ASSERT(obj != NULL);
484
485 DUK_UNREF(obj); /* unreferenced w/o tracebacks */
486
487 duk__add_compiler_error_line(thr);
488
489 #if defined(DUK_USE_TRACEBACKS)
490 /* If tracebacks are enabled, the '_Tracedata' property is the only
491 * thing we need: 'fileName' and 'lineNumber' are virtual properties
492 * which use '_Tracedata'. (Check _Tracedata only as own property.)
493 */
494 if (duk_hobject_find_entry_tval_ptr_stridx(thr->heap, obj, DUK_STRIDX_INT_TRACEDATA) != NULL) {
495 DUK_DDD(DUK_DDDPRINT("error value already has a '_Tracedata' property, not modifying it"));
496 } else {
497 duk__add_traceback(thr, thr_callstack, c_filename, c_line, flags);
498 }
499 #else
500 /* Without tracebacks the concrete .fileName and .lineNumber need
501 * to be added directly.
502 */
503 duk__add_fileline(thr, thr_callstack, c_filename, c_line, flags);
504 #endif
505
506 #if defined(DUK_USE_ASSERTIONS)
507 DUK_ASSERT(duk_get_top(thr) == entry_top);
508 #endif
509 }
510 #endif /* DUK_USE_AUGMENT_ERROR_CREATE */
511
512 /*
513 * Augment an error at creation time with _Tracedata/fileName/lineNumber
514 * and allow a user error handler (if defined) to process/replace the error.
515 * The error to be augmented is at the stack top.
516 *
517 * thr: thread containing the error value
518 * thr_callstack: thread which should be used for generating callstack etc.
519 * c_filename: C __FILE__ related to the error
520 * c_line: C __LINE__ related to the error
521 * flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE:
522 * if true, don't fileName/line as error source, otherwise use traceback
523 * (needed because user code filename/line are reported but internal ones
524 * are not)
525 */
526
527 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
duk_err_augment_error_create(duk_hthread * thr,duk_hthread * thr_callstack,const char * c_filename,duk_int_t c_line,duk_small_uint_t flags)528 DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
529 duk_hobject *obj;
530
531 DUK_ASSERT(thr != NULL);
532 DUK_ASSERT(thr_callstack != NULL);
533
534 /* [ ... error ] */
535
536 /*
537 * Criteria for augmenting:
538 *
539 * - augmentation enabled in build (naturally)
540 * - error value internal prototype chain contains the built-in
541 * Error prototype object (i.e. 'val instanceof Error')
542 *
543 * Additional criteria for built-in augmenting:
544 *
545 * - error value is an extensible object
546 */
547
548 obj = duk_get_hobject(thr, -1);
549 if (!obj) {
550 DUK_DDD(DUK_DDDPRINT("value is not an object, skip both built-in and user augment"));
551 return;
552 }
553 if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], 1 /*ignore_loop*/)) {
554 /* If the value has a prototype loop, it's critical not to
555 * throw here. Instead, assume the value is not to be
556 * augmented.
557 */
558 DUK_DDD(DUK_DDDPRINT("value is not an error instance, skip both built-in and user augment"));
559 return;
560 }
561 if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) {
562 DUK_DDD(DUK_DDDPRINT("error meets criteria, built-in augment"));
563 duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, obj, flags);
564 } else {
565 DUK_DDD(DUK_DDDPRINT("error does not meet criteria, no built-in augment"));
566 }
567
568 /* [ ... error ] */
569
570 #if defined(DUK_USE_ERRCREATE)
571 duk__err_augment_user(thr, DUK_STRIDX_ERR_CREATE);
572 #endif
573 }
574 #endif /* DUK_USE_AUGMENT_ERROR_CREATE */
575
576 /*
577 * Augment an error at throw time; allow a user error handler (if defined)
578 * to process/replace the error. The error to be augmented is at the
579 * stack top.
580 */
581
582 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
duk_err_augment_error_throw(duk_hthread * thr)583 DUK_INTERNAL void duk_err_augment_error_throw(duk_hthread *thr) {
584 #if defined(DUK_USE_ERRTHROW)
585 duk__err_augment_user(thr, DUK_STRIDX_ERR_THROW);
586 #endif /* DUK_USE_ERRTHROW */
587 }
588 #endif /* DUK_USE_AUGMENT_ERROR_THROW */
589