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_context *ctx = (duk_context *) thr;
64 	duk_tval *tv_hnd;
65 	duk_small_uint_t call_flags;
66 	duk_int_t rc;
67 
68 	DUK_ASSERT(thr != NULL);
69 	DUK_ASSERT(thr->heap != NULL);
70 	DUK_ASSERT_DISABLE(stridx_cb >= 0);  /* unsigned */
71 	DUK_ASSERT(stridx_cb < DUK_HEAP_NUM_STRINGS);
72 
73 	if (DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap)) {
74 		DUK_DD(DUK_DDPRINT("recursive call to error handler, ignore"));
75 		return;
76 	}
77 
78 	/*
79 	 *  Check whether or not we have an error handler.
80 	 *
81 	 *  We must be careful of not triggering an error when looking up the
82 	 *  property.  For instance, if the property is a getter, we don't want
83 	 *  to call it, only plain values are allowed.  The value, if it exists,
84 	 *  is not checked.  If the value is not a function, a TypeError happens
85 	 *  when it is called and that error replaces the original one.
86 	 */
87 
88 	DUK_ASSERT_VALSTACK_SPACE(thr, 4);  /* 3 entries actually needed below */
89 
90 	/* [ ... errval ] */
91 
92 	if (thr->builtins[DUK_BIDX_DUKTAPE] == NULL) {
93 		/* When creating built-ins, some of the built-ins may not be set
94 		 * and we want to tolerate that when throwing errors.
95 		 */
96 		DUK_DD(DUK_DDPRINT("error occurred when DUK_BIDX_DUKTAPE is NULL, ignoring"));
97 		return;
98 	}
99 	tv_hnd = duk_hobject_find_existing_entry_tval_ptr(thr->heap,
100 	                                                  thr->builtins[DUK_BIDX_DUKTAPE],
101 	                                                  DUK_HTHREAD_GET_STRING(thr, stridx_cb));
102 	if (tv_hnd == NULL) {
103 		DUK_DD(DUK_DDPRINT("error handler does not exist or is not a plain value: %!T",
104 		                   (duk_tval *) tv_hnd));
105 		return;
106 	}
107 	DUK_DDD(DUK_DDDPRINT("error handler dump (callability not checked): %!T",
108 	                     (duk_tval *) tv_hnd));
109 	duk_push_tval(ctx, tv_hnd);
110 
111 	/* [ ... errval errhandler ] */
112 
113 	duk_insert(ctx, -2);  /* -> [ ... errhandler errval ] */
114 	duk_push_undefined(ctx);
115 	duk_insert(ctx, -2);  /* -> [ ... errhandler undefined(= this) errval ] */
116 
117 	/* [ ... errhandler undefined errval ] */
118 
119 	/*
120 	 *  DUK_CALL_FLAG_IGNORE_RECLIMIT causes duk_handle_call() to ignore C
121 	 *  recursion depth limit (and won't increase it either).  This is
122 	 *  dangerous, but useful because it allows the error handler to run
123 	 *  even if the original error is caused by C recursion depth limit.
124 	 *
125 	 *  The heap level DUK_HEAP_FLAG_ERRHANDLER_RUNNING is set for the
126 	 *  duration of the error handler and cleared afterwards.  This flag
127 	 *  prevents the error handler from running recursively.  The flag is
128 	 *  heap level so that the flag properly controls even coroutines
129 	 *  launched by an error handler.  Since the flag is heap level, it is
130 	 *  critical to restore it correctly.
131 	 *
132 	 *  We ignore errors now: a success return and an error value both
133 	 *  replace the original error value.  (This would be easy to change.)
134 	 */
135 
136 	DUK_ASSERT(!DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap));  /* since no recursive error handler calls */
137 	DUK_HEAP_SET_ERRHANDLER_RUNNING(thr->heap);
138 
139 	call_flags = DUK_CALL_FLAG_IGNORE_RECLIMIT;  /* ignore reclimit, not constructor */
140 
141 	rc = duk_handle_call_protected(thr,
142 	                               1,            /* num args */
143 	                               call_flags);  /* call_flags */
144 	DUK_UNREF(rc);  /* no need to check now: both success and error are OK */
145 
146 	DUK_ASSERT(DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap));
147 	DUK_HEAP_CLEAR_ERRHANDLER_RUNNING(thr->heap);
148 
149 	/* [ ... errval ] */
150 }
151 #endif  /* DUK_USE_ERRTHROW || DUK_USE_ERRCREATE */
152 
153 /*
154  *  Add ._Tracedata to an error on the stack top.
155  */
156 
157 #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_bool_t noblame_fileline)158 DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) {
159 	duk_context *ctx = (duk_context *) thr;
160 	duk_small_uint_t depth;
161 	duk_int_t i, i_min;
162 	duk_uarridx_t arr_idx;
163 	duk_double_t d;
164 
165 	DUK_ASSERT(thr != NULL);
166 	DUK_ASSERT(thr_callstack != NULL);
167 	DUK_ASSERT(ctx != NULL);
168 
169 	/* [ ... error ] */
170 
171 	/*
172 	 *  The traceback format is pretty arcane in an attempt to keep it compact
173 	 *  and cheap to create.  It may change arbitrarily from version to version.
174 	 *  It should be decoded/accessed through version specific accessors only.
175 	 *
176 	 *  See doc/error-objects.rst.
177 	 */
178 
179 	DUK_DDD(DUK_DDDPRINT("adding traceback to object: %!T",
180 	                     (duk_tval *) duk_get_tval(ctx, -1)));
181 
182 	duk_push_array(ctx);  /* XXX: specify array size, as we know it */
183 	arr_idx = 0;
184 
185 	/* Compiler SyntaxErrors (and other errors) come first, and are
186 	 * blamed by default (not flagged "noblame").
187 	 */
188 	if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
189 		duk_push_hstring(ctx, thr->compile_ctx->h_filename);
190 		duk_xdef_prop_index_wec(ctx, -2, arr_idx);
191 		arr_idx++;
192 
193 		duk_push_uint(ctx, (duk_uint_t) thr->compile_ctx->curr_token.start_line);  /* (flags<<32) + (line), flags = 0 */
194 		duk_xdef_prop_index_wec(ctx, -2, arr_idx);
195 		arr_idx++;
196 	}
197 
198 	/* Filename/line from C macros (__FILE__, __LINE__) are added as an
199 	 * entry with a special format: (string, number).  The number contains
200 	 * the line and flags.
201 	 */
202 
203 	/* XXX: optimize: allocate an array part to the necessary size (upwards
204 	 * estimate) and fill in the values directly into the array part; finally
205 	 * update 'length'.
206 	 */
207 
208 	/* XXX: using duk_put_prop_index() would cause obscure error cases when Array.prototype
209 	 * has write-protected array index named properties.  This was seen as DoubleErrors
210 	 * in e.g. some test262 test cases.  Using duk_xdef_prop_index() is better but heavier.
211 	 * The best fix is to fill in the tracedata directly into the array part.  There are
212 	 * no side effect concerns if the array part is allocated directly and only INCREFs
213 	 * happen after that.
214 	 */
215 
216 	/* [ ... error arr ] */
217 
218 	if (c_filename) {
219 		duk_push_string(ctx, c_filename);
220 		duk_xdef_prop_index_wec(ctx, -2, arr_idx);
221 		arr_idx++;
222 
223 		d = (noblame_fileline ? ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) +
224 		    (duk_double_t) c_line;
225 		duk_push_number(ctx, d);
226 		duk_xdef_prop_index_wec(ctx, -2, arr_idx);
227 		arr_idx++;
228 	}
229 
230 	/* traceback depth doesn't take into account the filename/line
231 	 * special handling above (intentional)
232 	 */
233 	depth = DUK_USE_TRACEBACK_DEPTH;
234 	i_min = (thr_callstack->callstack_top > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0);
235 	DUK_ASSERT(i_min >= 0);
236 
237 	/* [ ... error arr ] */
238 
239 	DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX);  /* callstack limits */
240 	for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) {
241 		duk_uint32_t pc;
242 
243 		/*
244 		 *  Note: each API operation potentially resizes the callstack,
245 		 *  so be careful to re-lookup after every operation.  Currently
246 		 *  these is no issue because we don't store a temporary 'act'
247 		 *  pointer at all.  (This would be a non-issue if we operated
248 		 *  directly on the array part.)
249 		 */
250 
251 		/* [... arr] */
252 
253 		DUK_ASSERT_DISABLE(thr_callstack->callstack[i].pc >= 0);  /* unsigned */
254 
255 		/* Add function object. */
256 		duk_push_tval(ctx, &(thr_callstack->callstack + i)->tv_func);
257 		duk_xdef_prop_index_wec(ctx, -2, arr_idx);
258 		arr_idx++;
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_hthread_get_act_prev_pc(thr_callstack, thr_callstack->callstack + i);
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) thr_callstack->callstack[i].flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc;
269 		duk_push_number(ctx, d);  /* -> [... arr num] */
270 		duk_xdef_prop_index_wec(ctx, -2, arr_idx);
271 		arr_idx++;
272 	}
273 
274 	/* XXX: set with duk_hobject_set_length() when tracedata is filled directly */
275 	duk_push_uint(ctx, (duk_uint_t) arr_idx);
276 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC);
277 
278 	/* [ ... error arr ] */
279 
280 	duk_xdef_prop_stridx_wec(ctx, -2, DUK_STRIDX_INT_TRACEDATA);  /* -> [ ... error ] */
281 }
282 #endif  /* DUK_USE_TRACEBACKS */
283 
284 /*
285  *  Add .fileName and .lineNumber to an error on the stack top.
286  */
287 
288 #if !defined(DUK_USE_TRACEBACKS)
duk__add_fileline(duk_hthread * thr,duk_hthread * thr_callstack,const char * c_filename,duk_int_t c_line,duk_bool_t noblame_fileline)289 DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) {
290 	duk_context *ctx;
291 #if defined(DUK_USE_ASSERTIONS)
292 	duk_int_t entry_top;
293 #endif
294 
295 	ctx = (duk_context *) thr;
296 #if defined(DUK_USE_ASSERTIONS)
297 	entry_top = duk_get_top(ctx);
298 #endif
299 
300 	/*
301 	 *  If tracebacks are disabled, 'fileName' and 'lineNumber' are added
302 	 *  as plain own properties.  Since Error.prototype has accessors of
303 	 *  the same name, we need to define own properties directly (cannot
304 	 *  just use e.g. duk_put_prop_stridx).  Existing properties are not
305 	 *  overwritten in case they already exist.
306 	 */
307 
308 	if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
309 		/* Compiler SyntaxError (or other error) gets the primary blame.
310 		 * Currently no flag to prevent blaming.
311 		 */
312 		duk_push_uint(ctx, (duk_uint_t) thr->compile_ctx->curr_token.start_line);
313 		duk_push_hstring(ctx, thr->compile_ctx->h_filename);
314 	} else if (c_filename && !noblame_fileline) {
315 		/* C call site gets blamed next, unless flagged not to do so.
316 		 * XXX: file/line is disabled in minimal builds, so disable this
317 		 * too when appropriate.
318 		 */
319 		duk_push_int(ctx, c_line);
320 		duk_push_string(ctx, c_filename);
321 	} else {
322 		/* Finally, blame the innermost callstack entry which has a
323 		 * .fileName property.
324 		 */
325 		duk_small_uint_t depth;
326 		duk_int_t i, i_min;
327 		duk_uint32_t ecma_line;
328 
329 		depth = DUK_USE_TRACEBACK_DEPTH;
330 		i_min = (thr_callstack->callstack_top > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0);
331 		DUK_ASSERT(i_min >= 0);
332 
333 		DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX);  /* callstack limits */
334 		for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) {
335 			duk_activation *act;
336 			duk_hobject *func;
337 			duk_uint32_t pc;
338 
339 			DUK_UNREF(pc);
340 			act = thr_callstack->callstack + i;
341 			DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size);
342 
343 			func = DUK_ACT_GET_FUNC(act);
344 			if (func == NULL) {
345 				/* Lightfunc, not blamed now. */
346 				continue;
347 			}
348 
349 			/* PC points to next instruction, find offending PC,
350 			 * PC == 0 for native code.
351 			 */
352 			pc = duk_hthread_get_act_prev_pc(thr, act);  /* thr argument only used for thr->heap, so specific thread doesn't matter */
353 			DUK_ASSERT_DISABLE(pc >= 0);  /* unsigned */
354 			DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
355 			act = NULL;  /* invalidated by pushes, so get out of the way */
356 
357 			duk_push_hobject(ctx, func);
358 
359 			/* [ ... error func ] */
360 
361 			duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME);
362 			if (!duk_is_string(ctx, -1)) {
363 				duk_pop_2(ctx);
364 				continue;
365 			}
366 
367 			/* [ ... error func fileName ] */
368 
369 			ecma_line = 0;
370 #if defined(DUK_USE_PC2LINE)
371 			if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
372 				ecma_line = duk_hobject_pc2line_query(ctx, -2, (duk_uint_fast32_t) pc);
373 			} else {
374 				/* Native function, no relevant lineNumber. */
375 			}
376 #endif  /* DUK_USE_PC2LINE */
377 			duk_push_u32(ctx, ecma_line);
378 
379 			/* [ ... error func fileName lineNumber ] */
380 
381 			duk_replace(ctx, -3);
382 
383 			/* [ ... error lineNumber fileName ] */
384 			goto define_props;
385 		}
386 
387 		/* No activation matches, use undefined for both .fileName and
388 		 * .lineNumber (matches what we do with a _Tracedata based
389 		 * no-match lookup.
390 		 */
391 		duk_push_undefined(ctx);
392 		duk_push_undefined(ctx);
393 	}
394 
395  define_props:
396 	/* [ ... error lineNumber fileName ] */
397 #if defined(DUK_USE_ASSERTIONS)
398 	DUK_ASSERT(duk_get_top(ctx) == entry_top + 2);
399 #endif
400 	duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
401 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
402 }
403 #endif  /* !DUK_USE_TRACEBACKS */
404 
405 /*
406  *  Add line number to a compiler error.
407  */
408 
duk__add_compiler_error_line(duk_hthread * thr)409 DUK_LOCAL void duk__add_compiler_error_line(duk_hthread *thr) {
410 	duk_context *ctx;
411 
412 	/* Append a "(line NNN)" to the "message" property of any error
413 	 * thrown during compilation.  Usually compilation errors are
414 	 * SyntaxErrors but they can also be out-of-memory errors and
415 	 * the like.
416 	 */
417 
418 	/* [ ... error ] */
419 
420 	ctx = (duk_context *) thr;
421 	DUK_ASSERT(duk_is_object(ctx, -1));
422 
423 	if (!(thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL)) {
424 		return;
425 	}
426 
427 	DUK_DDD(DUK_DDDPRINT("compile error, before adding line info: %!T",
428 	                     (duk_tval *) duk_get_tval(ctx, -1)));
429 
430 	if (duk_get_prop_stridx(ctx, -1, DUK_STRIDX_MESSAGE)) {
431 		duk_push_sprintf(ctx, " (line %ld)", (long) thr->compile_ctx->curr_token.start_line);
432 		duk_concat(ctx, 2);
433 		duk_put_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE);
434 	} else {
435 		duk_pop(ctx);
436 	}
437 
438 	DUK_DDD(DUK_DDDPRINT("compile error, after adding line info: %!T",
439 	                     (duk_tval *) duk_get_tval(ctx, -1)));
440 }
441 
442 /*
443  *  Augment an error being created using Duktape specific properties
444  *  like _Tracedata or .fileName/.lineNumber.
445  */
446 
447 #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_small_int_t noblame_fileline,duk_hobject * obj)448 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_small_int_t noblame_fileline, duk_hobject *obj) {
449 	duk_context *ctx = (duk_context *) thr;
450 #if defined(DUK_USE_ASSERTIONS)
451 	duk_int_t entry_top;
452 #endif
453 
454 #if defined(DUK_USE_ASSERTIONS)
455 	entry_top = duk_get_top(ctx);
456 #endif
457 	DUK_ASSERT(obj != NULL);
458 
459 	DUK_UNREF(obj);  /* unreferenced w/o tracebacks */
460 	DUK_UNREF(ctx);  /* unreferenced w/o asserts */
461 
462 	duk__add_compiler_error_line(thr);
463 
464 #if defined(DUK_USE_TRACEBACKS)
465 	/* If tracebacks are enabled, the '_Tracedata' property is the only
466 	 * thing we need: 'fileName' and 'lineNumber' are virtual properties
467 	 * which use '_Tracedata'.
468 	 */
469 	if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_INT_TRACEDATA(thr))) {
470 		DUK_DDD(DUK_DDDPRINT("error value already has a '_Tracedata' property, not modifying it"));
471 	} else {
472 		duk__add_traceback(thr, thr_callstack, c_filename, c_line, noblame_fileline);
473 	}
474 #else
475 	/* Without tracebacks the concrete .fileName and .lineNumber need
476 	 * to be added directly.
477 	 */
478 	duk__add_fileline(thr, thr_callstack, c_filename, c_line, noblame_fileline);
479 #endif
480 
481 #if defined(DUK_USE_ASSERTIONS)
482 	DUK_ASSERT(duk_get_top(ctx) == entry_top);
483 #endif
484 }
485 #endif  /* DUK_USE_AUGMENT_ERROR_CREATE */
486 
487 /*
488  *  Augment an error at creation time with _Tracedata/fileName/lineNumber
489  *  and allow a user error handler (if defined) to process/replace the error.
490  *  The error to be augmented is at the stack top.
491  *
492  *  thr: thread containing the error value
493  *  thr_callstack: thread which should be used for generating callstack etc.
494  *  c_filename: C __FILE__ related to the error
495  *  c_line: C __LINE__ related to the error
496  *  noblame_fileline: if true, don't fileName/line as error source, otherwise use traceback
497  *                    (needed because user code filename/line are reported but internal ones
498  *                    are not)
499  *
500  *  XXX: rename noblame_fileline to flags field; combine it to some existing
501  *  field (there are only a few call sites so this may not be worth it).
502  */
503 
504 #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_bool_t noblame_fileline)505 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_bool_t noblame_fileline) {
506 	duk_context *ctx = (duk_context *) thr;
507 	duk_hobject *obj;
508 
509 	DUK_ASSERT(thr != NULL);
510 	DUK_ASSERT(thr_callstack != NULL);
511 	DUK_ASSERT(ctx != NULL);
512 
513 	/* [ ... error ] */
514 
515 	/*
516 	 *  Criteria for augmenting:
517 	 *
518 	 *   - augmentation enabled in build (naturally)
519 	 *   - error value internal prototype chain contains the built-in
520 	 *     Error prototype object (i.e. 'val instanceof Error')
521 	 *
522 	 *  Additional criteria for built-in augmenting:
523 	 *
524 	 *   - error value is an extensible object
525 	 */
526 
527 	obj = duk_get_hobject(ctx, -1);
528 	if (!obj) {
529 		DUK_DDD(DUK_DDDPRINT("value is not an object, skip both built-in and user augment"));
530 		return;
531 	}
532 	if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], 1 /*ignore_loop*/)) {
533 		/* If the value has a prototype loop, it's critical not to
534 		 * throw here.  Instead, assume the value is not to be
535 		 * augmented.
536 		 */
537 		DUK_DDD(DUK_DDDPRINT("value is not an error instance, skip both built-in and user augment"));
538 		return;
539 	}
540 	if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) {
541 		DUK_DDD(DUK_DDDPRINT("error meets criteria, built-in augment"));
542 		duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, noblame_fileline, obj);
543 	} else {
544 		DUK_DDD(DUK_DDDPRINT("error does not meet criteria, no built-in augment"));
545 	}
546 
547 	/* [ ... error ] */
548 
549 #if defined(DUK_USE_ERRCREATE)
550 	duk__err_augment_user(thr, DUK_STRIDX_ERR_CREATE);
551 #endif
552 }
553 #endif  /* DUK_USE_AUGMENT_ERROR_CREATE */
554 
555 /*
556  *  Augment an error at throw time; allow a user error handler (if defined)
557  *  to process/replace the error.  The error to be augmented is at the
558  *  stack top.
559  */
560 
561 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
duk_err_augment_error_throw(duk_hthread * thr)562 DUK_INTERNAL void duk_err_augment_error_throw(duk_hthread *thr) {
563 #if defined(DUK_USE_ERRTHROW)
564 	duk__err_augment_user(thr, DUK_STRIDX_ERR_THROW);
565 #endif  /* DUK_USE_ERRTHROW */
566 }
567 #endif  /* DUK_USE_AUGMENT_ERROR_THROW */
568