vm.c 50.5 KB
Newer Older
1
2
3
4
5
6
/*
 * This file is part of the Micro Python project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013, 2014 Damien P. George
7
 * Copyright (c) 2014 Paul Sokolovsky
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

Damien's avatar
Damien committed
28
#include <stdio.h>
29
#include <string.h>
Damien's avatar
Damien committed
30
31
#include <assert.h>

32
#include "py/mpstate.h"
33
34
#include "py/nlr.h"
#include "py/emitglue.h"
35
#include "py/objtype.h"
36
37
38
#include "py/runtime.h"
#include "py/bc0.h"
#include "py/bc.h"
Damien's avatar
Damien committed
39

40
#if 0
41
#define TRACE(ip) printf("sp=" INT_FMT " ", sp - code_state->sp); mp_bytecode_print2(ip, 1);
42
43
44
#else
#define TRACE(ip)
#endif
45

Paul Sokolovsky's avatar
Paul Sokolovsky committed
46
47
48
49
50
51
52
// 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.

53
// Exception stack unwind reasons (WHY_* in CPython-speak)
54
55
// TODO perhaps compress this to RETURN=0, JUMP>0, with number of unwinds
// left to do encoded in the JUMP number
56
57
typedef enum {
    UNWIND_RETURN = 1,
58
    UNWIND_JUMP,
59
60
} mp_unwind_reason_t;

61
62
#define DECODE_UINT \
    mp_uint_t unum = 0; \
63
64
    do { \
        unum = (unum << 7) + (*ip & 0x7f); \
65
66
67
    } while ((*ip++ & 0x80) != 0)
#define DECODE_ULABEL mp_uint_t ulab = (ip[0] | (ip[1] << 8)); ip += 2
#define DECODE_SLABEL mp_uint_t slab = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2
68
#define DECODE_QSTR qstr qst = 0; \
69
70
    do { \
        qst = (qst << 7) + (*ip & 0x7f); \
71
    } while ((*ip++ & 0x80) != 0)
72
#define DECODE_PTR \
73
    ip = (byte*)(((mp_uint_t)ip + sizeof(mp_uint_t) - 1) & (~(sizeof(mp_uint_t) - 1))); /* align ip */ \
74
75
    void *ptr = (void*)*(mp_uint_t*)ip; \
    ip += sizeof(mp_uint_t)
76
77
#define PUSH(val) *++sp = (val)
#define POP() (*sp--)
78
79
#define TOP() (*sp)
#define SET_TOP(val) *sp = (val)
Damien's avatar
Damien committed
80

81
#define PUSH_EXC_BLOCK(with_or_finally) do { \
82
83
    DECODE_ULABEL; /* except labels are always forward */ \
    ++exc_sp; \
84
    exc_sp->handler = ip + ulab; \
85
    exc_sp->val_sp = MP_TAGPTR_MAKE(sp, ((with_or_finally) << 1) | currently_in_except_block); \
Damien George's avatar
Damien George committed
86
    exc_sp->prev_exc = MP_OBJ_NULL; \
87
88
    currently_in_except_block = 0; /* in a try block now */ \
} while (0)
89

90
#define POP_EXC_BLOCK() \
91
    currently_in_except_block = MP_TAGPTR_TAG0(exc_sp->val_sp); /* restore previous state */ \
92
93
    exc_sp--; /* pop back to previous exception handler */

94
95
// 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
96
97
98
99
// 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]
100
mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc) {
101
102
#define SELECTIVE_EXC_IP (0)
#if SELECTIVE_EXC_IP
103
#define MARK_EXC_IP_SELECTIVE() { code_state->ip = ip; } /* stores ip 1 byte past last opcode */
104
105
106
#define MARK_EXC_IP_GLOBAL()
#else
#define MARK_EXC_IP_SELECTIVE()
107
#define MARK_EXC_IP_GLOBAL() { code_state->ip = ip; } /* stores ip pointing to last opcode */
108
#endif
109
#if MICROPY_OPT_COMPUTED_GOTO
110
    #include "py/vmentrytable.h"
111
    #define DISPATCH() do { \
112
        TRACE(ip); \
113
        MARK_EXC_IP_GLOBAL(); \
114
        goto *entry_table[*ip++]; \
115
    } while(0)
116
    #define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check
117
118
    #define ENTRY(op) entry_##op
    #define ENTRY_DEFAULT entry_default
119
#else
120
    #define DISPATCH() break
121
    #define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check
122
123
    #define ENTRY(op) case op
    #define ENTRY_DEFAULT default
124
125
#endif

126
127
128
129
130
    // nlr_raise needs to be implemented as a goto, so that the C compiler's flow analyser
    // sees that it's possible for us to jump from the dispatch loop to the exception
    // handler.  Without this, the code may have a different stack layout in the dispatch
    // loop and the exception handler, leading to very obscure bugs.
    #define RAISE(o) do { nlr_pop(); nlr.ret_val = o; goto exception_handler; } while(0)
Damien's avatar
Damien committed
131

132
133
134
#if MICROPY_STACKLESS
run_code_state: ;
#endif
135
    // Pointers which are constant for particular invocation of mp_execute_bytecode()
136
137
    mp_obj_t */*const*/ fastn = &code_state->state[code_state->n_state - 1];
    mp_exc_stack_t */*const*/ exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state);
138

139
    // variables that are visible to the exception handler (declared volatile)
140
    volatile bool currently_in_except_block = MP_TAGPTR_TAG0(code_state->exc_sp); // 0 or 1, to detect nested exceptions
141
    mp_exc_stack_t *volatile exc_sp = MP_TAGPTR_PTR(code_state->exc_sp); // stack grows up, exc_sp points to top of stack
Damien's avatar
Damien committed
142

143
    // outer exception handling loop
Damien's avatar
Damien committed
144
    for (;;) {
145
        nlr_buf_t nlr;
146
outer_dispatch_loop:
147
        if (nlr_push(&nlr) == 0) {
148
            // local variables that are not visible to the exception handler
149
150
            const byte *ip = code_state->ip;
            mp_obj_t *sp = code_state->sp;
151
            mp_obj_t obj_shared;
152

153
154
155
            // If we have exception to inject, now that we finish setting up
            // execution context, raise it. This works as if RAISE_VARARGS
            // bytecode was executed.
156
157
158
            // Injecting exc into yield from generator is a special case,
            // handled by MP_BC_YIELD_FROM itself
            if (inject_exc != MP_OBJ_NULL && *ip != MP_BC_YIELD_FROM) {
159
                mp_obj_t exc = inject_exc;
160
                inject_exc = MP_OBJ_NULL;
161
162
                exc = mp_make_raise_obj(exc);
                RAISE(exc);
163
            }
164

165
166
            // loop to execute byte code
            for (;;) {
167
dispatch_loop:
168
#if MICROPY_OPT_COMPUTED_GOTO
169
170
                DISPATCH();
#else
171
                TRACE(ip);
172
                MARK_EXC_IP_GLOBAL();
173
                switch (*ip++) {
174
#endif
175

176
177
178
                ENTRY(MP_BC_LOAD_CONST_FALSE):
                    PUSH(mp_const_false);
                    DISPATCH();
179

180
181
182
                ENTRY(MP_BC_LOAD_CONST_NONE):
                    PUSH(mp_const_none);
                    DISPATCH();
183

184
185
186
                ENTRY(MP_BC_LOAD_CONST_TRUE):
                    PUSH(mp_const_true);
                    DISPATCH();
187

188
189
190
                ENTRY(MP_BC_LOAD_CONST_ELLIPSIS):
                    PUSH((mp_obj_t)&mp_const_ellipsis_obj);
                    DISPATCH();
191

192
                ENTRY(MP_BC_LOAD_CONST_SMALL_INT): {
193
                    mp_int_t num = 0;
194
195
196
197
198
199
200
201
202
203
                    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));
                    DISPATCH();
                }
204

205
                ENTRY(MP_BC_LOAD_CONST_BYTES): {
206
207
208
                    DECODE_QSTR;
                    PUSH(mp_load_const_bytes(qst));
                    DISPATCH();
209
                }
210

211
                ENTRY(MP_BC_LOAD_CONST_STRING): {
212
213
214
                    DECODE_QSTR;
                    PUSH(mp_load_const_str(qst));
                    DISPATCH();
215
                }
216

217
218
219
220
221
222
                ENTRY(MP_BC_LOAD_CONST_OBJ): {
                    DECODE_PTR;
                    PUSH(ptr);
                    DISPATCH();
                }

223
224
225
226
                ENTRY(MP_BC_LOAD_NULL):
                    PUSH(MP_OBJ_NULL);
                    DISPATCH();

227
                ENTRY(MP_BC_LOAD_FAST_N): {
228
                    DECODE_UINT;
229
                    obj_shared = fastn[-unum];
230
                    load_check:
231
232
                    if (obj_shared == MP_OBJ_NULL) {
                        local_name_error: {
233
                            MARK_EXC_IP_SELECTIVE();
234
235
236
                            mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NameError, "local variable referenced before assignment");
                            RAISE(obj);
                        }
237
                    }
238
                    PUSH(obj_shared);
239
                    DISPATCH();
240
                }
241

242
                ENTRY(MP_BC_LOAD_DEREF): {
243
                    DECODE_UINT;
244
                    obj_shared = mp_obj_cell_get(fastn[-unum]);
245
                    goto load_check;
246
                }
247

248
                #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
249
                ENTRY(MP_BC_LOAD_NAME): {
250
                    MARK_EXC_IP_SELECTIVE();
251
252
253
                    DECODE_QSTR;
                    PUSH(mp_load_name(qst));
                    DISPATCH();
254
                }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
                #else
                ENTRY(MP_BC_LOAD_NAME): {
                    MARK_EXC_IP_SELECTIVE();
                    DECODE_QSTR;
                    mp_obj_t key = MP_OBJ_NEW_QSTR(qst);
                    mp_uint_t x = *ip;
                    if (x < MP_STATE_CTX(dict_locals)->map.alloc && MP_STATE_CTX(dict_locals)->map.table[x].key == key) {
                        PUSH(MP_STATE_CTX(dict_locals)->map.table[x].value);
                    } else {
                        mp_map_elem_t *elem = mp_map_lookup(&MP_STATE_CTX(dict_locals)->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP);
                        if (elem != NULL) {
                            *(byte*)ip = (elem - &MP_STATE_CTX(dict_locals)->map.table[0]) & 0xff;
                            PUSH(elem->value);
                        } else {
                            PUSH(mp_load_name(MP_OBJ_QSTR_VALUE(key)));
                        }
                    }
                    ip++;
                    DISPATCH();
                }
                #endif
276

277
                #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
278
                ENTRY(MP_BC_LOAD_GLOBAL): {
279
                    MARK_EXC_IP_SELECTIVE();
280
281
282
                    DECODE_QSTR;
                    PUSH(mp_load_global(qst));
                    DISPATCH();
283
                }
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
                #else
                ENTRY(MP_BC_LOAD_GLOBAL): {
                    MARK_EXC_IP_SELECTIVE();
                    DECODE_QSTR;
                    mp_obj_t key = MP_OBJ_NEW_QSTR(qst);
                    mp_uint_t x = *ip;
                    if (x < MP_STATE_CTX(dict_globals)->map.alloc && MP_STATE_CTX(dict_globals)->map.table[x].key == key) {
                        PUSH(MP_STATE_CTX(dict_globals)->map.table[x].value);
                    } else {
                        mp_map_elem_t *elem = mp_map_lookup(&MP_STATE_CTX(dict_globals)->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP);
                        if (elem != NULL) {
                            *(byte*)ip = (elem - &MP_STATE_CTX(dict_globals)->map.table[0]) & 0xff;
                            PUSH(elem->value);
                        } else {
                            PUSH(mp_load_global(MP_OBJ_QSTR_VALUE(key)));
                        }
                    }
                    ip++;
                    DISPATCH();
                }
                #endif
305

306
                #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
307
                ENTRY(MP_BC_LOAD_ATTR): {
308
                    MARK_EXC_IP_SELECTIVE();
309
310
311
                    DECODE_QSTR;
                    SET_TOP(mp_load_attr(TOP(), qst));
                    DISPATCH();
312
                }
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
                #else
                ENTRY(MP_BC_LOAD_ATTR): {
                    MARK_EXC_IP_SELECTIVE();
                    DECODE_QSTR;
                    mp_obj_t top = TOP();
                    if (mp_obj_get_type(top)->load_attr == mp_obj_instance_load_attr) {
                        mp_obj_instance_t *self = top;
                        mp_uint_t x = *ip;
                        mp_obj_t key = MP_OBJ_NEW_QSTR(qst);
                        mp_map_elem_t *elem;
                        if (x < self->members.alloc && self->members.table[x].key == key) {
                            elem = &self->members.table[x];
                        } else {
                            elem = mp_map_lookup(&self->members, key, MP_MAP_LOOKUP);
                            if (elem != NULL) {
                                *(byte*)ip = elem - &self->members.table[0];
                            } else {
                                goto load_attr_cache_fail;
                            }
                        }
                        SET_TOP(elem->value);
                        ip++;
                        DISPATCH();
                    }
                load_attr_cache_fail:
                    SET_TOP(mp_load_attr(top, qst));
                    ip++;
                    DISPATCH();
                }
                #endif
343

344
                ENTRY(MP_BC_LOAD_METHOD): {
345
                    MARK_EXC_IP_SELECTIVE();
346
347
348
349
                    DECODE_QSTR;
                    mp_load_method(*sp, qst, sp);
                    sp += 1;
                    DISPATCH();
350
                }
351
352

                ENTRY(MP_BC_LOAD_BUILD_CLASS):
353
                    MARK_EXC_IP_SELECTIVE();
354
355
356
                    PUSH(mp_load_build_class());
                    DISPATCH();

357
                ENTRY(MP_BC_LOAD_SUBSCR): {
358
                    MARK_EXC_IP_SELECTIVE();
359
360
                    mp_obj_t index = POP();
                    SET_TOP(mp_obj_subscr(TOP(), index, MP_OBJ_SENTINEL));
361
                    DISPATCH();
362
                }
363

364
                ENTRY(MP_BC_STORE_FAST_N): {
365
366
367
                    DECODE_UINT;
                    fastn[-unum] = POP();
                    DISPATCH();
368
                }
369

370
                ENTRY(MP_BC_STORE_DEREF): {
371
372
373
                    DECODE_UINT;
                    mp_obj_cell_set(fastn[-unum], POP());
                    DISPATCH();
374
                }
375

376
                ENTRY(MP_BC_STORE_NAME): {
377
                    MARK_EXC_IP_SELECTIVE();
378
379
380
                    DECODE_QSTR;
                    mp_store_name(qst, POP());
                    DISPATCH();
381
                }
382

383
                ENTRY(MP_BC_STORE_GLOBAL): {
384
                    MARK_EXC_IP_SELECTIVE();
385
386
387
                    DECODE_QSTR;
                    mp_store_global(qst, POP());
                    DISPATCH();
388
                }
389

390
                #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
391
                ENTRY(MP_BC_STORE_ATTR): {
392
                    MARK_EXC_IP_SELECTIVE();
393
394
395
396
                    DECODE_QSTR;
                    mp_store_attr(sp[0], qst, sp[-1]);
                    sp -= 2;
                    DISPATCH();
397
                }
398
                #else
399
400
401
                // This caching code works with MICROPY_PY_BUILTINS_PROPERTY and/or
                // MICROPY_PY_DESCRIPTORS enabled because if the attr exists in
                // self->members then it can't be a property or have descriptors.  A
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
                // consequence of this is that we can't use MP_MAP_LOOKUP_ADD_IF_NOT_FOUND
                // in the fast-path below, because that store could override a property.
                ENTRY(MP_BC_STORE_ATTR): {
                    MARK_EXC_IP_SELECTIVE();
                    DECODE_QSTR;
                    mp_obj_t top = TOP();
                    if (mp_obj_get_type(top)->store_attr == mp_obj_instance_store_attr && sp[-1] != MP_OBJ_NULL) {
                        mp_obj_instance_t *self = top;
                        mp_uint_t x = *ip;
                        mp_obj_t key = MP_OBJ_NEW_QSTR(qst);
                        mp_map_elem_t *elem;
                        if (x < self->members.alloc && self->members.table[x].key == key) {
                            elem = &self->members.table[x];
                        } else {
                            elem = mp_map_lookup(&self->members, key, MP_MAP_LOOKUP);
                            if (elem != NULL) {
                                *(byte*)ip = elem - &self->members.table[0];
                            } else {
                                goto store_attr_cache_fail;
                            }
                        }
                        elem->value = sp[-1];
                        sp -= 2;
                        ip++;
                        DISPATCH();
                    }
                store_attr_cache_fail:
                    mp_store_attr(sp[0], qst, sp[-1]);
                    sp -= 2;
                    ip++;
                    DISPATCH();
                }
                #endif
435
436

                ENTRY(MP_BC_STORE_SUBSCR):
437
                    MARK_EXC_IP_SELECTIVE();
438
                    mp_obj_subscr(sp[-1], sp[0], sp[-2]);
439
440
441
                    sp -= 3;
                    DISPATCH();

442
                ENTRY(MP_BC_DELETE_FAST): {
443
                    MARK_EXC_IP_SELECTIVE();
444
445
446
447
448
449
                    DECODE_UINT;
                    if (fastn[-unum] == MP_OBJ_NULL) {
                        goto local_name_error;
                    }
                    fastn[-unum] = MP_OBJ_NULL;
                    DISPATCH();
450
                }
451

452
                ENTRY(MP_BC_DELETE_DEREF): {
453
                    MARK_EXC_IP_SELECTIVE();
454
455
456
457
458
459
                    DECODE_UINT;
                    if (mp_obj_cell_get(fastn[-unum]) == MP_OBJ_NULL) {
                        goto local_name_error;
                    }
                    mp_obj_cell_set(fastn[-unum], MP_OBJ_NULL);
                    DISPATCH();
460
                }
461

462
                ENTRY(MP_BC_DELETE_NAME): {
463
                    MARK_EXC_IP_SELECTIVE();
464
465
466
                    DECODE_QSTR;
                    mp_delete_name(qst);
                    DISPATCH();
467
                }
468

469
                ENTRY(MP_BC_DELETE_GLOBAL): {
470
                    MARK_EXC_IP_SELECTIVE();
471
472
473
                    DECODE_QSTR;
                    mp_delete_global(qst);
                    DISPATCH();
474
                }
475

476
477
478
                ENTRY(MP_BC_DUP_TOP): {
                    mp_obj_t top = TOP();
                    PUSH(top);
479
                    DISPATCH();
480
                }
481
482
483
484
485
486
487
488
489
490
491

                ENTRY(MP_BC_DUP_TOP_TWO):
                    sp += 2;
                    sp[0] = sp[-2];
                    sp[-1] = sp[-3];
                    DISPATCH();

                ENTRY(MP_BC_POP_TOP):
                    sp -= 1;
                    DISPATCH();

492
493
                ENTRY(MP_BC_ROT_TWO): {
                    mp_obj_t top = sp[0];
494
                    sp[0] = sp[-1];
495
                    sp[-1] = top;
496
                    DISPATCH();
497
                }
498

499
500
                ENTRY(MP_BC_ROT_THREE): {
                    mp_obj_t top = sp[0];
501
502
                    sp[0] = sp[-1];
                    sp[-1] = sp[-2];
503
                    sp[-2] = top;
504
                    DISPATCH();
505
                }
506

507
                ENTRY(MP_BC_JUMP): {
508
                    DECODE_SLABEL;
509
                    ip += slab;
510
                    DISPATCH_WITH_PEND_EXC_CHECK();
511
                }
512

513
                ENTRY(MP_BC_POP_JUMP_IF_TRUE): {
514
515
                    DECODE_SLABEL;
                    if (mp_obj_is_true(POP())) {
516
                        ip += slab;
517
                    }
518
                    DISPATCH_WITH_PEND_EXC_CHECK();
519
                }
Damien's avatar
Damien committed
520

521
                ENTRY(MP_BC_POP_JUMP_IF_FALSE): {
522
523
                    DECODE_SLABEL;
                    if (!mp_obj_is_true(POP())) {
524
                        ip += slab;
525
                    }
526
                    DISPATCH_WITH_PEND_EXC_CHECK();
527
                }
Damien's avatar
Damien committed
528

529
                ENTRY(MP_BC_JUMP_IF_TRUE_OR_POP): {
530
531
                    DECODE_SLABEL;
                    if (mp_obj_is_true(TOP())) {
532
                        ip += slab;
533
534
535
                    } else {
                        sp--;
                    }
536
                    DISPATCH_WITH_PEND_EXC_CHECK();
537
                }
538

539
                ENTRY(MP_BC_JUMP_IF_FALSE_OR_POP): {
540
541
542
543
                    DECODE_SLABEL;
                    if (mp_obj_is_true(TOP())) {
                        sp--;
                    } else {
544
                        ip += slab;
545
                    }
546
                    DISPATCH_WITH_PEND_EXC_CHECK();
547
                }
548

549
                ENTRY(MP_BC_SETUP_WITH): {
550
                    MARK_EXC_IP_SELECTIVE();
551
552
553
554
                    mp_obj_t obj = TOP();
                    SET_TOP(mp_load_attr(obj, MP_QSTR___exit__));
                    mp_load_method(obj, MP_QSTR___enter__, sp + 1);
                    mp_obj_t ret = mp_call_method_n_kw(0, 0, sp + 1);
555
                    PUSH_EXC_BLOCK(1);
556
                    PUSH(ret);
557
                    DISPATCH();
558
                }
559
560

                ENTRY(MP_BC_WITH_CLEANUP): {
561
                    MARK_EXC_IP_SELECTIVE();
562
563
564
565
566
567
568
                    // 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.
                    static const mp_obj_t no_exc[] = {mp_const_none, mp_const_none, mp_const_none};
                    if (TOP() == mp_const_none) {
                        sp--;
569
                        mp_obj_t obj = TOP();
570
                        SET_TOP(mp_const_none);
571
                        mp_call_function_n_kw(obj, 3, 0, no_exc);
572
                    } else if (MP_OBJ_IS_SMALL_INT(TOP())) {
573
574
575
576
577
578
579
580
                        mp_int_t cause_val = MP_OBJ_SMALL_INT_VALUE(TOP());
                        if (cause_val == UNWIND_RETURN) {
                            mp_call_function_n_kw(sp[-2], 3, 0, no_exc);
                        } else {
                            assert(cause_val == UNWIND_JUMP);
                            mp_call_function_n_kw(sp[-3], 3, 0, no_exc);
                            // Pop __exit__ boundmethod at sp[-3]
                            sp[-3] = sp[-2];
581
                        }
582
583
584
                        sp[-2] = sp[-1]; // copy retval down
                        sp[-1] = sp[0]; // copy cause down
                        sp--; // discard top value (was cause)
585
586
                    } else {
                        assert(mp_obj_is_exception_type(TOP()));
587
588
589
590
591
592
593
                        // Need to pass (sp[0], sp[-1], sp[-2]) as arguments so must reverse the
                        // order of these on the value stack (don't want to create a temporary
                        // array because it increases stack footprint of the VM).
                        mp_obj_t obj = sp[-2];
                        sp[-2] = sp[0];
                        sp[0] = obj;
                        mp_obj_t ret_value = mp_call_function_n_kw(sp[-3], 3, 0, &sp[-2]);
594
                        if (mp_obj_is_true(ret_value)) {
595
596
597
                            // 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...
598
                            sp -= 4;
599
600
601
                            // ... pop "with" exception handler, and signal END_FINALLY
                            // to just execute finally handler normally (by pushing None
                            // on value stack)
602
                            assert(exc_sp >= exc_stack);
603
604
                            POP_EXC_BLOCK();
                            PUSH(mp_const_none);
605
606
607
608
609
610
611
612
                        } else {
                            // Pop __exit__ boundmethod at sp[-3], remembering that top 3 values
                            // are reversed.
                            sp[-3] = sp[0];
                            obj = sp[-2];
                            sp[-2] = sp[-1];
                            sp[-1] = obj;
                            sp--;
613
                        }
614
615
616
                    }
                    DISPATCH();
                }
617

618
                ENTRY(MP_BC_UNWIND_JUMP): {
619
                    MARK_EXC_IP_SELECTIVE();
620
                    DECODE_SLABEL;
621
                    PUSH((void*)(ip + slab)); // push destination ip for jump
622
                    PUSH((void*)(mp_uint_t)(*ip)); // push number of exception handlers to unwind (0x80 bit set if we also need to pop stack)
623
624
unwind_jump:;
                    mp_uint_t unum = (mp_uint_t)POP(); // get number of exception handlers to unwind
625
                    while ((unum & 0x7f) > 0) {
626
                        unum -= 1;
627
                        assert(exc_sp >= exc_stack);
628
                        if (MP_TAGPTR_TAG1(exc_sp->val_sp)) {
629
630
631
632
633
634
635
636
637
                            // 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
638
                        }
639
640
641
                        exc_sp--;
                    }
                    ip = (const byte*)POP(); // pop destination ip for jump
642
643
644
                    if (unum != 0) {
                        sp--;
                    }
645
                    DISPATCH_WITH_PEND_EXC_CHECK();
646
                }
647
648
649

                // matched against: POP_BLOCK or POP_EXCEPT (anything else?)
                ENTRY(MP_BC_SETUP_EXCEPT):
650
                ENTRY(MP_BC_SETUP_FINALLY): {
651
                    MARK_EXC_IP_SELECTIVE();
652
653
654
655
656
                    #if SELECTIVE_EXC_IP
                    PUSH_EXC_BLOCK((code_state->ip[-1] == MP_BC_SETUP_FINALLY) ? 1 : 0);
                    #else
                    PUSH_EXC_BLOCK((code_state->ip[0] == MP_BC_SETUP_FINALLY) ? 1 : 0);
                    #endif
657
                    DISPATCH();
658
                }
659
660

                ENTRY(MP_BC_END_FINALLY):
661
                    MARK_EXC_IP_SELECTIVE();
662
663
664
665
666
667
                    // not fully implemented
                    // if TOS is an exception, reraises the exception (3 values on TOS)
                    // if TOS is None, just pops it and continues
                    // if TOS is an integer, does something else
                    // else error
                    if (mp_obj_is_exception_type(TOP())) {
668
                        RAISE(sp[-1]);
669
670
                    }
                    if (TOP() == mp_const_none) {
671
                        sp--;
672
673
                    } else {
                        assert(MP_OBJ_IS_SMALL_INT(TOP()));
674
675
676
                        // 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());
677
678
679
680
681
                        if (reason == UNWIND_RETURN) {
                            goto unwind_return;
                        } else {
                            assert(reason == UNWIND_JUMP);
                            goto unwind_jump;
682
683
684
685
686
                        }
                    }
                    DISPATCH();

                ENTRY(MP_BC_GET_ITER):
687
                    MARK_EXC_IP_SELECTIVE();
688
689
690
                    SET_TOP(mp_getiter(TOP()));
                    DISPATCH();

691
                ENTRY(MP_BC_FOR_ITER): {
692
                    MARK_EXC_IP_SELECTIVE();
693
                    DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
694
                    code_state->sp = sp;
695
                    assert(TOP());
696
697
                    mp_obj_t value = mp_iternext_allow_raise(TOP());
                    if (value == MP_OBJ_STOP_ITERATION) {
698
                        --sp; // pop the exhausted iterator
699
                        ip += ulab; // jump to after for-block
700
                    } else {
701
                        PUSH(value); // push the next iteration value
702
703
                    }
                    DISPATCH();
704
                }
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732

                // matched against: SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH
                ENTRY(MP_BC_POP_BLOCK):
                    // we are exiting an exception handler, so pop the last one of the exception-stack
                    assert(exc_sp >= exc_stack);
                    POP_EXC_BLOCK();
                    DISPATCH();

                // matched against: SETUP_EXCEPT
                ENTRY(MP_BC_POP_EXCEPT):
                    // TODO need to work out how blocks work etc
                    // pops block, checks it's an exception block, and restores the stack, saving the 3 exception values to local threadstate
                    assert(exc_sp >= exc_stack);
                    assert(currently_in_except_block);
                    //sp = (mp_obj_t*)(*exc_sp--);
                    //exc_sp--; // discard ip
                    POP_EXC_BLOCK();
                    //sp -= 3; // pop 3 exception values
                    DISPATCH();

                ENTRY(MP_BC_NOT):
                    if (TOP() == mp_const_true) {
                        SET_TOP(mp_const_false);
                    } else {
                        SET_TOP(mp_const_true);
                    }
                    DISPATCH();

733
                ENTRY(MP_BC_BUILD_TUPLE): {
734
                    MARK_EXC_IP_SELECTIVE();
735
736
737
738
                    DECODE_UINT;
                    sp -= unum - 1;
                    SET_TOP(mp_obj_new_tuple(unum, sp));
                    DISPATCH();
739
                }
740

741
                ENTRY(MP_BC_BUILD_LIST): {
742
                    MARK_EXC_IP_SELECTIVE();
743
744
745
746
                    DECODE_UINT;
                    sp -= unum - 1;
                    SET_TOP(mp_obj_new_list(unum, sp));
                    DISPATCH();
747
                }
748

749
                ENTRY(MP_BC_LIST_APPEND): {
750
                    MARK_EXC_IP_SELECTIVE();
751
752
753
754
755
                    DECODE_UINT;
                    // I think it's guaranteed by the compiler that sp[unum] is a list
                    mp_obj_list_append(sp[-unum], sp[0]);
                    sp--;
                    DISPATCH();
756
                }
757

758
                ENTRY(MP_BC_BUILD_MAP): {
759
                    MARK_EXC_IP_SELECTIVE();
760
761
762
                    DECODE_UINT;
                    PUSH(mp_obj_new_dict(unum));
                    DISPATCH();
763
                }
764
765

                ENTRY(MP_BC_STORE_MAP):
766
                    MARK_EXC_IP_SELECTIVE();
767
768
769
770
                    sp -= 2;
                    mp_obj_dict_store(sp[0], sp[2], sp[1]);
                    DISPATCH();

771
                ENTRY(MP_BC_MAP_ADD): {
772
                    MARK_EXC_IP_SELECTIVE();
773
774
775
776
777
                    DECODE_UINT;
                    // I think it's guaranteed by the compiler that sp[-unum - 1] is a map
                    mp_obj_dict_store(sp[-unum - 1], sp[0], sp[-1]);
                    sp -= 2;
                    DISPATCH();
778
                }
779

780
#if MICROPY_PY_BUILTINS_SET
781
                ENTRY(MP_BC_BUILD_SET): {
782
                    MARK_EXC_IP_SELECTIVE();
783
784
785
786
                    DECODE_UINT;
                    sp -= unum - 1;
                    SET_TOP(mp_obj_new_set(unum, sp));
                    DISPATCH();
787
                }
788

789
                ENTRY(MP_BC_SET_ADD): {
790
                    MARK_EXC_IP_SELECTIVE();
791
792
793
794
795
                    DECODE_UINT;
                    // I think it's guaranteed by the compiler that sp[-unum] is a set
                    mp_obj_set_store(sp[-unum], sp[0]);
                    sp--;
                    DISPATCH();
796
                }
797
#endif
Damien's avatar
Damien committed
798

799
#if MICROPY_PY_BUILTINS_SLICE
800
                ENTRY(MP_BC_BUILD_SLICE): {
801
                    MARK_EXC_IP_SELECTIVE();
802
803
                    DECODE_UINT;
                    if (unum == 2) {
804
805
806
                        mp_obj_t stop = POP();
                        mp_obj_t start = TOP();
                        SET_TOP(mp_obj_new_slice(start, stop, mp_const_none));
807
                    } else {
808
809
810
811
                        mp_obj_t step = POP();
                        mp_obj_t stop = POP();
                        mp_obj_t start = TOP();
                        SET_TOP(mp_obj_new_slice(start, stop, step));
812
813
                    }
                    DISPATCH();
814
                }
815
#endif
816

817
                ENTRY(MP_BC_UNPACK_SEQUENCE): {
818
                    MARK_EXC_IP_SELECTIVE();
819
820
821
822
                    DECODE_UINT;
                    mp_unpack_sequence(sp[0], unum, sp);
                    sp += unum - 1;
                    DISPATCH();
823
                }
824

825
                ENTRY(MP_BC_UNPACK_EX): {
826
                    MARK_EXC_IP_SELECTIVE();
827
828
829
830
                    DECODE_UINT;
                    mp_unpack_ex(sp[0], unum, sp);
                    sp += (unum & 0xff) + ((unum >> 8) & 0xff);
                    DISPATCH();
831
                }
832

833
                ENTRY(MP_BC_MAKE_FUNCTION): {
834
                    DECODE_PTR;
835
                    PUSH(mp_make_function_from_raw_code(ptr, MP_OBJ_NULL, MP_OBJ_NULL));
836
                    DISPATCH();
837
                }
838

839
                ENTRY(MP_BC_MAKE_FUNCTION_DEFARGS): {
840
841
                    DECODE_PTR;
                    // Stack layout: def_tuple def_dict <- TOS
842
                    mp_obj_t def_dict = POP();
843
                    SET_TOP(mp_make_function_from_raw_code(ptr, TOP(), def_dict));
844
                    DISPATCH();
845
                }
846

847
                ENTRY(MP_BC_MAKE_CLOSURE): {
848
                    DECODE_PTR;
849
                    mp_uint_t n_closed_over = *ip++;
850
851
                    // Stack layout: closed_overs <- TOS
                    sp -= n_closed_over - 1;
852
                    SET_TOP(mp_make_closure_from_raw_code(ptr, n_closed_over, sp));
853
                    DISPATCH();
854
                }
855

856
                ENTRY(MP_BC_MAKE_CLOSURE_DEFARGS): {
857
                    DECODE_PTR;
858
                    mp_uint_t n_closed_over = *ip++;
859
860
                    // Stack layout: def_tuple def_dict closed_overs <- TOS
                    sp -= 2 + n_closed_over - 1;
861
                    SET_TOP(mp_make_closure_from_raw_code(ptr, 0x100 | n_closed_over, sp));
862
                    DISPATCH();
863
                }
864

865
                ENTRY(MP_BC_CALL_FUNCTION): {
866
                    MARK_EXC_IP_SELECTIVE();
867
868
869
870
                    DECODE_UINT;
                    // unum & 0xff == n_positional
                    // (unum >> 8) & 0xff == n_keyword
                    sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe);
871
872
873
874
875
876
877
878
879
880
881
882
                    #if MICROPY_STACKLESS
                    if (mp_obj_get_type(*sp) == &mp_type_fun_bc) {
                        code_state->ip = ip;
                        code_state->sp = sp;
                        code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block);
                        mp_code_state *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1);
                        new_state->prev = code_state;
                        code_state = new_state;
                        nlr_pop();
                        goto run_code_state;
                    }
                    #endif
883
884
                    SET_TOP(mp_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1));
                    DISPATCH();
885
                }
886

887
                ENTRY(MP_BC_CALL_FUNCTION_VAR_KW): {
888
                    MARK_EXC_IP_SELECTIVE();
889
890
891
892
893
894
895
896
                    DECODE_UINT;
                    // unum & 0xff == n_positional
                    // (unum >> 8) & 0xff == n_keyword
                    // We have folowing stack layout here: