]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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) | |
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 | ||
11fdf7f2 | 139 | call_flags = DUK_CALL_FLAG_IGNORE_RECLIMIT; /* ignore reclimit, not constructor */ |
7c673cae | 140 | |
11fdf7f2 TL |
141 | rc = duk_handle_call_protected(thr, |
142 | 1, /* num args */ | |
143 | call_flags); /* call_flags */ | |
7c673cae FG |
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 | /* | |
11fdf7f2 | 154 | * Add ._Tracedata to an error on the stack top. |
7c673cae FG |
155 | */ |
156 | ||
11fdf7f2 | 157 | #if defined(DUK_USE_TRACEBACKS) |
7c673cae FG |
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 | ||
11fdf7f2 TL |
185 | /* Compiler SyntaxErrors (and other errors) come first, and are |
186 | * blamed by default (not flagged "noblame"). | |
7c673cae FG |
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 | ||
11fdf7f2 | 198 | /* Filename/line from C macros (__FILE__, __LINE__) are added as an |
7c673cae FG |
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. | |
11fdf7f2 TL |
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. | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
284 | /* |
285 | * Add .fileName and .lineNumber to an error on the stack top. | |
286 | */ | |
287 | ||
288 | #if !defined(DUK_USE_TRACEBACKS) | |
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) | |
7c673cae FG |
292 | duk_int_t entry_top; |
293 | #endif | |
294 | ||
11fdf7f2 TL |
295 | ctx = (duk_context *) thr; |
296 | #if defined(DUK_USE_ASSERTIONS) | |
7c673cae FG |
297 | entry_top = duk_get_top(ctx); |
298 | #endif | |
7c673cae | 299 | |
7c673cae FG |
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) { | |
11fdf7f2 TL |
309 | /* Compiler SyntaxError (or other error) gets the primary blame. |
310 | * Currently no flag to prevent blaming. | |
311 | */ | |
7c673cae | 312 | duk_push_uint(ctx, (duk_uint_t) thr->compile_ctx->curr_token.start_line); |
11fdf7f2 | 313 | duk_push_hstring(ctx, thr->compile_ctx->h_filename); |
7c673cae | 314 | } else if (c_filename && !noblame_fileline) { |
11fdf7f2 TL |
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. | |
7c673cae | 318 | */ |
7c673cae | 319 | duk_push_int(ctx, c_line); |
11fdf7f2 TL |
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; | |
7c673cae FG |
337 | duk_uint32_t pc; |
338 | ||
11fdf7f2 TL |
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, | |
7c673cae FG |
350 | * PC == 0 for native code. |
351 | */ | |
11fdf7f2 | 352 | pc = duk_hthread_get_act_prev_pc(thr, act); /* thr argument only used for thr->heap, so specific thread doesn't matter */ |
7c673cae FG |
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); | |
11fdf7f2 TL |
362 | if (!duk_is_string(ctx, -1)) { |
363 | duk_pop_2(ctx); | |
364 | continue; | |
365 | } | |
366 | ||
367 | /* [ ... error func fileName ] */ | |
7c673cae | 368 | |
11fdf7f2 | 369 | ecma_line = 0; |
7c673cae FG |
370 | #if defined(DUK_USE_PC2LINE) |
371 | if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { | |
11fdf7f2 | 372 | ecma_line = duk_hobject_pc2line_query(ctx, -2, (duk_uint_fast32_t) pc); |
7c673cae FG |
373 | } else { |
374 | /* Native function, no relevant lineNumber. */ | |
375 | } | |
376 | #endif /* DUK_USE_PC2LINE */ | |
11fdf7f2 TL |
377 | duk_push_u32(ctx, ecma_line); |
378 | ||
379 | /* [ ... error func fileName lineNumber ] */ | |
380 | ||
381 | duk_replace(ctx, -3); | |
7c673cae | 382 | |
11fdf7f2 TL |
383 | /* [ ... error lineNumber fileName ] */ |
384 | goto define_props; | |
7c673cae | 385 | } |
11fdf7f2 TL |
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); | |
7c673cae | 393 | } |
7c673cae | 394 | |
11fdf7f2 TL |
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 | ||
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) | |
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) | |
7c673cae FG |
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) | |
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")); | |
11fdf7f2 | 542 | duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, noblame_fileline, obj); |
7c673cae FG |
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) | |
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 */ |