Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/core/exceptions.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
#ifdef _MSC_VER
4
#define snprintf _snprintf
5
#define vsnprintf _vsnprintf
6
#endif
7
8
static int crash_on_error = 0;
9
10
/* Maps ID of exception category to its name. */
11
0
static const char * cat_name(MVMThreadContext *tc, MVMint32 cat) {
12
0
    switch (cat) {
13
0
        case MVM_EX_CAT_CATCH:
14
0
            return "catch";
15
0
        case MVM_EX_CAT_CONTROL:
16
0
            return "control";
17
0
        case MVM_EX_CAT_NEXT:
18
0
            return "next";
19
0
        case MVM_EX_CAT_REDO:
20
0
            return "redo";
21
0
        case MVM_EX_CAT_LAST:
22
0
            return "last";
23
0
        case MVM_EX_CAT_RETURN:
24
0
            return "return";
25
0
        case MVM_EX_CAT_TAKE:
26
0
            return "take";
27
0
        case MVM_EX_CAT_WARN:
28
0
            return "warn";
29
0
        case MVM_EX_CAT_SUCCEED:
30
0
            return "succeed";
31
0
        case MVM_EX_CAT_PROCEED:
32
0
            return "proceed";
33
0
        case MVM_EX_CAT_NEXT | MVM_EX_CAT_LABELED:
34
0
            return "next_label";
35
0
        case MVM_EX_CAT_REDO | MVM_EX_CAT_LABELED:
36
0
            return "redo_label";
37
0
        case MVM_EX_CAT_LAST | MVM_EX_CAT_LABELED:
38
0
            return "last_label";
39
0
        default:
40
0
            return "unknown";
41
0
    }
42
0
}
43
44
/* Checks if an exception handler is already on the active handler stack,
45
 * so we don't re-trigger the same exception handler. Note: We have static
46
 * handlers that get reused, so also check for the same handler being in
47
 * the same frame, otherwise we consider the handler as being another one. */
48
415k
static MVMuint8 in_handler_stack(MVMThreadContext *tc, MVMFrameHandler *fh, MVMFrame *f) {
49
415k
    if (tc->active_handlers) {
50
9
        MVMActiveHandler *ah = tc->active_handlers;
51
19
        while (ah) {
52
16
            if (ah->handler == fh && ah->frame == f)
53
6
                return 1;
54
10
            ah = ah->next_handler;
55
10
        }
56
9
    }
57
415k
    return 0;
58
415k
}
59
60
/* Checks if a frame is still active. Naively, we could scan the call stack
61
 * for it, but since we always clean up ->work when a frame is removed from
62
 * the call stack we can do it in O(1) that way. */
63
273k
static MVMuint8 in_caller_chain(MVMThreadContext *tc, MVMFrame *f_maybe) {
64
273k
    return f_maybe->work ? 1 : 0;
65
273k
}
66
67
68
/* Information about a located handler. */
69
typedef struct {
70
    MVMFrame        *frame;
71
    MVMFrameHandler *handler;
72
    MVMJitHandler   *jit_handler;
73
    MVMint32         handler_out_of_dynamic_scope;
74
} LocatedHandler;
75
76
1.33M
static MVMint32 handler_can_handle(MVMFrame *f, MVMFrameHandler *fh, MVMint32 cat, MVMObject *payload) {
77
1.33M
    MVMuint32         category_mask = fh->category_mask;
78
1.33M
    MVMuint64       block_has_label = category_mask & MVM_EX_CAT_LABELED;
79
1.33M
    MVMuint64           block_label = block_has_label ? (MVMuint64)(f->work[fh->label_reg].o) : 0;
80
1.33M
    MVMuint64          thrown_label = payload ? (MVMuint64)payload : 0;
81
1.33M
    MVMuint64 identical_label_found = thrown_label == block_label;
82
595k
    return ((cat & category_mask) == cat && (!(cat & MVM_EX_CAT_LABELED) || identical_label_found))
83
739k
        || ((category_mask & MVM_EX_CAT_CONTROL) && cat != MVM_EX_CAT_CATCH);
84
1.33M
}
85
86
/* Looks through the handlers of a particular scope, and sees if one will
87
 * match what we're looking for. Returns 1 to it if so; if not,
88
 * returns 0. */
89
static MVMint32 search_frame_handlers(MVMThreadContext *tc, MVMFrame *f,
90
                                      MVMuint8 mode, MVMuint32 cat,
91
419k
                                      MVMObject *payload, LocatedHandler *lh) {
92
419k
    MVMuint32  i;
93
419k
    if (f->spesh_cand && f->spesh_cand->jitcode && f->jit_entry_label) {
94
1.56k
        MVMJitHandler    *jhs = f->spesh_cand->jitcode->handlers;
95
1.56k
        MVMFrameHandler  *fhs = f->effective_handlers;
96
1.56k
        MVMint32 num_handlers = f->spesh_cand->jitcode->num_handlers;
97
1.56k
        void         **labels = f->spesh_cand->jitcode->labels;
98
1.56k
        void       *cur_label = f->jit_entry_label;
99
1.58k
        for (i = 0; i < num_handlers; i++) {
100
21
            if (mode == MVM_EX_THROW_LEX && fhs[i].inlined_and_not_lexical)
101
0
                continue;
102
21
            if (!handler_can_handle(f, &fhs[i], cat, payload))
103
21
                continue;
104
0
            if (cur_label >= labels[jhs[i].start_label] &&
105
0
                cur_label <= labels[jhs[i].end_label] &&
106
0
                !in_handler_stack(tc, &fhs[i], f)) {
107
0
                lh->handler     = &fhs[i];
108
0
                lh->jit_handler = &jhs[i];
109
0
                return 1;
110
0
            }
111
0
        }
112
417k
    } else {
113
417k
        MVMint32 num_handlers = f->spesh_cand
114
141k
            ? f->spesh_cand->num_handlers
115
275k
            : f->static_info->body.num_handlers;
116
417k
        MVMint32 pc;
117
417k
        if (f == tc->cur_frame)
118
414k
            pc = (MVMuint32)(*tc->interp_cur_op - *tc->interp_bytecode_start);
119
417k
        else
120
3.09k
            pc = (MVMuint32)(f->return_address - f->effective_bytecode);
121
1.33M
        for (i = 0; i < num_handlers; i++) {
122
1.33M
            MVMFrameHandler  *fh = &f->effective_handlers[i];
123
1.33M
            if (mode == MVM_EX_THROW_LEX && fh->inlined_and_not_lexical)
124
0
                continue;
125
1.33M
            if (!handler_can_handle(f, fh, cat, payload))
126
739k
                continue;
127
595k
            if (pc >= fh->start_offset && pc <= fh->end_offset && !in_handler_stack(tc, fh, f)) {
128
415k
                lh->handler = fh;
129
415k
                return 1;
130
415k
            }
131
595k
        }
132
417k
    }
133
3.40k
    return 0;
134
419k
}
135
136
/* Searches for a handler of the specified category, relative to the given
137
 * starting frame, searching according to the chosen mode. */
138
static LocatedHandler search_for_handler_from(MVMThreadContext *tc, MVMFrame *f,
139
415k
        MVMuint8 mode, MVMuint32 cat, MVMObject *payload) {
140
415k
    LocatedHandler lh;
141
415k
    lh.frame = NULL;
142
415k
    lh.handler = NULL;
143
415k
    lh.jit_handler = NULL;
144
415k
    lh.handler_out_of_dynamic_scope = 0;
145
415k
    switch (mode) {
146
0
        case MVM_EX_THROW_LEX_CALLER:
147
0
            f = f->caller;
148
0
            while (f && f->static_info->body.is_thunk)
149
0
                f = f->caller;
150
0
            /* And now we've gone down a caller, it's just lexical... */
151
273k
        case MVM_EX_THROW_LEX:
152
273k
            while (f != NULL) {
153
273k
                if (search_frame_handlers(tc, f, MVM_EX_THROW_LEX, cat, payload, &lh)) {
154
273k
                    if (in_caller_chain(tc, f))
155
273k
                        lh.frame = f;
156
273k
                    else
157
0
                        lh.handler_out_of_dynamic_scope = 1;
158
273k
                    return lh;
159
273k
                }
160
0
                f = f->outer;
161
0
            }
162
0
            return lh;
163
142k
        case MVM_EX_THROW_DYN:
164
145k
            while (f != NULL) {
165
145k
                if (search_frame_handlers(tc, f, mode, cat, payload, &lh)) {
166
142k
                    lh.frame = f;
167
142k
                    return lh;
168
142k
                }
169
3.40k
                f = f->caller;
170
3.40k
            }
171
0
            return lh;
172
0
        case MVM_EX_THROW_LEXOTIC:
173
0
            while (f != NULL) {
174
0
                lh = search_for_handler_from(tc, f, MVM_EX_THROW_LEX, cat, payload);
175
0
                if (lh.frame != NULL)
176
0
                    return lh;
177
0
                f = f->caller;
178
0
            }
179
0
            return lh;
180
0
        default:
181
0
            MVM_panic(1, "Unhandled exception throw mode %d", (int)mode);
182
415k
    }
183
415k
}
184
185
/* Runs an exception handler (which really means updating interpreter state
186
 * so that when we return to the runloop, we're in the handler). If there is
187
 * an exception object already, it will be used; NULL can be passed if there
188
 * is not one, meaning it will be created if needed (based on the category
189
 * parameter; if ex_obj is passed, the category is not used). */
190
static void unwind_after_handler(MVMThreadContext *tc, void *sr_data);
191
static void cleanup_active_handler(MVMThreadContext *tc, void *sr_data);
192
static void run_handler(MVMThreadContext *tc, LocatedHandler lh, MVMObject *ex_obj,
193
415k
                        MVMuint32 category, MVMObject *payload) {
194
415k
    switch (lh.handler->action) {
195
273k
    case MVM_EX_ACTION_GOTO_WITH_PAYLOAD:
196
273k
        if (payload)
197
273k
            tc->last_payload = payload;
198
0
        else if (ex_obj && ((MVMException *)ex_obj)->body.payload)
199
0
            tc->last_payload = ((MVMException *)ex_obj)->body.payload;
200
0
        else
201
0
            tc->last_payload = tc->instance->VMNull;
202
273k
        /* Deliberate fallthrough to unwind below. */
203
273k
204
415k
    case MVM_EX_ACTION_GOTO:
205
415k
        if (lh.jit_handler) {
206
0
            void **labels = lh.frame->spesh_cand->jitcode->labels;
207
0
            MVMuint8  *pc = lh.frame->spesh_cand->jitcode->bytecode;
208
0
            lh.frame->jit_entry_label = labels[lh.jit_handler->goto_label];
209
0
            MVM_frame_unwind_to(tc, lh.frame, pc, 0, NULL);
210
415k
        } else {
211
415k
            MVM_frame_unwind_to(tc, lh.frame, NULL, lh.handler->goto_offset, NULL);
212
415k
        }
213
415k
        break;
214
273k
215
288
    case MVM_EX_ACTION_INVOKE: {
216
288
        /* Create active handler record. */
217
288
        MVMActiveHandler *ah = MVM_malloc(sizeof(MVMActiveHandler));
218
288
        MVMFrame *cur_frame = tc->cur_frame;
219
288
        MVMObject *handler_code;
220
288
221
288
        /* Ensure we have an exception object. */
222
288
        if (ex_obj == NULL) {
223
1
            MVMROOT(tc, cur_frame, {
224
1
            MVMROOT(tc, lh.frame, {
225
1
            MVMROOT(tc, payload, {
226
1
                ex_obj = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTException);
227
1
            });
228
1
            });
229
1
            });
230
1
            ((MVMException *)ex_obj)->body.category = category;
231
1
            MVM_ASSIGN_REF(tc, &(ex_obj->header), ((MVMException *)ex_obj)->body.payload, payload);
232
1
        }
233
288
234
288
        /* Find frame to invoke. */
235
288
        handler_code = MVM_frame_find_invokee(tc, lh.frame->work[lh.handler->block_reg].o, NULL);
236
288
237
288
        /* Install active handler record. */
238
288
        ah->frame           = lh.frame;
239
288
        ah->handler         = lh.handler;
240
288
        ah->jit_handler     = lh.jit_handler;
241
288
        ah->ex_obj          = ex_obj;
242
288
        ah->next_handler    = tc->active_handlers;
243
288
        tc->active_handlers = ah;
244
288
245
288
        /* Set up special return to unwinding after running the
246
288
         * handler. */
247
288
        cur_frame->return_value        = (MVMRegister *)&tc->last_handler_result;
248
288
        cur_frame->return_type         = MVM_RETURN_OBJ;
249
288
        cur_frame->special_return      = unwind_after_handler;
250
288
        cur_frame->special_unwind      = cleanup_active_handler;
251
288
        cur_frame->special_return_data = ah;
252
288
253
288
        /* Invoke the handler frame and return to runloop. */
254
288
        STABLE(handler_code)->invoke(tc, handler_code, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_NULL_ARGS),
255
288
                                     cur_frame->args);
256
288
        break;
257
273k
    }
258
0
    default:
259
0
        MVM_panic(1, "Unimplemented handler action");
260
415k
    }
261
415k
}
262
263
/* Unwinds after a handler. */
264
276
static void unwind_after_handler(MVMThreadContext *tc, void *sr_data) {
265
276
    MVMFrame     *frame;
266
276
    MVMException *exception;
267
276
    MVMuint32     goto_offset;
268
276
    MVMuint8     *abs_address;
269
276
270
276
271
276
    /* Get active handler; sanity check (though it's possible other cases
272
276
     * should be supported). */
273
276
    MVMActiveHandler *ah = (MVMActiveHandler *)sr_data;
274
276
    if (tc->active_handlers != ah)
275
0
        MVM_panic(1, "Trying to unwind from wrong handler");
276
276
277
276
    /* Grab info we'll need to unwind. */
278
276
    frame       = ah->frame;
279
276
    exception   = (MVMException *)ah->ex_obj;
280
276
    if (ah->jit_handler) {
281
0
        void **labels = frame->spesh_cand->jitcode->labels;
282
0
        frame->jit_entry_label = labels[ah->jit_handler->goto_label];
283
0
        abs_address = frame->spesh_cand->jitcode->bytecode;
284
0
        goto_offset = 0;
285
0
    }
286
276
    else {
287
276
        goto_offset = ah->handler->goto_offset;
288
276
        abs_address = NULL;
289
276
    }
290
276
    /* Clean up. */
291
276
    tc->active_handlers = ah->next_handler;
292
276
    MVM_free(ah);
293
276
294
276
    /* Do the unwinding as needed. */
295
276
    if (exception && exception->body.return_after_unwind) {
296
0
        MVM_frame_unwind_to(tc, frame->caller, NULL, 0, tc->last_handler_result);
297
0
    }
298
276
    else {
299
276
        MVM_frame_unwind_to(tc, frame, abs_address, goto_offset, NULL);
300
276
    }
301
276
}
302
303
/* Cleans up an active handler record if we unwind over it. */
304
3
static void cleanup_active_handler(MVMThreadContext *tc, void *sr_data) {
305
3
    /* Get active handler; sanity check (though it's possible other cases
306
3
     * should be supported). */
307
3
    MVMActiveHandler *ah = (MVMActiveHandler *)sr_data;
308
3
    if (tc->active_handlers != ah)
309
0
        MVM_panic(1, "Trying to unwind over wrong handler");
310
3
311
3
    /* Clean up. */
312
3
    tc->active_handlers = ah->next_handler;
313
3
    MVM_free(ah);
314
3
}
315
316
0
char * MVM_exception_backtrace_line(MVMThreadContext *tc, MVMFrame *cur_frame, MVMuint16 not_top) {
317
0
    MVMString *filename = cur_frame->static_info->body.cu->body.filename;
318
0
    MVMString *name = cur_frame->static_info->body.name;
319
0
    /* XXX TODO: make the caller pass in a char ** and a length pointer so
320
0
     * we can update it if necessary, and the caller can cache it. */
321
0
    char *o = MVM_malloc(1024);
322
0
    MVMuint8 *cur_op = not_top ? cur_frame->return_address : cur_frame->throw_address;
323
0
    MVMuint32 offset = cur_op - cur_frame->effective_bytecode;
324
0
    MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &cur_frame->static_info->body,
325
0
                                        offset > 0 ? offset - 1 : 0);
326
0
327
0
    MVMuint32 line_number = annot ? annot->line_number : 1;
328
0
    MVMuint16 string_heap_index = annot ? annot->filename_string_heap_index : 0;
329
0
    char *tmp1 = annot && string_heap_index < cur_frame->static_info->body.cu->body.num_strings
330
0
        ? MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc,
331
0
                cur_frame->static_info->body.cu, string_heap_index))
332
0
        : NULL;
333
0
334
0
    char *filename_c = filename
335
0
        ? MVM_string_utf8_encode_C_string(tc, filename)
336
0
        : "<ephemeral file>";
337
0
    char *name_c = name
338
0
        ? MVM_string_utf8_encode_C_string(tc, name)
339
0
        : "<anonymous frame>";
340
0
341
0
    snprintf(o, 1024, " %s %s:%u  (%s:%s)",
342
0
        not_top ? "from" : "  at",
343
0
        tmp1 ? tmp1 : "<unknown>",
344
0
        line_number,
345
0
        filename_c,
346
0
        name_c
347
0
    );
348
0
    if (filename)
349
0
        MVM_free(filename_c);
350
0
    if (name)
351
0
        MVM_free(name_c);
352
0
353
0
    if (tmp1)
354
0
        MVM_free(tmp1);
355
0
    if (annot)
356
0
        MVM_free(annot);
357
0
358
0
    return o;
359
0
}
360
361
/* Returns a list of hashes containing file, line, sub and annotations. */
362
0
MVMObject * MVM_exception_backtrace(MVMThreadContext *tc, MVMObject *ex_obj) {
363
0
    MVMFrame *cur_frame;
364
0
    MVMObject *arr = NULL, *annotations = NULL, *row = NULL, *value = NULL;
365
0
    MVMuint32 count = 0;
366
0
    MVMString *k_file = NULL, *k_line = NULL, *k_sub = NULL, *k_anno = NULL;
367
0
368
0
    if (IS_CONCRETE(ex_obj) && REPR(ex_obj)->ID == MVM_REPR_ID_MVMException)
369
0
        cur_frame = ((MVMException *)ex_obj)->body.origin;
370
0
    else
371
0
        MVM_exception_throw_adhoc(tc, "Op 'backtrace' needs an exception object");
372
0
373
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&arr);
374
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&annotations);
375
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&row);
376
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&value);
377
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_file);
378
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_line);
379
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_sub);
380
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_anno);
381
0
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&cur_frame);
382
0
383
0
    k_file = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "file");
384
0
    k_line = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "line");
385
0
    k_sub  = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "sub");
386
0
    k_anno = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "annotations");
387
0
388
0
    arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
389
0
390
0
    while (cur_frame != NULL) {
391
0
        MVMuint8             *cur_op = count ? cur_frame->return_address : cur_frame->throw_address;
392
0
        MVMuint32             offset = cur_op - cur_frame->effective_bytecode;
393
0
        MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &cur_frame->static_info->body,
394
0
                                            offset > 0 ? offset - 1 : 0);
395
0
        MVMint32              fshi   = annot ? (MVMint32)annot->filename_string_heap_index : -1;
396
0
        char            *line_number = MVM_malloc(16);
397
0
        MVMString      *filename_str;
398
0
        snprintf(line_number, 16, "%d", annot ? annot->line_number : 1);
399
0
400
0
        /* annotations hash will contain "file" and "line" */
401
0
        annotations = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash);
402
0
403
0
        /* file */
404
0
        filename_str = fshi >= 0 && fshi < cur_frame->static_info->body.cu->body.num_strings
405
0
             ? MVM_cu_string(tc, cur_frame->static_info->body.cu, fshi)
406
0
             : cur_frame->static_info->body.cu->body.filename;
407
0
        value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type,
408
0
            filename_str ? filename_str : tc->instance->str_consts.empty);
409
0
        MVM_repr_bind_key_o(tc, annotations, k_file, value);
410
0
411
0
        /* line */
412
0
        value = (MVMObject *)MVM_string_ascii_decode_nt(tc, tc->instance->VMString, line_number);
413
0
        value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, (MVMString *)value);
414
0
        MVM_repr_bind_key_o(tc, annotations, k_line, value);
415
0
        MVM_free(line_number);
416
0
417
0
        /* row will contain "sub" and "annotations" */
418
0
        row = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash);
419
0
        MVM_repr_bind_key_o(tc, row, k_sub, cur_frame->code_ref);
420
0
        MVM_repr_bind_key_o(tc, row, k_anno, annotations);
421
0
422
0
        MVM_repr_push_o(tc, arr, row);
423
0
        MVM_free(annot);
424
0
425
0
        cur_frame = cur_frame->caller;
426
0
        while (cur_frame && cur_frame->static_info->body.is_thunk)
427
0
            cur_frame = cur_frame->caller;
428
0
        count++;
429
0
    }
430
0
431
0
    MVM_gc_root_temp_pop_n(tc, 9);
432
0
433
0
    return arr;
434
0
}
435
436
/* Returns the lines (backtrace) of an exception-object as an array. */
437
0
MVMObject * MVM_exception_backtrace_strings(MVMThreadContext *tc, MVMObject *ex_obj) {
438
0
    MVMException *ex;
439
0
    MVMFrame *cur_frame;
440
0
    MVMObject *arr;
441
0
442
0
    if (IS_CONCRETE(ex_obj) && REPR(ex_obj)->ID == MVM_REPR_ID_MVMException)
443
0
        ex = (MVMException *)ex_obj;
444
0
    else
445
0
        MVM_exception_throw_adhoc(tc, "Op 'backtracestrings' needs an exception object");
446
0
447
0
    arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
448
0
    cur_frame = ex->body.origin;
449
0
450
0
    MVMROOT(tc, arr, {
451
0
    MVMROOT(tc, cur_frame, {
452
0
        MVMuint32 count = 0;
453
0
        while (cur_frame != NULL) {
454
0
            char      *line     = MVM_exception_backtrace_line(tc, cur_frame, count++);
455
0
            MVMString *line_str = MVM_string_utf8_decode(tc, tc->instance->VMString, line, strlen(line));
456
0
            MVMObject *line_obj = MVM_repr_box_str(tc, tc->instance->boot_types.BOOTStr, line_str);
457
0
            MVM_repr_push_o(tc, arr, line_obj);
458
0
            cur_frame = cur_frame->caller;
459
0
            MVM_free(line);
460
0
        }
461
0
    });
462
0
    });
463
0
464
0
    return arr;
465
0
}
466
467
/* Dumps a backtrace relative to the current frame to stderr. */
468
0
void MVM_dump_backtrace(MVMThreadContext *tc) {
469
0
    MVMFrame *cur_frame = tc->cur_frame;
470
0
    MVMuint32 count = 0;
471
0
    MVMROOT(tc, cur_frame, {
472
0
        while (cur_frame != NULL) {
473
0
            char *line = MVM_exception_backtrace_line(tc, cur_frame, count++);
474
0
            fprintf(stderr, "%s\n", line);
475
0
            MVM_free(line);
476
0
            cur_frame = cur_frame->caller;
477
0
        }
478
0
    });
479
0
}
480
481
/* Panic over an unhandled exception throw by category. */
482
0
static void panic_unhandled_cat(MVMThreadContext *tc, MVMuint32 cat) {
483
0
    /* If it's a control exception, try promoting it to a catch one. */
484
0
    if (cat != MVM_EX_CAT_CATCH) {
485
0
        MVM_exception_throw_adhoc(tc, "No exception handler located for %s",
486
0
            cat_name(tc, cat));
487
0
    }
488
0
    else {
489
0
        fprintf(stderr, "No exception handler located for %s\n", cat_name(tc, cat));
490
0
        MVM_dump_backtrace(tc);
491
0
        if (crash_on_error)
492
0
            abort();
493
0
        else
494
0
            exit(1);
495
0
    }
496
0
}
497
498
/* Panic over an unhandled exception object. */
499
0
static void panic_unhandled_ex(MVMThreadContext *tc, MVMException *ex) {
500
0
    char *backtrace;
501
0
502
0
    /* If it's a control exception, try promoting it to a catch one; use
503
0
     * the category name. */
504
0
    if (ex->body.category != MVM_EX_CAT_CATCH)
505
0
        panic_unhandled_cat(tc, ex->body.category);
506
0
507
0
    /* If there's no message, fall back to category also. */
508
0
    if (!ex->body.message)
509
0
        panic_unhandled_cat(tc, ex->body.category);
510
0
511
0
    /* Otherwise, dump message and a backtrace. */
512
0
    backtrace = MVM_string_utf8_encode_C_string(tc, ex->body.message);
513
0
    fprintf(stderr, "Unhandled exception: %s\n", backtrace);
514
0
    MVM_free(backtrace);
515
0
    MVM_dump_backtrace(tc);
516
0
    if (crash_on_error)
517
0
        abort();
518
0
    else
519
0
        exit(1);
520
0
}
521
522
/* Checks if we're throwing lexically, and - if yes - if the current HLL has
523
 * a handler for unlocated lexical handlers. */
524
0
static MVMint32 use_lexical_handler_hll_error(MVMThreadContext *tc, MVMuint8 mode) {
525
0
    return (mode == MVM_EX_THROW_LEX || mode == MVM_EX_THROW_LEX_CALLER) &&
526
0
        !MVM_is_null(tc, MVM_hll_current(tc)->lexical_handler_not_found_error);
527
0
}
528
529
/* Invokes the HLL's handler for unresolved lexical throws. */
530
0
static void invoke_lexical_handler_hll_error(MVMThreadContext *tc, MVMint64 cat, LocatedHandler lh) {
531
0
    MVMObject *handler = MVM_hll_current(tc)->lexical_handler_not_found_error;
532
0
    MVMCallsite *callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INT_INT);
533
0
    handler = MVM_frame_find_invokee(tc, handler, NULL);
534
0
    MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, callsite);
535
0
    tc->cur_frame->args[0].i64 = cat;
536
0
    tc->cur_frame->args[1].i64 = lh.handler_out_of_dynamic_scope;
537
0
    STABLE(handler)->invoke(tc, handler, callsite, tc->cur_frame->args);
538
0
}
539
540
/* Throws an exception by category, searching for a handler according to
541
 * the specified mode. If the handler resumes, the resumption result will
542
 * be put into resume_result. Leaves the interpreter in a state where it
543
 * will next run the instruction of the handler. If there is no handler,
544
 * it will panic and exit with a backtrace. */
545
141k
void MVM_exception_throwcat(MVMThreadContext *tc, MVMuint8 mode, MVMuint32 cat, MVMRegister *resume_result) {
546
141k
    LocatedHandler lh = search_for_handler_from(tc, tc->cur_frame, mode, cat, NULL);
547
141k
    if (lh.frame == NULL) {
548
0
        if (use_lexical_handler_hll_error(tc, mode)) {
549
0
            invoke_lexical_handler_hll_error(tc, cat, lh);
550
0
            return;
551
0
        }
552
0
        panic_unhandled_cat(tc, cat);
553
0
    }
554
141k
    run_handler(tc, lh, NULL, cat, NULL);
555
141k
}
556
557
154
void MVM_exception_die(MVMThreadContext *tc, MVMString *str, MVMRegister *rr) {
558
154
    MVMException *ex;
559
154
    MVMROOT(tc, str, {
560
154
        ex = (MVMException *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTException);
561
154
    });
562
154
    ex->body.category = MVM_EX_CAT_CATCH;
563
154
    MVM_ASSIGN_REF(tc, &(ex->common.header), ex->body.message, str);
564
154
    MVM_exception_throwobj(tc, MVM_EX_THROW_DYN, (MVMObject *)ex, rr);
565
154
}
566
567
/* Throws the specified exception object, taking the category from it. If
568
 * the handler resumes, the resumption result will be put into resume_result.
569
 * Leaves the interpreter in a state where it will next run the instruction of
570
 * the handler. If there is no handler, it will panic and exit with a backtrace. */
571
195
void MVM_exception_throwobj(MVMThreadContext *tc, MVMuint8 mode, MVMObject *ex_obj, MVMRegister *resume_result) {
572
195
    LocatedHandler  lh;
573
195
    MVMException   *ex;
574
195
575
195
    /* The current frame will be assigned as the thrower of the exception, so
576
195
     * force it onto the heap before we begin (promoting it later would mean
577
195
     * outer handler search result would be outdated). */
578
195
    MVMROOT(tc, ex_obj, {
579
195
        MVM_frame_force_to_heap(tc, tc->cur_frame);
580
195
    });
581
195
582
195
    if (IS_CONCRETE(ex_obj) && REPR(ex_obj)->ID == MVM_REPR_ID_MVMException)
583
195
        ex = (MVMException *)ex_obj;
584
195
    else
585
0
        MVM_exception_throw_adhoc(tc, "Can only throw an exception object");
586
195
587
195
    if (!ex->body.category)
588
9
        ex->body.category = MVM_EX_CAT_CATCH;
589
195
    if (resume_result) {
590
190
        ex->body.resume_addr = *tc->interp_cur_op;
591
190
        /* Ensure that the jit resume label is stored. The throwish
592
190
         * control guard should ensure that the jit entry label point to
593
190
         * a position just after throwing. */
594
190
        ex->body.jit_resume_label = tc->cur_frame->jit_entry_label;
595
190
    }
596
195
    lh = search_for_handler_from(tc, tc->cur_frame, mode, ex->body.category, ex->body.payload);
597
195
    if (lh.frame == NULL) {
598
0
        if (use_lexical_handler_hll_error(tc, mode)) {
599
0
            invoke_lexical_handler_hll_error(tc, ex->body.category, lh);
600
0
            return;
601
0
        }
602
0
        panic_unhandled_ex(tc, ex);
603
0
    }
604
195
605
195
    if (!ex->body.origin) {
606
190
        MVM_ASSIGN_REF(tc, &(ex->common.header), ex->body.origin, tc->cur_frame);
607
190
        tc->cur_frame->throw_address = *(tc->interp_cur_op);
608
190
    }
609
195
610
195
    run_handler(tc, lh, ex_obj, 0, NULL);
611
195
}
612
613
/* Throws an exception of the specified category and with the specified payload.
614
 * If a goto or payload handler exists, then no exception object will be created. */
615
273k
void MVM_exception_throwpayload(MVMThreadContext *tc, MVMuint8 mode, MVMuint32 cat, MVMObject *payload, MVMRegister *resume_result) {
616
273k
    LocatedHandler lh = search_for_handler_from(tc, tc->cur_frame, mode, cat, NULL);
617
273k
    if (lh.frame == NULL) {
618
0
        if (use_lexical_handler_hll_error(tc, mode)) {
619
0
            invoke_lexical_handler_hll_error(tc, cat, lh);
620
0
            return;
621
0
        }
622
0
        panic_unhandled_cat(tc, cat);
623
0
    }
624
273k
    run_handler(tc, lh, NULL, cat, payload);
625
273k
}
626
627
9
void MVM_exception_resume(MVMThreadContext *tc, MVMObject *ex_obj) {
628
9
    MVMException     *ex;
629
9
    MVMFrame         *target;
630
9
    MVMActiveHandler *ah;
631
9
632
9
    if (IS_CONCRETE(ex_obj) && REPR(ex_obj)->ID == MVM_REPR_ID_MVMException)
633
9
        ex = (MVMException *)ex_obj;
634
9
    else
635
0
        MVM_exception_throw_adhoc(tc, "Can only resume an exception object");
636
9
637
9
    /* Check that everything is in place to do the resumption. */
638
9
    if (!ex->body.resume_addr)
639
0
        MVM_exception_throw_adhoc(tc, "This exception is not resumable");
640
9
    target = ex->body.origin;
641
9
    if (!target)
642
0
        MVM_exception_throw_adhoc(tc, "This exception is not resumable");
643
9
    if (target->special_return != unwind_after_handler)
644
0
        MVM_exception_throw_adhoc(tc, "This exception is not resumable");
645
9
    if (!in_caller_chain(tc, target))
646
0
        MVM_exception_throw_adhoc(tc, "Too late to resume this exception");
647
9
648
9
    /* Check that this is the exception we're currently handling. */
649
9
    if (!tc->active_handlers)
650
0
        MVM_exception_throw_adhoc(tc, "Can only resume an exception in its handler");
651
9
    if (tc->active_handlers->ex_obj != ex_obj)
652
0
        MVM_exception_throw_adhoc(tc, "Can only resume the current exception");
653
9
654
9
    /* Clear special return handler; we'll do its work here. */
655
9
    target->special_return = NULL;
656
9
    target->special_unwind = NULL;
657
9
658
9
    /* Clear the current active handler. */
659
9
    ah = tc->active_handlers;
660
9
    tc->active_handlers = ah->next_handler;
661
9
    MVM_free(ah);
662
9
663
9
    /* Unwind to the thrower of the exception; set PC and jit entry label. */
664
9
    target->jit_entry_label = ex->body.jit_resume_label;
665
9
    MVM_frame_unwind_to(tc, target, ex->body.resume_addr, 0, NULL);
666
9
}
667
668
0
static MVMObject* get_lexotic_for_handler_idx(MVMThreadContext *tc, MVMint32 handler_idx) {
669
0
    MVMLexotic *lexotic;
670
0
    MVMStaticFrame  *sf = tc->cur_frame->static_info;
671
0
    /* See if we've got this lexotic cached; return it if so. */
672
0
    if (sf->body.pool_index < tc->lexotic_cache_size) {
673
0
        lexotic = tc->lexotic_cache[sf->body.pool_index];
674
0
        if (lexotic && lexotic->body.handler_idx == handler_idx)
675
0
            return (MVMObject *)lexotic;
676
0
    }
677
0
678
0
    /* Allocate lexotic object, set it up, and cache it. */
679
0
    MVMROOT(tc, sf, {
680
0
        lexotic = (MVMLexotic *)MVM_repr_alloc_init(tc, tc->instance->Lexotic);
681
0
    });
682
0
    lexotic->body.handler_idx = handler_idx;
683
0
    MVM_ASSIGN_REF(tc, &(lexotic->common.header), lexotic->body.sf, sf);
684
0
    if (sf->body.pool_index >= tc->lexotic_cache_size) {
685
0
        MVMuint32 orig_size = tc->lexotic_cache_size;
686
0
        tc->lexotic_cache_size = sf->body.pool_index + 1;
687
0
        tc->lexotic_cache = orig_size
688
0
            ? MVM_realloc(tc->lexotic_cache, tc->lexotic_cache_size * sizeof(MVMLexotic *))
689
0
            : MVM_malloc(tc->lexotic_cache_size * sizeof(MVMLexotic *));
690
0
        memset(tc->lexotic_cache + orig_size, 0,
691
0
            (tc->lexotic_cache_size - orig_size) * sizeof(MVMLexotic *));
692
0
    }
693
0
    if (!tc->lexotic_cache[sf->body.pool_index])
694
0
        tc->lexotic_cache[sf->body.pool_index] = lexotic;
695
0
696
0
    return (MVMObject *)lexotic;
697
0
}
698
699
/* Creates a new lexotic. */
700
0
MVMObject * MVM_exception_newlexotic(MVMThreadContext *tc, MVMuint32 offset) {
701
0
    /* Locate handler associated with the specified label. */
702
0
    MVMFrame       *f     = tc->cur_frame;
703
0
    MVMStaticFrame *sf    = f->static_info;
704
0
    MVMint32 handler_idx  = -1;
705
0
    MVMint32 num_handlers = f->spesh_cand
706
0
        ? f->spesh_cand->num_handlers
707
0
        : sf->body.num_handlers;
708
0
    MVMuint32 i;
709
0
    for (i = 0; i < num_handlers; i++) {
710
0
        if (f->effective_handlers[i].action == MVM_EX_ACTION_GOTO &&
711
0
                f->effective_handlers[i].goto_offset == offset) {
712
0
            handler_idx = i;
713
0
            break;
714
0
        }
715
0
    }
716
0
    if (handler_idx < 0)
717
0
        MVM_exception_throw_adhoc(tc, "Label with no handler passed to newlexotic");
718
0
    return get_lexotic_for_handler_idx(tc, handler_idx);
719
0
}
720
721
/* Creates a new lexotic from the JIT. The JIT doesn't have access to
722
 * the offset, so we can't find it from within there. */
723
0
MVMObject * MVM_exception_newlexotic_from_jit(MVMThreadContext *tc, MVMint32 label) {
724
0
    /* Locate handler associated with the specified label. */
725
0
    MVMFrame       *f       = tc->cur_frame;
726
0
    MVMint32 handler_idx    = -1;
727
0
    MVMint32 num_handlers   = f->spesh_cand->jitcode->num_handlers;
728
0
    MVMJitHandler *handlers = f->spesh_cand->jitcode->handlers;
729
0
    MVMuint32 i;
730
0
    for (i = 0; i < num_handlers; i++) {
731
0
        if (f->effective_handlers[i].action == MVM_EX_ACTION_GOTO &&
732
0
            handlers[i].goto_label == label) {
733
0
            handler_idx = i;
734
0
            break;
735
0
        }
736
0
    }
737
0
    if (handler_idx < 0)
738
0
        MVM_exception_throw_adhoc(tc, "Label with no handler passed to newlexotic");
739
0
    return get_lexotic_for_handler_idx(tc, handler_idx);
740
0
}
741
742
743
/* Unwinds to a lexotic captured handler. */
744
0
void MVM_exception_gotolexotic(MVMThreadContext *tc, MVMint32 handler_idx, MVMStaticFrame *sf) {
745
0
    MVMFrame *f, *search;
746
0
    f = NULL;
747
0
    search = tc->cur_frame;
748
0
    while (search) {
749
0
        f = search;
750
0
        while (f) {
751
0
            if (f->static_info == sf)
752
0
                break;
753
0
            f = f->outer;
754
0
        }
755
0
        if (f)
756
0
            break;
757
0
        search = search->caller;
758
0
    }
759
0
    if (f && in_caller_chain(tc, f)) {
760
0
        LocatedHandler lh;
761
0
        lh.frame = f;
762
0
        lh.handler = &(f->effective_handlers[handler_idx]);
763
0
        if (f->spesh_cand && f->spesh_cand->jitcode)
764
0
            lh.jit_handler = &(f->spesh_cand->jitcode->handlers[handler_idx]);
765
0
        else
766
0
            lh.jit_handler = NULL;
767
0
        run_handler(tc, lh, NULL, MVM_EX_CAT_RETURN, NULL);
768
0
    }
769
0
    else {
770
0
        MVM_exception_throw_adhoc(tc, "Too late to invoke lexotic return");
771
0
    }
772
0
}
773
774
/* Panics and shuts down the VM. Don't do this unless it's something quite
775
 * unrecoverable.
776
 * TODO: Some hook for embedders.
777
 */
778
MVM_NO_RETURN
779
0
void MVM_panic(MVMint32 exitCode, const char *messageFormat, ...) {
780
0
    va_list args;
781
0
    fprintf(stderr, "MoarVM panic: ");
782
0
    va_start(args, messageFormat);
783
0
    vfprintf(stderr, messageFormat, args);
784
0
    va_end(args);
785
0
    fwrite("\n", 1, 1, stderr);
786
0
    if (crash_on_error)
787
0
        abort();
788
0
    else
789
0
        exit(exitCode);
790
0
}
791
792
MVM_NO_RETURN
793
0
void MVM_panic_allocation_failed(size_t len) {
794
0
    MVM_panic(1, "Memory allocation failed; could not allocate %"MVM_PRSz" bytes", len);
795
0
}
796
797
/* A kinder MVM_panic() that doesn't assume our memory is corrupted (but does kill the
798
 * process to indicate that we've made an error */
799
MVM_NO_RETURN
800
0
void MVM_oops(MVMThreadContext *tc, const char *messageFormat, ...) {
801
0
    va_list args;
802
0
    va_start(args, messageFormat);
803
0
    vfprintf(stderr, messageFormat, args);
804
0
    va_end(args);
805
0
    fprintf(stderr, "\n");
806
0
    MVM_dump_backtrace(tc);
807
0
    fprintf(stderr, "\n");
808
0
    exit(1);
809
0
}
810
811
/* Throws an ad-hoc (untyped) exception. */
812
MVM_NO_RETURN
813
94
void MVM_exception_throw_adhoc(MVMThreadContext *tc, const char *messageFormat, ...) {
814
94
    va_list args;
815
94
    va_start(args, messageFormat);
816
94
    MVM_exception_throw_adhoc_free_va(tc, NULL, messageFormat, args);
817
94
    va_end(args);
818
94
}
819
820
/* Throws an ad-hoc (untyped) exception. */
821
MVM_NO_RETURN
822
0
void MVM_exception_throw_adhoc_va(MVMThreadContext *tc, const char *messageFormat, va_list args) {
823
0
    MVM_exception_throw_adhoc_free_va(tc, NULL, messageFormat, args);
824
0
}
825
826
/* Throws an ad-hoc (untyped) exception, taking a NULL-terminated array of
827
 * char pointers to deallocate after message construction. */
828
MVM_NO_RETURN
829
9
void MVM_exception_throw_adhoc_free(MVMThreadContext *tc, char **waste, const char *messageFormat, ...) {
830
9
    va_list args;
831
9
    va_start(args, messageFormat);
832
9
    MVM_exception_throw_adhoc_free_va(tc, waste, messageFormat, args);
833
9
    va_end(args);
834
9
}
835
836
/* Throws an ad-hoc (untyped) exception, taking a NULL-terminated array of
837
 * char pointers to deallocate after message construction. */
838
MVM_NO_RETURN
839
103
void MVM_exception_throw_adhoc_free_va(MVMThreadContext *tc, char **waste, const char *messageFormat, va_list args) {
840
103
    LocatedHandler lh;
841
103
    MVMException *ex;
842
103
843
103
    /* The current frame will be assigned as the thrower of the exception, so
844
103
     * force it onto the heap before we begin. */
845
103
    if (tc->cur_frame)
846
103
        MVM_frame_force_to_heap(tc, tc->cur_frame);
847
103
848
103
    /* Create and set up an exception object. */
849
103
    ex = (MVMException *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTException);
850
103
    MVMROOT(tc, ex, {
851
103
        char      *c_message = MVM_malloc(1024);
852
103
        int        bytes     = vsnprintf(c_message, 1024, messageFormat, args);
853
103
        MVMString *message   = MVM_string_utf8_decode(tc, tc->instance->VMString, c_message, bytes);
854
103
        MVM_free(c_message);
855
103
856
103
        /* Clean up after ourselves to avoid leaking C strings. */
857
103
        if (waste) {
858
103
            while(*waste)
859
103
                MVM_free(*waste++);
860
103
        }
861
103
862
103
        MVM_ASSIGN_REF(tc, &(ex->common.header), ex->body.message, message);
863
103
        if (tc->cur_frame) {
864
103
            ex->body.origin = tc->cur_frame;
865
103
            tc->cur_frame->throw_address = *(tc->interp_cur_op);
866
103
        }
867
103
        else {
868
103
            ex->body.origin = NULL;
869
103
        }
870
103
        ex->body.category = MVM_EX_CAT_CATCH;
871
103
    });
872
103
873
103
    /* Try to locate a handler, so long as we're in the interpreter. */
874
103
    if (tc->interp_cur_op)
875
103
        lh = search_for_handler_from(tc, tc->cur_frame, MVM_EX_THROW_DYN, ex->body.category, NULL);
876
103
    else
877
0
        lh.frame = NULL;
878
103
879
103
    /* Do we have a handler to unwind to? */
880
103
    if (lh.frame == NULL) {
881
0
        /* No handler. Should we crash on these? */
882
0
        if (crash_on_error) {
883
0
            /* Yes, abort. */
884
0
            vfprintf(stderr, messageFormat, args);
885
0
            fwrite("\n", 1, 1, stderr);
886
0
            MVM_dump_backtrace(tc);
887
0
            abort();
888
0
        }
889
0
        else {
890
0
            /* No, just the usual panic. */
891
0
            panic_unhandled_ex(tc, ex);
892
0
        }
893
0
    }
894
103
895
103
    /* Run the handler, which doesn't actually run it but rather sets up the
896
103
     * interpreter so that when we return to it, we'll be at the handler. */
897
103
    run_handler(tc, lh, (MVMObject *)ex, MVM_EX_CAT_CATCH, NULL);
898
103
899
103
    /* Clear any C stack temporaries that code may have pushed before throwing
900
103
     * the exception, and release any needed mutex. */
901
103
    MVM_gc_root_temp_pop_all(tc);
902
103
    MVM_tc_release_ex_release_mutex(tc);
903
103
904
103
    /* Jump back into the interpreter. */
905
103
    longjmp(tc->interp_jump, 1);
906
103
}
907
908
0
void MVM_crash_on_error(void) {
909
0
    crash_on_error = 1;
910
0
}