vm.c 35.6 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); \
Damien George's avatar
Damien George committed
54
    exc_sp->prev_exc = MP_OBJ_NULL; \
55
56
    currently_in_except_block = 0; /* in a try block now */

57
58
59
60
#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 */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

699
700
                    case MP_BC_RAISE_VARARGS:
                        unum = *ip++;
701
702
                        assert(unum <= 1);
                        if (unum == 0) {
Damien George's avatar
Damien George committed
703
704
705
706
707
708
709
710
711
                            // search for the inner-most previous exception, to reraise it
                            obj1 = MP_OBJ_NULL;
                            for (mp_exc_stack *e = exc_sp; e >= exc_stack; e--) {
                                if (e->prev_exc != MP_OBJ_NULL) {
                                    obj1 = e->prev_exc;
                                    break;
                                }
                            }
                            if (obj1 == MP_OBJ_NULL) {
712
713
                                nlr_jump(mp_obj_new_exception_msg(&mp_type_RuntimeError, "No active exception to reraise"));
                            }
714
715
716
                        } else {
                            obj1 = POP();
                        }
717
                        nlr_jump(rt_make_raise_obj(obj1));
718

719
                    case MP_BC_YIELD_VALUE:
720
yield:
721
722
723
                        nlr_pop();
                        *ip_in_out = ip;
                        *sp_in_out = sp;
724
                        *exc_sp_in_out = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block);
725
                        return MP_VM_RETURN_YIELD;
726

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

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

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

778
                    case MP_BC_IMPORT_NAME:
779
780
                        DECODE_QSTR;
                        obj1 = POP();
781
                        SET_TOP(rt_import_name(qst, obj1, TOP()));
782
783
                        break;

784
                    case MP_BC_IMPORT_FROM:
785
                        DECODE_QSTR;
786
                        obj1 = rt_import_from(TOP(), qst);
787
788
789
                        PUSH(obj1);
                        break;

790
                    case MP_BC_IMPORT_STAR:
791
                        rt_import_all(POP());
792
793
                        break;

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

        } else {
            // exception occurred

805
806
807
808
809
810
811
812
813
            // 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
            }

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

832
833
834
            while (currently_in_except_block) {
                // nested exception

835
                assert(exc_sp >= exc_stack);
836
837
838
839
840

                // 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
841
                POP_EXC_BLOCK();
842
843
            }

844
            if (exc_sp >= exc_stack) {
845
846
847
                // set flag to indicate that we are now handling an exception
                currently_in_except_block = 1;

848
                // catch exception and pass to byte code
849
                sp = MP_TAGPTR_PTR(exc_sp->val_sp);
850
                ip = exc_sp->handler;
Damien George's avatar
Damien George committed
851
852
                // save this exception in the stack so it can be used in a reraise, if needed
                exc_sp->prev_exc = nlr.ret_val;
Damien's avatar
Damien committed
853
                // push(traceback, exc-val, exc-type)
854
                PUSH(mp_const_none);
Damien's avatar
Damien committed
855
                PUSH(nlr.ret_val);
856
                PUSH(mp_obj_get_type(nlr.ret_val));
857

858
            } else {
859
860
861
862
                // 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;
863
            }
Damien's avatar
Damien committed
864
865
866
        }
    }
}