vm.c 35.2 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
54
55
    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); \
    currently_in_except_block = 0; /* in a try block now */

56
57
58
59
#define POP_EXC_BLOCK() \
    currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); /* restore previous state */ \
    exc_sp--; /* pop back to previous exception handler */

60
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) {
61
62
63
64
65
66
    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;

67
68
69
70
    // 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;
71

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

80
81
82
83
84
85
86
87
    // 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;

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

96
97
98
99
100
101
102
    // 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
103
104
105
106
        }
    }

    // execute the byte code
107
    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);
108
109
110
111
112
113
114
115
116
117
118
119
120

    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;
121
122
123
    }
}

124
125
// 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
126
127
128
129
// 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]
130
131
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,
132
133
                                           mp_exc_stack *exc_stack, mp_exc_stack **exc_sp_in_out,
                                           volatile mp_obj_t inject_exc) {
Damien's avatar
Damien committed
134
135
    // careful: be sure to declare volatile any variables read in the exception handler (written is ok, I think)

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

143
144
    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
145
    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
146

147
    // outer exception handling loop
Damien's avatar
Damien committed
148
    for (;;) {
149
outer_dispatch_loop:
150
        if (nlr_push(&nlr) == 0) {
151
152
153
            // 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.
154
155
156
            // 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) {
157
158
159
160
                mp_obj_t t = inject_exc;
                inject_exc = MP_OBJ_NULL;
                nlr_jump(rt_make_raise_obj(t));
            }
161
162
            // loop to execute byte code
            for (;;) {
163
dispatch_loop:
164
                save_ip = ip;
165
166
                int op = *ip++;
                switch (op) {
167
168
                    case MP_BC_LOAD_CONST_FALSE:
                        PUSH(mp_const_false);
169
170
                        break;

171
172
                    case MP_BC_LOAD_CONST_NONE:
                        PUSH(mp_const_none);
173
174
                        break;

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

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

183
                    case MP_BC_LOAD_CONST_SMALL_INT: {
184
                        machine_int_t num = 0;
185
186
187
188
189
190
191
192
                        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));
193
                        break;
194
                    }
195

196
197
                    case MP_BC_LOAD_CONST_INT:
                        DECODE_QSTR;
198
                        PUSH(mp_obj_new_int_from_long_str(qstr_str(qst)));
199
200
                        break;

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

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

211
212
                    case MP_BC_LOAD_CONST_BYTES:
                        DECODE_QSTR;
213
                        PUSH(rt_load_const_bytes(qst));
214
215
                        break;

216
                    case MP_BC_LOAD_CONST_STRING:
217
                        DECODE_QSTR;
218
                        PUSH(rt_load_const_str(qst));
219
220
                        break;

221
                    case MP_BC_LOAD_FAST_0:
222
                        PUSH(fastn[0]);
223
224
                        break;

225
                    case MP_BC_LOAD_FAST_1:
226
                        PUSH(fastn[-1]);
227
228
                        break;

229
                    case MP_BC_LOAD_FAST_2:
230
                        PUSH(fastn[-2]);
231
232
                        break;

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

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

243
                    case MP_BC_LOAD_NAME:
244
                        DECODE_QSTR;
245
                        PUSH(rt_load_name(qst));
246
247
                        break;

248
                    case MP_BC_LOAD_GLOBAL:
249
                        DECODE_QSTR;
250
                        PUSH(rt_load_global(qst));
251
252
                        break;

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

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

264
                    case MP_BC_LOAD_BUILD_CLASS:
265
266
267
                        PUSH(rt_load_build_class());
                        break;

268
                    case MP_BC_STORE_FAST_0:
269
                        fastn[0] = POP();
270
271
                        break;

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

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

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

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

290
                    case MP_BC_STORE_NAME:
291
                        DECODE_QSTR;
292
                        rt_store_name(qst, POP());
293
294
                        break;

295
                    case MP_BC_STORE_GLOBAL:
296
                        DECODE_QSTR;
297
                        rt_store_global(qst, POP());
298
299
                        break;

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

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

311
312
313
314
315
                    case MP_BC_DELETE_NAME:
                        DECODE_QSTR;
                        rt_delete_name(qst);
                        break;

316
                    case MP_BC_DUP_TOP:
317
                        obj1 = TOP();
318
319
320
                        PUSH(obj1);
                        break;

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

327
                    case MP_BC_POP_TOP:
328
                        sp -= 1;
329
330
                        break;

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

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

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

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

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

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

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

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

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

                    case MP_BC_WITH_CLEANUP: {
398
399
400
401
                        // 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.
402
403
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
                        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;
443
444
                                // ... pop "with" exception handler, and signal END_FINALLY
                                // to just execute finally handler normally (by pushing None
445
446
447
                                // on value stack)
                                assert(exc_sp >= exc_stack);
                                assert(exc_sp->opcode == MP_BC_SETUP_WITH);
448
                                POP_EXC_BLOCK();
449
450
451
452
453
454
455
456
                                PUSH(mp_const_none);
                            }
                        } else {
                            assert(0);
                        }
                        break;
                    }

457
458
459
460
461
462
463
464
465
                    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);
466
                            if (exc_sp->opcode == MP_BC_SETUP_FINALLY || exc_sp->opcode == MP_BC_SETUP_WITH) {
467
468
469
470
471
472
473
474
475
476
477
478
479
                                // 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
480
481
                        break;

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

488
                    case MP_BC_END_FINALLY:
489
                        // not fully implemented
490
491
                        // if TOS is an exception, reraises the exception (3 values on TOS)
                        // if TOS is None, just pops it and continues
492
                        // if TOS is an integer, does something else
493
                        // else error
494
495
                        if (mp_obj_is_exception_type(TOP())) {
                            nlr_jump(sp[-1]);
496
497
498
                        }
                        if (TOP() == mp_const_none) {
                            sp--;
499
500
501
502
503
504
505
                        } 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;
506
507
                                case UNWIND_JUMP:
                                    goto unwind_jump;
508
509
                            }
                            assert(0);
510
511
512
                        } else {
                            assert(0);
                        }
513
514
                        break;

515
                    case MP_BC_GET_ITER:
516
                        SET_TOP(rt_getiter(TOP()));
517
518
                        break;

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

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

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

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

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

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

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

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

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

588
                    case MP_BC_BUILD_MAP:
589
590
591
592
                        DECODE_UINT;
                        PUSH(rt_build_map(unum));
                        break;

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

598
                    case MP_BC_MAP_ADD:
Damien's avatar
Damien committed
599
                        DECODE_UINT;
600
601
602
                        // 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
603
604
                        break;

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

611
                    case MP_BC_SET_ADD:
Damien's avatar
Damien committed
612
                        DECODE_UINT;
613
614
615
                        // 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
616
617
                        break;

618
#if MICROPY_ENABLE_SLICE
619
620
621
622
623
624
625
626
627
628
629
                    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;
630
#endif
631

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

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

                    case MP_BC_MAKE_FUNCTION_DEFARGS:
                        DECODE_UINT;
645
                        SET_TOP(rt_make_function_from_id(unum, false, TOP()));
646
647
                        break;

648
                    case MP_BC_MAKE_CLOSURE:
Damien's avatar
Damien committed
649
                        DECODE_UINT;
650
651
652
653
654
655
656
                        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
657
658
                        break;

659
                    case MP_BC_CALL_FUNCTION:
660
                        DECODE_UINT;
661
662
663
664
                        // 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));
665
666
                        break;

667
                    case MP_BC_CALL_METHOD:
668
                        DECODE_UINT;
669
670
671
672
                        // 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));
673
674
                        break;

675
                    case MP_BC_RETURN_VALUE:
676
677
unwind_return:
                        while (exc_sp >= exc_stack) {
678
                            if (exc_sp->opcode == MP_BC_SETUP_FINALLY || exc_sp->opcode == MP_BC_SETUP_WITH) {
679
680
681
682
683
684
685
686
687
688
689
690
691
692
                                // 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--;
                        }
693
                        nlr_pop();
694
                        *sp_in_out = sp;
695
                        assert(exc_sp == exc_stack - 1);
696
                        return MP_VM_RETURN_NORMAL;
697

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

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

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

                        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);
                            }

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

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

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

784
                    case MP_BC_IMPORT_STAR:
785
                        rt_import_all(POP());
786
787
                        break;

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

        } else {
            // exception occurred

799
800
801
802
803
804
805
806
807
            // 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
            }

808
            // set file and line number that the exception occurred at
809
810
            // TODO: don't set traceback for exceptions re-raised by END_FINALLY.
            // But consider how to handle nested exceptions.
811
812
            // 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) {
813
                machine_uint_t code_info_size = code_info[0] | (code_info[1] << 8) | (code_info[2] << 16) | (code_info[3] << 24);
814
815
                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);
816
817
                machine_uint_t source_line = 1;
                machine_uint_t bc = save_ip - code_info - code_info_size;
818
                //printf("find %lu %d %d\n", bc, code_info[12], code_info[13]);
819
820
821
                for (const byte* ci = code_info + 12; *ci && bc >= ((*ci) & 31); ci++) {
                    bc -= *ci & 31;
                    source_line += *ci >> 5;
822
                }
823
                mp_obj_exception_add_traceback(nlr.ret_val, source_file, source_line, block_name);
824
825
            }

826
827
828
            while (currently_in_except_block) {
                // nested exception

829
                assert(exc_sp >= exc_stack);
830
831
832
833
834

                // 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
835
                POP_EXC_BLOCK();
836
837
            }

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

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

850
            } else {
851
852
853
854
                // 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;
855
            }
Damien's avatar
Damien committed
856
857
858
        }
    }
}