vm.c 31 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
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) {
49
50
51
52
53
54
    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;

55
56
57
58
    // 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;
59

60
61
    // allocate state for locals and stack
    mp_obj_t temp_state[10];
62
    mp_obj_t *state = &temp_state[0];
63
    if (n_state > 10) {
64
        state = m_new(mp_obj_t, n_state);
65
    }
66
67
    mp_obj_t *sp = &state[0] - 1;

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

76
    // init args
77
    for (uint i = 0; i < n_args; i++) {
78
        state[n_state - 1 - i] = args[i];
79
    }
80
81
82
    for (uint i = 0; i < n_args2; i++) {
        state[n_state - 1 - n_args - i] = args2[i];
    }
83

84
85
86
87
88
89
90
    // 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
91
92
93
94
        }
    }

    // execute the byte code
95
    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);
96
97
98
99
100
101
102
103
104
105
106
107
108

    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;
109
110
111
    }
}

112
113
// 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
114
115
116
117
// 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]
118
119
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,
120
121
                                           mp_exc_stack *exc_stack, mp_exc_stack **exc_sp_in_out,
                                           volatile mp_obj_t inject_exc) {
Damien's avatar
Damien committed
122
123
    // careful: be sure to declare volatile any variables read in the exception handler (written is ok, I think)

124
    const byte *ip = *ip_in_out;
125
    mp_obj_t *sp = *sp_in_out;
Damien's avatar
Damien committed
126
    machine_uint_t unum;
127
    qstr qst;
128
    mp_obj_t obj1, obj2;
129
    nlr_buf_t nlr;
Damien's avatar
Damien committed
130

131
132
    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
133
    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
134

135
    // outer exception handling loop
Damien's avatar
Damien committed
136
    for (;;) {
137
outer_dispatch_loop:
138
        if (nlr_push(&nlr) == 0) {
139
140
141
            // 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.
142
143
144
            // 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) {
145
146
147
148
                mp_obj_t t = inject_exc;
                inject_exc = MP_OBJ_NULL;
                nlr_jump(rt_make_raise_obj(t));
            }
149
150
            // loop to execute byte code
            for (;;) {
151
dispatch_loop:
152
                save_ip = ip;
153
154
                int op = *ip++;
                switch (op) {
155
156
                    case MP_BC_LOAD_CONST_FALSE:
                        PUSH(mp_const_false);
157
158
                        break;

159
160
                    case MP_BC_LOAD_CONST_NONE:
                        PUSH(mp_const_none);
161
162
                        break;

163
164
                    case MP_BC_LOAD_CONST_TRUE:
                        PUSH(mp_const_true);
165
                        break;
Damien George's avatar
Damien George committed
166
167
168
169

                    case MP_BC_LOAD_CONST_ELLIPSIS:
                        PUSH(mp_const_ellipsis);
                        break;
170

171
                    case MP_BC_LOAD_CONST_SMALL_INT: {
172
                        machine_int_t num = 0;
173
174
175
176
177
178
179
180
                        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));
181
                        break;
182
                    }
183

184
185
                    case MP_BC_LOAD_CONST_INT:
                        DECODE_QSTR;
186
                        PUSH(mp_obj_new_int_from_long_str(qstr_str(qst)));
187
188
                        break;

189
                    case MP_BC_LOAD_CONST_DEC:
Damien's avatar
Damien committed
190
                        DECODE_QSTR;
191
                        PUSH(rt_load_const_dec(qst));
Damien's avatar
Damien committed
192
193
                        break;

194
                    case MP_BC_LOAD_CONST_ID:
195
                        DECODE_QSTR;
196
                        PUSH(rt_load_const_str(qst)); // TODO
197
198
                        break;

199
200
                    case MP_BC_LOAD_CONST_BYTES:
                        DECODE_QSTR;
201
                        PUSH(rt_load_const_bytes(qst));
202
203
                        break;

204
                    case MP_BC_LOAD_CONST_STRING:
205
                        DECODE_QSTR;
206
                        PUSH(rt_load_const_str(qst));
207
208
                        break;

209
                    case MP_BC_LOAD_FAST_0:
210
                        PUSH(fastn[0]);
211
212
                        break;

213
                    case MP_BC_LOAD_FAST_1:
214
                        PUSH(fastn[-1]);
215
216
                        break;

217
                    case MP_BC_LOAD_FAST_2:
218
                        PUSH(fastn[-2]);
219
220
                        break;

221
                    case MP_BC_LOAD_FAST_N:
222
                        DECODE_UINT;
223
                        PUSH(fastn[-unum]);
224
225
                        break;

226
                    case MP_BC_LOAD_DEREF:
Damien's avatar
Damien committed
227
                        DECODE_UINT;
228
                        PUSH(rt_get_cell(fastn[-unum]));
Damien's avatar
Damien committed
229
230
                        break;

231
                    case MP_BC_LOAD_NAME:
232
                        DECODE_QSTR;
233
                        PUSH(rt_load_name(qst));
234
235
                        break;

236
                    case MP_BC_LOAD_GLOBAL:
237
                        DECODE_QSTR;
238
                        PUSH(rt_load_global(qst));
239
240
                        break;

241
                    case MP_BC_LOAD_ATTR:
242
                        DECODE_QSTR;
243
                        SET_TOP(rt_load_attr(TOP(), qst));
244
245
                        break;

246
                    case MP_BC_LOAD_METHOD:
247
                        DECODE_QSTR;
248
                        rt_load_method(*sp, qst, sp);
249
                        sp += 1;
250
251
                        break;

252
                    case MP_BC_LOAD_BUILD_CLASS:
253
254
255
                        PUSH(rt_load_build_class());
                        break;

256
                    case MP_BC_STORE_FAST_0:
257
                        fastn[0] = POP();
258
259
                        break;

260
                    case MP_BC_STORE_FAST_1:
261
                        fastn[-1] = POP();
262
263
                        break;

264
                    case MP_BC_STORE_FAST_2:
265
                        fastn[-2] = POP();
266
267
                        break;

268
                    case MP_BC_STORE_FAST_N:
269
                        DECODE_UINT;
270
                        fastn[-unum] = POP();
271
272
                        break;

273
                    case MP_BC_STORE_DEREF:
Damien's avatar
Damien committed
274
                        DECODE_UINT;
275
                        rt_set_cell(fastn[-unum], POP());
Damien's avatar
Damien committed
276
277
                        break;

278
                    case MP_BC_STORE_NAME:
279
                        DECODE_QSTR;
280
                        rt_store_name(qst, POP());
281
282
                        break;

283
                    case MP_BC_STORE_GLOBAL:
284
                        DECODE_QSTR;
285
                        rt_store_global(qst, POP());
286
287
                        break;

288
                    case MP_BC_STORE_ATTR:
289
                        DECODE_QSTR;
290
                        rt_store_attr(sp[0], qst, sp[-1]);
291
                        sp -= 2;
292
293
                        break;

294
                    case MP_BC_STORE_SUBSCR:
295
296
                        rt_store_subscr(sp[-1], sp[0], sp[-2]);
                        sp -= 3;
297
298
                        break;

299
300
301
302
303
                    case MP_BC_DELETE_NAME:
                        DECODE_QSTR;
                        rt_delete_name(qst);
                        break;

304
                    case MP_BC_DUP_TOP:
305
                        obj1 = TOP();
306
307
308
                        PUSH(obj1);
                        break;

309
                    case MP_BC_DUP_TOP_TWO:
310
311
312
                        sp += 2;
                        sp[0] = sp[-2];
                        sp[-1] = sp[-3];
313
314
                        break;

315
                    case MP_BC_POP_TOP:
316
                        sp -= 1;
317
318
                        break;

319
                    case MP_BC_ROT_TWO:
320
                        obj1 = sp[0];
321
322
                        sp[0] = sp[-1];
                        sp[-1] = obj1;
323
324
                        break;

325
                    case MP_BC_ROT_THREE:
326
                        obj1 = sp[0];
327
328
329
                        sp[0] = sp[-1];
                        sp[-1] = sp[-2];
                        sp[-2] = obj1;
330
331
                        break;

332
                    case MP_BC_JUMP:
Damien's avatar
Damien committed
333
334
                        DECODE_SLABEL;
                        ip += unum;
335
336
                        break;

337
                    case MP_BC_POP_JUMP_IF_TRUE:
Damien's avatar
Damien committed
338
                        DECODE_SLABEL;
339
                        if (rt_is_true(POP())) {
Damien's avatar
Damien committed
340
                            ip += unum;
341
342
343
                        }
                        break;

344
                    case MP_BC_POP_JUMP_IF_FALSE:
Damien's avatar
Damien committed
345
                        DECODE_SLABEL;
346
                        if (!rt_is_true(POP())) {
Damien's avatar
Damien committed
347
                            ip += unum;
348
349
350
                        }
                        break;

351
                    case MP_BC_JUMP_IF_TRUE_OR_POP:
Damien's avatar
Damien committed
352
                        DECODE_SLABEL;
353
                        if (rt_is_true(TOP())) {
Damien's avatar
Damien committed
354
355
                            ip += unum;
                        } else {
356
                            sp--;
Damien's avatar
Damien committed
357
358
359
                        }
                        break;

360
                    case MP_BC_JUMP_IF_FALSE_OR_POP:
Damien's avatar
Damien committed
361
                        DECODE_SLABEL;
362
                        if (rt_is_true(TOP())) {
363
                            sp--;
Damien's avatar
Damien committed
364
365
366
367
368
                        } else {
                            ip += unum;
                        }
                        break;

369
                        /* we are trying to get away without using this opcode
370
                    case MP_BC_SETUP_LOOP:
371
                        DECODE_UINT;
372
                        // push_block(MP_BC_SETUP_LOOP, ip + unum, sp)
373
374
375
                        break;
                        */

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
                    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);
                            if (exc_sp->opcode == MP_BC_SETUP_FINALLY) {
                                // 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
399
400
                        break;

Damien's avatar
Damien committed
401
                    // matched against: POP_BLOCK or POP_EXCEPT (anything else?)
402
                    case MP_BC_SETUP_EXCEPT:
403
                    case MP_BC_SETUP_FINALLY:
Damien's avatar
Damien committed
404
                        DECODE_ULABEL; // except labels are always forward
405
406
407
                        ++exc_sp;
                        exc_sp->opcode = op;
                        exc_sp->handler = ip + unum;
408
                        exc_sp->val_sp = MP_TAGPTR_MAKE(sp, currently_in_except_block);
409
                        currently_in_except_block = 0; // in a try block now
410
411
                        break;

412
                    case MP_BC_END_FINALLY:
413
                        // not fully implemented
414
415
                        // if TOS is an exception, reraises the exception (3 values on TOS)
                        // if TOS is None, just pops it and continues
416
                        // if TOS is an integer, does something else
417
                        // else error
418
                        if (mp_obj_is_exception_instance(TOP())) {
419
420
421
422
                            nlr_jump(TOP());
                        }
                        if (TOP() == mp_const_none) {
                            sp--;
423
424
425
426
427
428
429
                        } 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;
430
431
                                case UNWIND_JUMP:
                                    goto unwind_jump;
432
433
                            }
                            assert(0);
434
435
436
                        } else {
                            assert(0);
                        }
437
438
                        break;

439
                    case MP_BC_GET_ITER:
440
                        SET_TOP(rt_getiter(TOP()));
441
442
                        break;

443
                    case MP_BC_FOR_ITER:
Damien's avatar
Damien committed
444
                        DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
445
446
                        obj1 = rt_iternext_allow_raise(TOP());
                        if (obj1 == MP_OBJ_NULL) {
447
                            --sp; // pop the exhausted iterator
Damien's avatar
Damien committed
448
                            ip += unum; // jump to after for-block
449
450
451
452
453
                        } else {
                            PUSH(obj1); // push the next iteration value
                        }
                        break;

Damien's avatar
Damien committed
454
                    // matched against: SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH
455
                    case MP_BC_POP_BLOCK:
Damien's avatar
Damien committed
456
                        // we are exiting an exception handler, so pop the last one of the exception-stack
457
                        assert(exc_sp >= exc_stack);
458
                        currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); // restore previous state
459
                        exc_sp--; // pop back to previous exception handler
460
461
                        break;

462
                    // matched against: SETUP_EXCEPT
463
                    case MP_BC_POP_EXCEPT:
Damien's avatar
Damien committed
464
                        // TODO need to work out how blocks work etc
465
                        // pops block, checks it's an exception block, and restores the stack, saving the 3 exception values to local threadstate
466
                        assert(exc_sp >= exc_stack);
467
                        assert(currently_in_except_block);
468
                        //sp = (mp_obj_t*)(*exc_sp--);
Damien's avatar
Damien committed
469
                        //exc_sp--; // discard ip
470
                        currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); // restore previous state
471
                        exc_sp--; // pop back to previous exception handler
472
                        //sp -= 3; // pop 3 exception values
473
474
                        break;

475
476
477
478
479
480
481
482
                    case MP_BC_NOT:
                        if (TOP() == mp_const_true) {
                            SET_TOP(mp_const_false);
                        } else {
                            SET_TOP(mp_const_true);
                        }
                        break;

483
                    case MP_BC_UNARY_OP:
Damien's avatar
Damien committed
484
                        unum = *ip++;
485
                        SET_TOP(rt_unary_op(unum, TOP()));
Damien's avatar
Damien committed
486
487
                        break;

488
                    case MP_BC_BINARY_OP:
489
490
                        unum = *ip++;
                        obj2 = POP();
491
492
                        obj1 = TOP();
                        SET_TOP(rt_binary_op(unum, obj1, obj2));
493
494
                        break;

495
                    case MP_BC_BUILD_TUPLE:
496
                        DECODE_UINT;
497
498
                        sp -= unum - 1;
                        SET_TOP(rt_build_tuple(unum, sp));
499
500
                        break;

501
                    case MP_BC_BUILD_LIST:
502
                        DECODE_UINT;
503
504
                        sp -= unum - 1;
                        SET_TOP(rt_build_list(unum, sp));
505
506
                        break;

507
                    case MP_BC_LIST_APPEND:
508
509
                        DECODE_UINT;
                        // I think it's guaranteed by the compiler that sp[unum] is a list
510
511
                        rt_list_append(sp[-unum], sp[0]);
                        sp--;
512
513
                        break;

514
                    case MP_BC_BUILD_MAP:
515
516
517
518
                        DECODE_UINT;
                        PUSH(rt_build_map(unum));
                        break;

519
                    case MP_BC_STORE_MAP:
520
521
                        sp -= 2;
                        rt_store_map(sp[0], sp[2], sp[1]);
522
523
                        break;

524
                    case MP_BC_MAP_ADD:
Damien's avatar
Damien committed
525
                        DECODE_UINT;
526
527
528
                        // 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
529
530
                        break;

531
                    case MP_BC_BUILD_SET:
532
                        DECODE_UINT;
533
534
                        sp -= unum - 1;
                        SET_TOP(rt_build_set(unum, sp));
535
536
                        break;

537
                    case MP_BC_SET_ADD:
Damien's avatar
Damien committed
538
                        DECODE_UINT;
539
540
541
                        // 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
542
543
                        break;

544
#if MICROPY_ENABLE_SLICE
545
546
547
548
549
550
551
552
553
554
555
                    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;
556
#endif
557

558
                    case MP_BC_UNPACK_SEQUENCE:
559
                        DECODE_UINT;
560
                        rt_unpack_sequence(sp[0], unum, sp);
561
                        sp += unum - 1;
562
563
                        break;

564
                    case MP_BC_MAKE_FUNCTION:
565
                        DECODE_UINT;
566
567
568
569
570
571
                        PUSH(rt_make_function_from_id(unum, MP_OBJ_NULL));
                        break;

                    case MP_BC_MAKE_FUNCTION_DEFARGS:
                        DECODE_UINT;
                        SET_TOP(rt_make_function_from_id(unum, TOP()));
572
573
                        break;

574
                    case MP_BC_MAKE_CLOSURE:
Damien's avatar
Damien committed
575
                        DECODE_UINT;
576
577
578
579
580
581
582
                        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
583
584
                        break;

585
                    case MP_BC_CALL_FUNCTION:
586
                        DECODE_UINT;
587
588
589
590
                        // 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));
591
592
                        break;

593
                    case MP_BC_CALL_METHOD:
594
                        DECODE_UINT;
595
596
597
598
                        // 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));
599
600
                        break;

601
                    case MP_BC_RETURN_VALUE:
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
unwind_return:
                        while (exc_sp >= exc_stack) {
                            if (exc_sp->opcode == MP_BC_SETUP_FINALLY) {
                                // 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--;
                        }
619
                        nlr_pop();
620
                        *sp_in_out = sp;
621
                        assert(exc_sp == exc_stack - 1);
622
                        return MP_VM_RETURN_NORMAL;
623

624
625
                    case MP_BC_RAISE_VARARGS:
                        unum = *ip++;
626
627
628
629
630
631
632
633
                        assert(unum <= 1);
                        if (unum == 0) {
                            // This assumes that nlr.ret_val holds last raised
                            // exception and is not overwritten since then.
                            obj1 = nlr.ret_val;
                        } else {
                            obj1 = POP();
                        }
634
                        nlr_jump(rt_make_raise_obj(obj1));
635

636
                    case MP_BC_YIELD_VALUE:
637
yield:
638
639
640
                        nlr_pop();
                        *ip_in_out = ip;
                        *sp_in_out = sp;
641
                        *exc_sp_in_out = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block);
642
                        return MP_VM_RETURN_YIELD;
643

644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
                    case MP_BC_YIELD_FROM:
                    {
//#define EXC_MATCH(exc, type) MP_OBJ_IS_TYPE(exc, type)
#define EXC_MATCH(exc, type) mp_obj_exception_match(exc, type)
                        mp_vm_return_kind_t ret_kind;
                        obj1 = POP();
                        mp_obj_t t = MP_OBJ_NULL;
                        if (inject_exc != MP_OBJ_NULL) {
                            t = inject_exc;
                            inject_exc = MP_OBJ_NULL;
                            obj2 = mp_obj_gen_resume(TOP(), mp_const_none, t, &ret_kind);
                        } else {
                            obj2 = mp_obj_gen_resume(TOP(), obj1, MP_OBJ_NULL, &ret_kind);
                        }

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

                            // if it swallowed it, we re-raise GeneratorExit
                            if (t != MP_OBJ_NULL && EXC_MATCH(t, &mp_type_GeneratorExit)) {
                                nlr_jump(t);
                            }

                            break;
                        }
                        if (ret_kind == MP_VM_RETURN_EXCEPTION) {
                            // Pop exhausted gen
                            sp--;
                            if (EXC_MATCH(obj2, &mp_type_StopIteration)) {
                                printf("Generator explicitly raised StopIteration\n");
                                PUSH(mp_const_none);
                                break;
                            } else {
                                nlr_jump(obj2);
                            }
                        }
                    }

695
                    case MP_BC_IMPORT_NAME:
696
697
                        DECODE_QSTR;
                        obj1 = POP();
698
                        SET_TOP(rt_import_name(qst, obj1, TOP()));
699
700
                        break;

701
                    case MP_BC_IMPORT_FROM:
702
                        DECODE_QSTR;
703
                        obj1 = rt_import_from(TOP(), qst);
704
705
706
                        PUSH(obj1);
                        break;

707
                    case MP_BC_IMPORT_STAR:
708
                        rt_import_all(POP());
709
710
                        break;

711
                    default:
Damien's avatar
Damien committed
712
                        printf("code %p, byte code 0x%02x not implemented\n", ip, op);
713
714
                        assert(0);
                        nlr_pop();
715
                        return MP_VM_RETURN_NORMAL;
Damien's avatar
Damien committed
716
                }
717
718
719
720
721
            }

        } else {
            // exception occurred

722
723
724
725
726
727
728
729
730
            // 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
            }

731
            // set file and line number that the exception occurred at
732
733
            // TODO: don't set traceback for exceptions re-raised by END_FINALLY.
            // But consider how to handle nested exceptions.
734
            if (mp_obj_is_exception_instance(nlr.ret_val)) {
735
                machine_uint_t code_info_size = code_info[0] | (code_info[1] << 8) | (code_info[2] << 16) | (code_info[3] << 24);
736
737
                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);
738
739
                machine_uint_t source_line = 1;
                machine_uint_t bc = save_ip - code_info - code_info_size;
740
                //printf("find %lu %d %d\n", bc, code_info[12], code_info[13]);
741
742
743
                for (const byte* ci = code_info + 12; *ci && bc >= ((*ci) & 31); ci++) {
                    bc -= *ci & 31;
                    source_line += *ci >> 5;
744
                }
745
                mp_obj_exception_add_traceback(nlr.ret_val, source_file, source_line, block_name);
746
747
            }

748
749
750
            while (currently_in_except_block) {
                // nested exception

751
                assert(exc_sp >= exc_stack);
752
753
754
755
756

                // 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
757
                currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); // restore previous state
758
                exc_sp--; // pop back to previous exception handler
759
760
            }

761
            if (exc_sp >= exc_stack) {
762
763
764
                // set flag to indicate that we are now handling an exception
                currently_in_except_block = 1;

765
                // catch exception and pass to byte code
766
                sp = MP_TAGPTR_PTR(exc_sp->val_sp);
767
                ip = exc_sp->handler;
Damien's avatar
Damien committed
768
                // push(traceback, exc-val, exc-type)
769
                PUSH(mp_const_none);
Damien's avatar
Damien committed
770
                PUSH(nlr.ret_val);
771
                PUSH(nlr.ret_val); // TODO should be type(nlr.ret_val), I think...
772

773
            } else {
774
775
776
777
                // 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;
778
            }
Damien's avatar
Damien committed
779
780
781
        }
    }
}