vm.c 35.3 KB
Newer Older
Damien's avatar
Damien committed
1
2
3
#include <stdio.h>
#include <assert.h>

4
#include "nlr.h"
Damien's avatar
Damien committed
5
#include "misc.h"
6
#include "mpconfig.h"
7
#include "qstr.h"
8
#include "obj.h"
Damien's avatar
Damien committed
9
#include "runtime.h"
10
#include "bc0.h"
11
#include "bc.h"
12
#include "objgenerator.h"
Damien's avatar
Damien committed
13

Paul Sokolovsky's avatar
Paul Sokolovsky committed
14
15
16
17
18
19
20
// Value stack grows up (this makes it incompatible with native C stack, but
// makes sure that arguments to functions are in natural order arg1..argN
// (Python semantics mandates left-to-right evaluation order, including for
// function arguments). Stack pointer is pre-incremented and points at the
// top element.
// Exception stack also grows up, top element is also pointed at.

21
// Exception stack unwind reasons (WHY_* in CPython-speak)
22
23
// TODO perhaps compress this to RETURN=0, JUMP>0, with number of unwinds
// left to do encoded in the JUMP number
24
25
typedef enum {
    UNWIND_RETURN = 1,
26
    UNWIND_JUMP,
27
28
} mp_unwind_reason_t;

29
30
31
32
33
34
#define DECODE_UINT { \
    unum = 0; \
    do { \
        unum = (unum << 7) + (*ip & 0x7f); \
    } while ((*ip++ & 0x80) != 0); \
}
Damien's avatar
Damien committed
35
36
#define DECODE_ULABEL do { unum = (ip[0] | (ip[1] << 8)); ip += 2; } while (0)
#define DECODE_SLABEL do { unum = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2; } while (0)
37
38
39
40
41
42
#define DECODE_QSTR { \
    qst = 0; \
    do { \
        qst = (qst << 7) + (*ip & 0x7f); \
    } while ((*ip++ & 0x80) != 0); \
}
43
44
#define PUSH(val) *++sp = (val)
#define POP() (*sp--)
45
46
#define TOP() (*sp)
#define SET_TOP(val) *sp = (val)
Damien's avatar
Damien committed
47

48
#define PUSH_EXC_BLOCK() \
49
50
51
52
53
    DECODE_ULABEL; /* except labels are always forward */ \
    ++exc_sp; \
    exc_sp->opcode = op; \
    exc_sp->handler = ip + unum; \
    exc_sp->val_sp = MP_TAGPTR_MAKE(sp, currently_in_except_block); \
54
    exc_sp->prev_exc = nlr.ret_val; \
55
56
    currently_in_except_block = 0; /* in a try block now */

57
58
#define POP_EXC_BLOCK() \
    currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); /* restore previous state */ \
59
    if (currently_in_except_block) { nlr.ret_val = exc_sp->prev_exc; } \
60
61
    exc_sp--; /* pop back to previous exception handler */

62
mp_vm_return_kind_t mp_execute_byte_code(const byte *code, const mp_obj_t *args, uint n_args, const mp_obj_t *args2, uint n_args2, mp_obj_t *ret) {
63
64
65
66
67
68
    const byte *ip = code;

    // get code info size, and skip line number table
    machine_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
    ip += code_info_size;

69
70
71
72
    // bytecode prelude: state size and exception stack size; 16 bit uints
    machine_uint_t n_state = ip[0] | (ip[1] << 8);
    machine_uint_t n_exc_stack = ip[2] | (ip[3] << 8);
    ip += 4;
73

74
75
    // allocate state for locals and stack
    mp_obj_t temp_state[10];
76
    mp_obj_t *state = &temp_state[0];
77
    if (n_state > 10) {
78
        state = m_new(mp_obj_t, n_state);
79
    }
80
81
    mp_obj_t *sp = &state[0] - 1;

82
83
84
85
86
87
88
89
    // allocate state for exceptions
    mp_exc_stack exc_state[4];
    mp_exc_stack *exc_stack = &exc_state[0];
    if (n_exc_stack > 4) {
        exc_stack = m_new(mp_exc_stack, n_exc_stack);
    }
    mp_exc_stack *exc_sp = &exc_stack[0] - 1;

90
    // init args
91
    for (uint i = 0; i < n_args; i++) {
92
        state[n_state - 1 - i] = args[i];
93
    }
94
95
96
    for (uint i = 0; i < n_args2; i++) {
        state[n_state - 1 - n_args - i] = args2[i];
    }
97

98
99
100
101
102
103
104
    // bytecode prelude: initialise closed over variables
    for (uint n_local = *ip++; n_local > 0; n_local--) {
        uint local_num = *ip++;
        if (local_num < n_args + n_args2) {
            state[n_state - 1 - local_num] = mp_obj_new_cell(state[n_state - 1 - local_num]);
        } else {
            state[n_state - 1 - local_num] = mp_obj_new_cell(MP_OBJ_NULL);
Damien George's avatar
Damien George committed
105
106
107
108
        }
    }

    // execute the byte code
109
    mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code_2(code, &ip, &state[n_state - 1], &sp, exc_stack, &exc_sp, MP_OBJ_NULL);
110
111
112
113
114
115
116
117
118
119
120
121
122

    switch (vm_return_kind) {
        case MP_VM_RETURN_NORMAL:
            *ret = *sp;
            return MP_VM_RETURN_NORMAL;
        case MP_VM_RETURN_EXCEPTION:
            *ret = state[n_state - 1];
            return MP_VM_RETURN_EXCEPTION;
        case MP_VM_RETURN_YIELD: // byte-code shouldn't yield
        default:
            assert(0);
            *ret = mp_const_none;
            return MP_VM_RETURN_NORMAL;
123
124
125
    }
}

126
127
// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc)
// sp points to bottom of stack which grows up
128
129
130
131
// returns:
//  MP_VM_RETURN_NORMAL, sp valid, return value in *sp
//  MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp
//  MP_VM_RETURN_EXCEPTION, exception in fastn[0]
132
133
mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out,
                                           mp_obj_t *fastn, mp_obj_t **sp_in_out,
134
135
                                           mp_exc_stack *exc_stack, mp_exc_stack **exc_sp_in_out,
                                           volatile mp_obj_t inject_exc) {
Damien's avatar
Damien committed
136
137
    // careful: be sure to declare volatile any variables read in the exception handler (written is ok, I think)

138
    const byte *ip = *ip_in_out;
139
    mp_obj_t *sp = *sp_in_out;
Damien's avatar
Damien committed
140
    machine_uint_t unum;
141
    qstr qst;
142
    mp_obj_t obj1, obj2;
143
    nlr_buf_t nlr;
Damien's avatar
Damien committed
144

145
146
    volatile bool currently_in_except_block = MP_TAGPTR_TAG(*exc_sp_in_out); // 0 or 1, to detect nested exceptions
    mp_exc_stack *volatile exc_sp = MP_TAGPTR_PTR(*exc_sp_in_out); // stack grows up, exc_sp points to top of stack
147
    const byte *volatile save_ip = ip; // this is so we can access ip in the exception handler without making ip volatile (which means the compiler can't keep it in a register in the main loop)
Damien's avatar
Damien committed
148

149
    // outer exception handling loop
Damien's avatar
Damien committed
150
    for (;;) {
151
outer_dispatch_loop:
152
        if (nlr_push(&nlr) == 0) {
153
154
155
            // If we have exception to inject, now that we finish setting up
            // execution context, raise it. This works as if RAISE_VARARGS
            // bytecode was executed.
156
157
158
            // Injecting exc into yield from generator is a special case,
            // handled by MP_BC_YIELD_FROM itself
            if (inject_exc != MP_OBJ_NULL && *ip != MP_BC_YIELD_FROM) {
159
160
161
162
                mp_obj_t t = inject_exc;
                inject_exc = MP_OBJ_NULL;
                nlr_jump(rt_make_raise_obj(t));
            }
163
164
            // loop to execute byte code
            for (;;) {
165
dispatch_loop:
166
                save_ip = ip;
167
168
                int op = *ip++;
                switch (op) {
169
170
                    case MP_BC_LOAD_CONST_FALSE:
                        PUSH(mp_const_false);
171
172
                        break;

173
174
                    case MP_BC_LOAD_CONST_NONE:
                        PUSH(mp_const_none);
175
176
                        break;

177
178
                    case MP_BC_LOAD_CONST_TRUE:
                        PUSH(mp_const_true);
179
                        break;
Damien George's avatar
Damien George committed
180
181

                    case MP_BC_LOAD_CONST_ELLIPSIS:
182
                        PUSH((mp_obj_t)&mp_const_ellipsis_obj);
Damien George's avatar
Damien George committed
183
                        break;
184

185
                    case MP_BC_LOAD_CONST_SMALL_INT: {
186
                        machine_int_t num = 0;
187
188
189
190
191
192
193
194
                        if ((ip[0] & 0x40) != 0) {
                            // Number is negative
                            num--;
                        }
                        do {
                            num = (num << 7) | (*ip & 0x7f);
                        } while ((*ip++ & 0x80) != 0);
                        PUSH(MP_OBJ_NEW_SMALL_INT(num));
195
                        break;
196
                    }
197

198
199
                    case MP_BC_LOAD_CONST_INT:
                        DECODE_QSTR;
200
                        PUSH(mp_obj_new_int_from_long_str(qstr_str(qst)));
201
202
                        break;

203
                    case MP_BC_LOAD_CONST_DEC:
Damien's avatar
Damien committed
204
                        DECODE_QSTR;
205
                        PUSH(rt_load_const_dec(qst));
Damien's avatar
Damien committed
206
207
                        break;

208
                    case MP_BC_LOAD_CONST_ID:
209
                        DECODE_QSTR;
210
                        PUSH(rt_load_const_str(qst)); // TODO
211
212
                        break;

213
214
                    case MP_BC_LOAD_CONST_BYTES:
                        DECODE_QSTR;
215
                        PUSH(rt_load_const_bytes(qst));
216
217
                        break;

218
                    case MP_BC_LOAD_CONST_STRING:
219
                        DECODE_QSTR;
220
                        PUSH(rt_load_const_str(qst));
221
222
                        break;

223
                    case MP_BC_LOAD_FAST_0:
224
                        PUSH(fastn[0]);
225
226
                        break;

227
                    case MP_BC_LOAD_FAST_1:
228
                        PUSH(fastn[-1]);
229
230
                        break;

231
                    case MP_BC_LOAD_FAST_2:
232
                        PUSH(fastn[-2]);
233
234
                        break;

235
                    case MP_BC_LOAD_FAST_N:
236
                        DECODE_UINT;
237
                        PUSH(fastn[-unum]);
238
239
                        break;

240
                    case MP_BC_LOAD_DEREF:
Damien's avatar
Damien committed
241
                        DECODE_UINT;
242
                        PUSH(rt_get_cell(fastn[-unum]));
Damien's avatar
Damien committed
243
244
                        break;

245
                    case MP_BC_LOAD_NAME:
246
                        DECODE_QSTR;
247
                        PUSH(rt_load_name(qst));
248
249
                        break;

250
                    case MP_BC_LOAD_GLOBAL:
251
                        DECODE_QSTR;
252
                        PUSH(rt_load_global(qst));
253
254
                        break;

255
                    case MP_BC_LOAD_ATTR:
256
                        DECODE_QSTR;
257
                        SET_TOP(rt_load_attr(TOP(), qst));
258
259
                        break;

260
                    case MP_BC_LOAD_METHOD:
261
                        DECODE_QSTR;
262
                        rt_load_method(*sp, qst, sp);
263
                        sp += 1;
264
265
                        break;

266
                    case MP_BC_LOAD_BUILD_CLASS:
267
268
269
                        PUSH(rt_load_build_class());
                        break;

270
                    case MP_BC_STORE_FAST_0:
271
                        fastn[0] = POP();
272
273
                        break;

274
                    case MP_BC_STORE_FAST_1:
275
                        fastn[-1] = POP();
276
277
                        break;

278
                    case MP_BC_STORE_FAST_2:
279
                        fastn[-2] = POP();
280
281
                        break;

282
                    case MP_BC_STORE_FAST_N:
283
                        DECODE_UINT;
284
                        fastn[-unum] = POP();
285
286
                        break;

287
                    case MP_BC_STORE_DEREF:
Damien's avatar
Damien committed
288
                        DECODE_UINT;
289
                        rt_set_cell(fastn[-unum], POP());
Damien's avatar
Damien committed
290
291
                        break;

292
                    case MP_BC_STORE_NAME:
293
                        DECODE_QSTR;
294
                        rt_store_name(qst, POP());
295
296
                        break;

297
                    case MP_BC_STORE_GLOBAL:
298
                        DECODE_QSTR;
299
                        rt_store_global(qst, POP());
300
301
                        break;

302
                    case MP_BC_STORE_ATTR:
303
                        DECODE_QSTR;
304
                        rt_store_attr(sp[0], qst, sp[-1]);
305
                        sp -= 2;
306
307
                        break;

308
                    case MP_BC_STORE_SUBSCR:
309
310
                        rt_store_subscr(sp[-1], sp[0], sp[-2]);
                        sp -= 3;
311
312
                        break;

313
314
315
316
317
                    case MP_BC_DELETE_NAME:
                        DECODE_QSTR;
                        rt_delete_name(qst);
                        break;

318
                    case MP_BC_DUP_TOP:
319
                        obj1 = TOP();
320
321
322
                        PUSH(obj1);
                        break;

323
                    case MP_BC_DUP_TOP_TWO:
324
325
326
                        sp += 2;
                        sp[0] = sp[-2];
                        sp[-1] = sp[-3];
327
328
                        break;

329
                    case MP_BC_POP_TOP:
330
                        sp -= 1;
331
332
                        break;

333
                    case MP_BC_ROT_TWO:
334
                        obj1 = sp[0];
335
336
                        sp[0] = sp[-1];
                        sp[-1] = obj1;
337
338
                        break;

339
                    case MP_BC_ROT_THREE:
340
                        obj1 = sp[0];
341
342
343
                        sp[0] = sp[-1];
                        sp[-1] = sp[-2];
                        sp[-2] = obj1;
344
345
                        break;

346
                    case MP_BC_JUMP:
Damien's avatar
Damien committed
347
348
                        DECODE_SLABEL;
                        ip += unum;
349
350
                        break;

351
                    case MP_BC_POP_JUMP_IF_TRUE:
Damien's avatar
Damien committed
352
                        DECODE_SLABEL;
353
                        if (rt_is_true(POP())) {
Damien's avatar
Damien committed
354
                            ip += unum;
355
356
357
                        }
                        break;

358
                    case MP_BC_POP_JUMP_IF_FALSE:
Damien's avatar
Damien committed
359
                        DECODE_SLABEL;
360
                        if (!rt_is_true(POP())) {
Damien's avatar
Damien committed
361
                            ip += unum;
362
363
364
                        }
                        break;

365
                    case MP_BC_JUMP_IF_TRUE_OR_POP:
Damien's avatar
Damien committed
366
                        DECODE_SLABEL;
367
                        if (rt_is_true(TOP())) {
Damien's avatar
Damien committed
368
369
                            ip += unum;
                        } else {
370
                            sp--;
Damien's avatar
Damien committed
371
372
373
                        }
                        break;

374
                    case MP_BC_JUMP_IF_FALSE_OR_POP:
Damien's avatar
Damien committed
375
                        DECODE_SLABEL;
376
                        if (rt_is_true(TOP())) {
377
                            sp--;
Damien's avatar
Damien committed
378
379
380
381
382
                        } else {
                            ip += unum;
                        }
                        break;

383
                        /* we are trying to get away without using this opcode
384
                    case MP_BC_SETUP_LOOP:
385
                        DECODE_UINT;
386
                        // push_block(MP_BC_SETUP_LOOP, ip + unum, sp)
387
388
389
                        break;
                        */

390
                    case MP_BC_SETUP_WITH:
391
392
                        obj1 = TOP();
                        SET_TOP(rt_load_attr(obj1, MP_QSTR___exit__));
393
394
                        rt_load_method(obj1, MP_QSTR___enter__, sp + 1);
                        obj2 = rt_call_method_n_kw(0, 0, sp + 1);
395
                        PUSH_EXC_BLOCK();
396
397
398
399
                        PUSH(obj2);
                        break;

                    case MP_BC_WITH_CLEANUP: {
400
401
402
403
                        // Arriving here, there's "exception control block" on top of stack,
                        // and __exit__ bound method underneath it. Bytecode calls __exit__,
                        // and "deletes" it off stack, shifting "exception control block"
                        // to its place.
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
                        static const mp_obj_t no_exc[] = {mp_const_none, mp_const_none, mp_const_none};
                        if (TOP() == mp_const_none) {
                            sp--;
                            obj1 = TOP();
                            SET_TOP(mp_const_none);
                            obj2 = rt_call_function_n_kw(obj1, 3, 0, no_exc);
                        } else if (MP_OBJ_IS_SMALL_INT(TOP())) {
                            mp_obj_t cause = POP();
                            switch (MP_OBJ_SMALL_INT_VALUE(cause)) {
                                case UNWIND_RETURN: {
                                    mp_obj_t retval = POP();
                                    obj2 = rt_call_function_n_kw(TOP(), 3, 0, no_exc);
                                    SET_TOP(retval);
                                    PUSH(cause);
                                    break;
                                }
                                case UNWIND_JUMP: {
                                    obj2 = rt_call_function_n_kw(sp[-2], 3, 0, no_exc);
                                    // Pop __exit__ boundmethod at sp[-2]
                                    sp[-2] = sp[-1];
                                    sp[-1] = sp[0];
                                    SET_TOP(cause);
                                    break;
                                }
                                default:
                                    assert(0);
                            }
                        } else if (mp_obj_is_exception_type(TOP())) {
                            mp_obj_t args[3] = {sp[0], sp[-1], sp[-2]};
                            obj2 = rt_call_function_n_kw(sp[-3], 3, 0, args);
                            // Pop __exit__ boundmethod at sp[-3]
                            // TODO: Once semantics is proven, optimize for case when obj2 == True
                            sp[-3] = sp[-2];
                            sp[-2] = sp[-1];
                            sp[-1] = sp[0];
                            sp--;
                            if (rt_is_true(obj2)) {
                                // This is what CPython does
                                //PUSH(MP_OBJ_NEW_SMALL_INT(UNWIND_SILENCED));
                                // But what we need to do is - pop exception from value stack...
                                sp -= 3;
445
446
                                // ... pop "with" exception handler, and signal END_FINALLY
                                // to just execute finally handler normally (by pushing None
447
448
449
                                // on value stack)
                                assert(exc_sp >= exc_stack);
                                assert(exc_sp->opcode == MP_BC_SETUP_WITH);
450
                                POP_EXC_BLOCK();
451
452
453
454
455
456
457
458
                                PUSH(mp_const_none);
                            }
                        } else {
                            assert(0);
                        }
                        break;
                    }

459
460
461
462
463
464
465
466
467
                    case MP_BC_UNWIND_JUMP:
                        DECODE_SLABEL;
                        PUSH((void*)(ip + unum)); // push destination ip for jump
                        PUSH((void*)(machine_uint_t)(*ip)); // push number of exception handlers to unwind
unwind_jump:
                        unum = (machine_uint_t)POP(); // get number of exception handlers to unwind
                        while (unum > 0) {
                            unum -= 1;
                            assert(exc_sp >= exc_stack);
468
                            if (exc_sp->opcode == MP_BC_SETUP_FINALLY || exc_sp->opcode == MP_BC_SETUP_WITH) {
469
470
471
472
473
474
475
476
477
478
479
480
481
                                // We're going to run "finally" code as a coroutine
                                // (not calling it recursively). Set up a sentinel
                                // on a stack so it can return back to us when it is
                                // done (when END_FINALLY reached).
                                PUSH((void*)unum); // push number of exception handlers left to unwind
                                PUSH(MP_OBJ_NEW_SMALL_INT(UNWIND_JUMP)); // push sentinel
                                ip = exc_sp->handler; // get exception handler byte code address
                                exc_sp--; // pop exception handler
                                goto dispatch_loop; // run the exception handler
                            }
                            exc_sp--;
                        }
                        ip = (const byte*)POP(); // pop destination ip for jump
482
483
                        break;

Damien's avatar
Damien committed
484
                    // matched against: POP_BLOCK or POP_EXCEPT (anything else?)
485
                    case MP_BC_SETUP_EXCEPT:
486
                    case MP_BC_SETUP_FINALLY:
487
                        PUSH_EXC_BLOCK();
488
489
                        break;

490
                    case MP_BC_END_FINALLY:
491
                        // not fully implemented
492
493
                        // if TOS is an exception, reraises the exception (3 values on TOS)
                        // if TOS is None, just pops it and continues
494
                        // if TOS is an integer, does something else
495
                        // else error
496
497
                        if (mp_obj_is_exception_type(TOP())) {
                            nlr_jump(sp[-1]);
498
499
500
                        }
                        if (TOP() == mp_const_none) {
                            sp--;
501
502
503
504
505
506
507
                        } else if (MP_OBJ_IS_SMALL_INT(TOP())) {
                            // We finished "finally" coroutine and now dispatch back
                            // to our caller, based on TOS value
                            mp_unwind_reason_t reason = MP_OBJ_SMALL_INT_VALUE(POP());
                            switch (reason) {
                                case UNWIND_RETURN:
                                    goto unwind_return;
508
509
                                case UNWIND_JUMP:
                                    goto unwind_jump;
510
511
                            }
                            assert(0);
512
513
514
                        } else {
                            assert(0);
                        }
515
516
                        break;

517
                    case MP_BC_GET_ITER:
518
                        SET_TOP(rt_getiter(TOP()));
519
520
                        break;

521
                    case MP_BC_FOR_ITER:
Damien's avatar
Damien committed
522
                        DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
523
524
                        obj1 = rt_iternext_allow_raise(TOP());
                        if (obj1 == MP_OBJ_NULL) {
525
                            --sp; // pop the exhausted iterator
Damien's avatar
Damien committed
526
                            ip += unum; // jump to after for-block
527
528
529
530
531
                        } else {
                            PUSH(obj1); // push the next iteration value
                        }
                        break;

Damien's avatar
Damien committed
532
                    // matched against: SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH
533
                    case MP_BC_POP_BLOCK:
Damien's avatar
Damien committed
534
                        // we are exiting an exception handler, so pop the last one of the exception-stack
535
                        assert(exc_sp >= exc_stack);
536
                        POP_EXC_BLOCK();
537
538
                        break;

539
                    // matched against: SETUP_EXCEPT
540
                    case MP_BC_POP_EXCEPT:
Damien's avatar
Damien committed
541
                        // TODO need to work out how blocks work etc
542
                        // pops block, checks it's an exception block, and restores the stack, saving the 3 exception values to local threadstate
543
                        assert(exc_sp >= exc_stack);
544
                        assert(currently_in_except_block);
545
                        //sp = (mp_obj_t*)(*exc_sp--);
Damien's avatar
Damien committed
546
                        //exc_sp--; // discard ip
547
                        POP_EXC_BLOCK();
548
                        //sp -= 3; // pop 3 exception values
549
550
                        break;

551
552
553
554
555
556
557
558
                    case MP_BC_NOT:
                        if (TOP() == mp_const_true) {
                            SET_TOP(mp_const_false);
                        } else {
                            SET_TOP(mp_const_true);
                        }
                        break;

559
                    case MP_BC_UNARY_OP:
Damien's avatar
Damien committed
560
                        unum = *ip++;
561
                        SET_TOP(rt_unary_op(unum, TOP()));
Damien's avatar
Damien committed
562
563
                        break;

564
                    case MP_BC_BINARY_OP:
565
566
                        unum = *ip++;
                        obj2 = POP();
567
568
                        obj1 = TOP();
                        SET_TOP(rt_binary_op(unum, obj1, obj2));
569
570
                        break;

571
                    case MP_BC_BUILD_TUPLE:
572
                        DECODE_UINT;
573
574
                        sp -= unum - 1;
                        SET_TOP(rt_build_tuple(unum, sp));
575
576
                        break;

577
                    case MP_BC_BUILD_LIST:
578
                        DECODE_UINT;
579
580
                        sp -= unum - 1;
                        SET_TOP(rt_build_list(unum, sp));
581
582
                        break;

583
                    case MP_BC_LIST_APPEND:
584
585
                        DECODE_UINT;
                        // I think it's guaranteed by the compiler that sp[unum] is a list
586
587
                        rt_list_append(sp[-unum], sp[0]);
                        sp--;
588
589
                        break;

590
                    case MP_BC_BUILD_MAP:
591
592
593
594
                        DECODE_UINT;
                        PUSH(rt_build_map(unum));
                        break;

595
                    case MP_BC_STORE_MAP:
596
597
                        sp -= 2;
                        rt_store_map(sp[0], sp[2], sp[1]);
598
599
                        break;

600
                    case MP_BC_MAP_ADD:
Damien's avatar
Damien committed
601
                        DECODE_UINT;
602
603
604
                        // I think it's guaranteed by the compiler that sp[-unum - 1] is a map
                        rt_store_map(sp[-unum - 1], sp[0], sp[-1]);
                        sp -= 2;
Damien's avatar
Damien committed
605
606
                        break;

607
                    case MP_BC_BUILD_SET:
608
                        DECODE_UINT;
609
610
                        sp -= unum - 1;
                        SET_TOP(rt_build_set(unum, sp));
611
612
                        break;

613
                    case MP_BC_SET_ADD:
Damien's avatar
Damien committed
614
                        DECODE_UINT;
615
616
617
                        // I think it's guaranteed by the compiler that sp[-unum] is a set
                        rt_store_set(sp[-unum], sp[0]);
                        sp--;
Damien's avatar
Damien committed
618
619
                        break;

620
#if MICROPY_ENABLE_SLICE
621
622
623
624
625
626
627
628
629
630
631
                    case MP_BC_BUILD_SLICE:
                        DECODE_UINT;
                        if (unum == 2) {
                            obj2 = POP();
                            obj1 = TOP();
                            SET_TOP(mp_obj_new_slice(obj1, obj2, NULL));
                        } else {
                            printf("3-argument slice is not supported\n");
                            assert(0);
                        }
                        break;
632
#endif
633

634
                    case MP_BC_UNPACK_SEQUENCE:
635
                        DECODE_UINT;
636
                        rt_unpack_sequence(sp[0], unum, sp);
637
                        sp += unum - 1;
638
639
                        break;

640
                    case MP_BC_MAKE_FUNCTION:
641
                        DECODE_UINT;
642
                        PUSH(rt_make_function_from_id(unum, false, MP_OBJ_NULL));
643
644
645
646
                        break;

                    case MP_BC_MAKE_FUNCTION_DEFARGS:
                        DECODE_UINT;
647
                        SET_TOP(rt_make_function_from_id(unum, false, TOP()));
648
649
                        break;

650
                    case MP_BC_MAKE_CLOSURE:
Damien's avatar
Damien committed
651
                        DECODE_UINT;
652
653
654
655
656
657
658
                        SET_TOP(rt_make_closure_from_id(unum, TOP(), MP_OBJ_NULL));
                        break;

                    case MP_BC_MAKE_CLOSURE_DEFARGS:
                        DECODE_UINT;
                        obj1 = POP();
                        SET_TOP(rt_make_closure_from_id(unum, obj1, TOP()));
Damien's avatar
Damien committed
659
660
                        break;

661
                    case MP_BC_CALL_FUNCTION:
662
                        DECODE_UINT;
663
664
665
666
                        // unum & 0xff == n_positional
                        // (unum >> 8) & 0xff == n_keyword
                        sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe);
                        SET_TOP(rt_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1));
667
668
                        break;

669
                    case MP_BC_CALL_METHOD:
670
                        DECODE_UINT;
671
672
673
674
                        // unum & 0xff == n_positional
                        // (unum >> 8) & 0xff == n_keyword
                        sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 1;
                        SET_TOP(rt_call_method_n_kw(unum & 0xff, (unum >> 8) & 0xff, sp));
675
676
                        break;

677
                    case MP_BC_RETURN_VALUE:
678
679
unwind_return:
                        while (exc_sp >= exc_stack) {
680
                            if (exc_sp->opcode == MP_BC_SETUP_FINALLY || exc_sp->opcode == MP_BC_SETUP_WITH) {
681
682
683
684
685
686
687
688
689
690
691
692
693
694
                                // We're going to run "finally" code as a coroutine
                                // (not calling it recursively). Set up a sentinel
                                // on a stack so it can return back to us when it is
                                // done (when END_FINALLY reached).
                                PUSH(MP_OBJ_NEW_SMALL_INT(UNWIND_RETURN));
                                ip = exc_sp->handler;
                                // We don't need to do anything with sp, finally is just
                                // syntactic sugar for sequential execution??
                                // sp =
                                exc_sp--;
                                goto dispatch_loop;
                            }
                            exc_sp--;
                        }
695
                        nlr_pop();
696
                        *sp_in_out = sp;
697
                        assert(exc_sp == exc_stack - 1);
698
                        return MP_VM_RETURN_NORMAL;
699

700
701
                    case MP_BC_RAISE_VARARGS:
                        unum = *ip++;
702
703
                        assert(unum <= 1);
                        if (unum == 0) {
704
705
706
                            if (!currently_in_except_block) {
                                nlr_jump(mp_obj_new_exception_msg(&mp_type_RuntimeError, "No active exception to reraise"));
                            }
707
708
709
710
711
712
                            // This assumes that nlr.ret_val holds last raised
                            // exception and is not overwritten since then.
                            obj1 = nlr.ret_val;
                        } else {
                            obj1 = POP();
                        }
713
                        nlr_jump(rt_make_raise_obj(obj1));
714

715
                    case MP_BC_YIELD_VALUE:
716
yield:
717
718
719
                        nlr_pop();
                        *ip_in_out = ip;
                        *sp_in_out = sp;
720
                        *exc_sp_in_out = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block);
721
                        return MP_VM_RETURN_YIELD;
722

723
                    case MP_BC_YIELD_FROM: {
724
725
//#define EXC_MATCH(exc, type) MP_OBJ_IS_TYPE(exc, type)
#define EXC_MATCH(exc, type) mp_obj_exception_match(exc, type)
726
#define GENERATOR_EXIT_IF_NEEDED(t) if (t != MP_OBJ_NULL && EXC_MATCH(t, &mp_type_GeneratorExit)) { nlr_jump(t); }
727
728
                        mp_vm_return_kind_t ret_kind;
                        obj1 = POP();
729
                        mp_obj_t t_exc = MP_OBJ_NULL;
730
                        if (inject_exc != MP_OBJ_NULL) {
731
                            t_exc = inject_exc;
732
                            inject_exc = MP_OBJ_NULL;
733
                            ret_kind = mp_obj_gen_resume(TOP(), mp_const_none, t_exc, &obj2);
734
                        } else {
735
                            ret_kind = mp_obj_gen_resume(TOP(), obj1, MP_OBJ_NULL, &obj2);
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
                        }

                        if (ret_kind == MP_VM_RETURN_YIELD) {
                            ip--;
                            PUSH(obj2);
                            goto yield;
                        }
                        if (ret_kind == MP_VM_RETURN_NORMAL) {
                            // Pop exhausted gen
                            sp--;
                            if (obj2 == MP_OBJ_NULL) {
                                // Optimize StopIteration
                                // TODO: get StopIteration's value
                                PUSH(mp_const_none);
                            } else {
                                PUSH(obj2);
                            }

754
755
756
                            // If we injected GeneratorExit downstream, then even
                            // if it was swallowed, we re-raise GeneratorExit
                            GENERATOR_EXIT_IF_NEEDED(t_exc);
757
758
759
760
761
762
                            break;
                        }
                        if (ret_kind == MP_VM_RETURN_EXCEPTION) {
                            // Pop exhausted gen
                            sp--;
                            if (EXC_MATCH(obj2, &mp_type_StopIteration)) {
763
764
765
766
                                PUSH(mp_obj_exception_get_value(obj2));
                                // If we injected GeneratorExit downstream, then even
                                // if it was swallowed, we re-raise GeneratorExit
                                GENERATOR_EXIT_IF_NEEDED(t_exc);
767
768
769
770
771
772
773
                                break;
                            } else {
                                nlr_jump(obj2);
                            }
                        }
                    }

774
                    case MP_BC_IMPORT_NAME:
775
776
                        DECODE_QSTR;
                        obj1 = POP();
777
                        SET_TOP(rt_import_name(qst, obj1, TOP()));
778
779
                        break;

780
                    case MP_BC_IMPORT_FROM:
781
                        DECODE_QSTR;
782
                        obj1 = rt_import_from(TOP(), qst);
783
784
785
                        PUSH(obj1);
                        break;

786
                    case MP_BC_IMPORT_STAR:
787
                        rt_import_all(POP());
788
789
                        break;

790
                    default:
Damien's avatar
Damien committed
791
                        printf("code %p, byte code 0x%02x not implemented\n", ip, op);
792
793
                        assert(0);
                        nlr_pop();
794
                        return MP_VM_RETURN_NORMAL;
Damien's avatar
Damien committed
795
                }
796
797
798
799
800
            }

        } else {
            // exception occurred

801
802
803
804
805
806
807
808
809
            // check if it's a StopIteration within a for block
            if (*save_ip == MP_BC_FOR_ITER && mp_obj_is_subclass_fast(mp_obj_get_type(nlr.ret_val), &mp_type_StopIteration)) {
                ip = save_ip + 1;
                DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
                --sp; // pop the exhausted iterator
                ip += unum; // jump to after for-block
                goto outer_dispatch_loop; // continue with dispatch loop
            }

810
            // set file and line number that the exception occurred at
811
812
            // TODO: don't set traceback for exceptions re-raised by END_FINALLY.
            // But consider how to handle nested exceptions.
813
814
            // TODO need a better way of not adding traceback to constant objects (right now, just GeneratorExit_obj)
            if (mp_obj_is_exception_instance(nlr.ret_val) && nlr.ret_val != &mp_const_GeneratorExit_obj) {
815
                machine_uint_t code_info_size = code_info[0] | (code_info[1] << 8) | (code_info[2] << 16) | (code_info[3] << 24);
816
817
                qstr source_file = code_info[4] | (code_info[5] << 8) | (code_info[6] << 16) | (code_info[7] << 24);
                qstr block_name = code_info[8] | (code_info[9] << 8) | (code_info[10] << 16) | (code_info[11] << 24);
818
819
                machine_uint_t source_line = 1;
                machine_uint_t bc = save_ip - code_info - code_info_size;
820
                //printf("find %lu %d %d\n", bc, code_info[12], code_info[13]);
821
822
823
                for (const byte* ci = code_info + 12; *ci && bc >= ((*ci) & 31); ci++) {
                    bc -= *ci & 31;
                    source_line += *ci >> 5;
824
                }
825
                mp_obj_exception_add_traceback(nlr.ret_val, source_file, source_line, block_name);
826
827
            }

828
829
830
            while (currently_in_except_block) {
                // nested exception

831
                assert(exc_sp >= exc_stack);
832
833
834
835
836

                // TODO make a proper message for nested exception
                // at the moment we are just raising the very last exception (the one that caused the nested exception)

                // move up to previous exception handler
837
                POP_EXC_BLOCK();
838
839
            }

840
            if (exc_sp >= exc_stack) {
841
842
843
                // set flag to indicate that we are now handling an exception
                currently_in_except_block = 1;

844
                // catch exception and pass to byte code
845
                sp = MP_TAGPTR_PTR(exc_sp->val_sp);
846
                ip = exc_sp->handler;
Damien's avatar
Damien committed
847
                // push(traceback, exc-val, exc-type)
848
                PUSH(mp_const_none);
Damien's avatar
Damien committed
849
                PUSH(nlr.ret_val);
850
                PUSH(mp_obj_get_type(nlr.ret_val));
851

852
            } else {
853
854
855
856
                // propagate exception to higher level
                // TODO what to do about ip and sp? they don't really make sense at this point
                fastn[0] = nlr.ret_val; // must put exception here because sp is invalid
                return MP_VM_RETURN_EXCEPTION;
857
            }
Damien's avatar
Damien committed
858
859
860
        }
    }
}