runtime.c 33.7 KB
Newer Older
1
// in principle, rt_xxx functions are called only by vm/native/viper and make assumptions about args
2
// mp_xxx functions are safer and can be called by anyone
3
// note that rt_assign_xxx are called only from emit*, and maybe we can rename them to reflect this
4

Damien's avatar
Damien committed
5
6
7
8
9
10
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

11
#include "nlr.h"
Damien's avatar
Damien committed
12
#include "misc.h"
13
#include "mpconfig.h"
14
#include "mpqstr.h"
15
16
#include "obj.h"
#include "runtime0.h"
Damien's avatar
Damien committed
17
#include "runtime.h"
18
19
20
#include "map.h"
#include "builtin.h"

21
#if 0 // print debugging info
22
#define DEBUG_PRINT (1)
23
#define WRITE_CODE (1)
24
25
26
#define DEBUG_printf(args...) printf(args)
#define DEBUG_OP_printf(args...) printf(args)
#else // don't print debugging info
27
#define DEBUG_printf(args...) (void)0
Damien's avatar
Damien committed
28
#define DEBUG_OP_printf(args...) (void)0
29
#endif
Damien's avatar
Damien committed
30

31
// locals and globals need to be pointers because they can be the same in outer module scope
32
33
34
static mp_map_t *map_locals;
static mp_map_t *map_globals;
static mp_map_t map_builtins;
35

Damien's avatar
Damien committed
36
typedef enum {
37
38
39
40
41
42
43
44
    MP_CODE_NONE,
    MP_CODE_BYTE,
    MP_CODE_NATIVE,
    MP_CODE_INLINE_ASM,
} mp_code_kind_t;

typedef struct _mp_code_t {
    mp_code_kind_t kind;
Damien's avatar
Damien committed
45
    int n_args;
46
47
48
    int n_locals;
    int n_stack;
    bool is_generator;
Damien's avatar
Damien committed
49
50
51
52
53
    union {
        struct {
            byte *code;
            uint len;
        } u_byte;
54
        struct {
55
            mp_fun_t fun;
56
57
        } u_native;
        struct {
58
            void *fun;
59
        } u_inline_asm;
Damien's avatar
Damien committed
60
    };
61
} mp_code_t;
Damien's avatar
Damien committed
62
63

static int next_unique_code_id;
64
65
static machine_uint_t unique_codes_alloc = 0;
static mp_code_t *unique_codes = NULL;
Damien's avatar
Damien committed
66

67
68
#ifdef WRITE_CODE
FILE *fp_write_code = NULL;
69
#endif
Damien's avatar
Damien committed
70

71
72
73
74
75
// a good optimising compiler will inline this if necessary
static void mp_map_add_qstr(mp_map_t *map, qstr qstr, mp_obj_t value) {
    mp_map_lookup(map, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
}

76
void rt_init(void) {
77
    // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New())
78
79
    map_locals = map_globals = mp_map_new(1);
    mp_map_add_qstr(map_globals, MP_QSTR___name__, mp_obj_new_str(MP_QSTR___main__));
80

81
    // init built-in hash table
82
    mp_map_init(&map_builtins, 3);
83
84

    // built-in exceptions (TODO, make these proper classes)
85
86
87
88
89
90
91
92
    mp_map_add_qstr(&map_builtins, MP_QSTR_AttributeError, mp_obj_new_exception(MP_QSTR_AttributeError));
    mp_map_add_qstr(&map_builtins, MP_QSTR_IndexError, mp_obj_new_exception(MP_QSTR_IndexError));
    mp_map_add_qstr(&map_builtins, MP_QSTR_KeyError, mp_obj_new_exception(MP_QSTR_KeyError));
    mp_map_add_qstr(&map_builtins, MP_QSTR_NameError, mp_obj_new_exception(MP_QSTR_NameError));
    mp_map_add_qstr(&map_builtins, MP_QSTR_TypeError, mp_obj_new_exception(MP_QSTR_TypeError));
    mp_map_add_qstr(&map_builtins, MP_QSTR_SyntaxError, mp_obj_new_exception(MP_QSTR_SyntaxError));
    mp_map_add_qstr(&map_builtins, MP_QSTR_ValueError, mp_obj_new_exception(MP_QSTR_ValueError));
    mp_map_add_qstr(&map_builtins, MP_QSTR_OSError, mp_obj_new_exception(MP_QSTR_OSError));
93

Damien George's avatar
Damien George committed
94
    // built-in objects
95
    mp_map_add_qstr(&map_builtins, MP_QSTR_Ellipsis, mp_const_ellipsis);
Damien George's avatar
Damien George committed
96

97
    // built-in core functions
98
99
    mp_map_add_qstr(&map_builtins, MP_QSTR___build_class__, (mp_obj_t)&mp_builtin___build_class___obj);
    mp_map_add_qstr(&map_builtins, MP_QSTR___repl_print__, rt_make_function_1(mp_builtin___repl_print__));
100

101
    // built-in types
102
    mp_map_add_qstr(&map_builtins, MP_QSTR_bool, (mp_obj_t)&bool_type);
103
#if MICROPY_ENABLE_FLOAT
104
    mp_map_add_qstr(&map_builtins, MP_QSTR_complex, (mp_obj_t)&complex_type);
105
#endif
106
    mp_map_add_qstr(&map_builtins, MP_QSTR_dict, (mp_obj_t)&dict_type);
107
#if MICROPY_ENABLE_FLOAT
108
    mp_map_add_qstr(&map_builtins, MP_QSTR_float, (mp_obj_t)&float_type);
109
#endif
110
111
112
113
    mp_map_add_qstr(&map_builtins, MP_QSTR_int, (mp_obj_t)&int_type);
    mp_map_add_qstr(&map_builtins, MP_QSTR_list, (mp_obj_t)&list_type);
    mp_map_add_qstr(&map_builtins, MP_QSTR_set, (mp_obj_t)&set_type);
    mp_map_add_qstr(&map_builtins, MP_QSTR_tuple, (mp_obj_t)&tuple_type);
114
    mp_map_add_qstr(&map_builtins, MP_QSTR_type, (mp_obj_t)&mp_const_type);
115
116

    // built-in user functions; TODO covert all to &mp_builtin_xxx's
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    mp_map_add_qstr(&map_builtins, MP_QSTR_abs, rt_make_function_1(mp_builtin_abs));
    mp_map_add_qstr(&map_builtins, MP_QSTR_all, rt_make_function_1(mp_builtin_all));
    mp_map_add_qstr(&map_builtins, MP_QSTR_any, rt_make_function_1(mp_builtin_any));
    mp_map_add_qstr(&map_builtins, MP_QSTR_callable, rt_make_function_1(mp_builtin_callable));
    mp_map_add_qstr(&map_builtins, MP_QSTR_chr, rt_make_function_1(mp_builtin_chr));
    mp_map_add_qstr(&map_builtins, MP_QSTR_divmod, rt_make_function_2(mp_builtin_divmod));
    mp_map_add_qstr(&map_builtins, MP_QSTR_hash, (mp_obj_t)&mp_builtin_hash_obj);
    mp_map_add_qstr(&map_builtins, MP_QSTR_iter, (mp_obj_t)&mp_builtin_iter_obj);
    mp_map_add_qstr(&map_builtins, MP_QSTR_len, rt_make_function_1(mp_builtin_len));
    mp_map_add_qstr(&map_builtins, MP_QSTR_max, rt_make_function_var(1, mp_builtin_max));
    mp_map_add_qstr(&map_builtins, MP_QSTR_min, rt_make_function_var(1, mp_builtin_min));
    mp_map_add_qstr(&map_builtins, MP_QSTR_next, (mp_obj_t)&mp_builtin_next_obj);
    mp_map_add_qstr(&map_builtins, MP_QSTR_ord, rt_make_function_1(mp_builtin_ord));
    mp_map_add_qstr(&map_builtins, MP_QSTR_pow, rt_make_function_var(2, mp_builtin_pow));
    mp_map_add_qstr(&map_builtins, MP_QSTR_print, rt_make_function_var(0, mp_builtin_print));
    mp_map_add_qstr(&map_builtins, MP_QSTR_range, rt_make_function_var(1, mp_builtin_range));
    mp_map_add_qstr(&map_builtins, MP_QSTR_sum, rt_make_function_var(1, mp_builtin_sum));
134
135

    next_unique_code_id = 1; // 0 indicates "no code"
136
    unique_codes_alloc = 0;
Damien's avatar
Damien committed
137
138
    unique_codes = NULL;

139
140
#ifdef WRITE_CODE
    fp_write_code = fopen("out-code", "wb");
141
#endif
Damien's avatar
Damien committed
142
143
}

144
void rt_deinit(void) {
145
    m_del(mp_code_t, unique_codes, unique_codes_alloc);
146
147
148
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fclose(fp_write_code);
Damien's avatar
Damien committed
149
    }
150
#endif
Damien's avatar
Damien committed
151
152
}

153
154
int rt_get_unique_code_id(void) {
    return next_unique_code_id++;
Damien's avatar
Damien committed
155
156
}

157
static void alloc_unique_codes(void) {
158
159
160
161
    if (next_unique_code_id > unique_codes_alloc) {
        // increase size of unique_codes table
        unique_codes = m_renew(mp_code_t, unique_codes, unique_codes_alloc, next_unique_code_id);
        for (int i = unique_codes_alloc; i < next_unique_code_id; i++) {
162
            unique_codes[i].kind = MP_CODE_NONE;
163
        }
164
        unique_codes_alloc = next_unique_code_id;
Damien's avatar
Damien committed
165
    }
166
167
}

Damien George's avatar
Damien George committed
168
void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, bool is_generator) {
169
170
    alloc_unique_codes();

171
    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
172
    unique_codes[unique_code_id].kind = MP_CODE_BYTE;
173
    unique_codes[unique_code_id].n_args = n_args;
174
175
176
    unique_codes[unique_code_id].n_locals = n_locals;
    unique_codes[unique_code_id].n_stack = n_stack;
    unique_codes[unique_code_id].is_generator = is_generator;
177
178
179
    unique_codes[unique_code_id].u_byte.code = code;
    unique_codes[unique_code_id].u_byte.len = len;

Damien's avatar
Damien committed
180
    //printf("byte code: %d bytes\n", len);
181
182

#ifdef DEBUG_PRINT
183
    DEBUG_printf("assign byte code: id=%d code=%p len=%u n_args=%d\n", unique_code_id, code, len, n_args);
184
185
186
187
188
189
190
    for (int i = 0; i < 128 && i < len; i++) {
        if (i > 0 && i % 16 == 0) {
            DEBUG_printf("\n");
        }
        DEBUG_printf(" %02x", code[i]);
    }
    DEBUG_printf("\n");
191
#if MICROPY_SHOW_BC
192
193
    extern void mp_show_byte_code(const byte *code, int len);
    mp_show_byte_code(code, len);
194
#endif
195
196
197
198
199
200
201
202

#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fwrite(code, len, 1, fp_write_code);
        fflush(fp_write_code);
    }
#endif
#endif
203
204
}

205
void rt_assign_native_code(int unique_code_id, void *fun, uint len, int n_args) {
206
207
    alloc_unique_codes();

208
    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
209
    unique_codes[unique_code_id].kind = MP_CODE_NATIVE;
Damien's avatar
Damien committed
210
    unique_codes[unique_code_id].n_args = n_args;
211
212
213
    unique_codes[unique_code_id].n_locals = 0;
    unique_codes[unique_code_id].n_stack = 0;
    unique_codes[unique_code_id].is_generator = false;
Damien's avatar
Damien committed
214
215
    unique_codes[unique_code_id].u_native.fun = fun;

216
    //printf("native code: %d bytes\n", len);
217

218
#ifdef DEBUG_PRINT
Damien's avatar
Damien committed
219
220
221
222
223
224
225
226
227
228
    DEBUG_printf("assign native code: id=%d fun=%p len=%u n_args=%d\n", unique_code_id, fun, len, n_args);
    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
    for (int i = 0; i < 128 && i < len; i++) {
        if (i > 0 && i % 16 == 0) {
            DEBUG_printf("\n");
        }
        DEBUG_printf(" %02x", fun_data[i]);
    }
    DEBUG_printf("\n");

229
230
231
232
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fwrite(fun_data, len, 1, fp_write_code);
        fflush(fp_write_code);
Damien's avatar
Damien committed
233
    }
234
235
#endif
#endif
Damien's avatar
Damien committed
236
237
}

238
void rt_assign_inline_asm_code(int unique_code_id, void *fun, uint len, int n_args) {
239
240
    alloc_unique_codes();

241
    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE);
242
    unique_codes[unique_code_id].kind = MP_CODE_INLINE_ASM;
Damien's avatar
Damien committed
243
    unique_codes[unique_code_id].n_args = n_args;
244
245
246
    unique_codes[unique_code_id].n_locals = 0;
    unique_codes[unique_code_id].n_stack = 0;
    unique_codes[unique_code_id].is_generator = false;
247
    unique_codes[unique_code_id].u_inline_asm.fun = fun;
Damien's avatar
Damien committed
248

249
#ifdef DEBUG_PRINT
250
251
252
253
254
255
256
257
258
259
    DEBUG_printf("assign inline asm code: id=%d fun=%p len=%u n_args=%d\n", unique_code_id, fun, len, n_args);
    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
    for (int i = 0; i < 128 && i < len; i++) {
        if (i > 0 && i % 16 == 0) {
            DEBUG_printf("\n");
        }
        DEBUG_printf(" %02x", fun_data[i]);
    }
    DEBUG_printf("\n");

260
261
262
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fwrite(fun_data, len, 1, fp_write_code);
263
    }
264
265
#endif
#endif
Damien's avatar
Damien committed
266
267
}

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
static bool fit_small_int(mp_small_int_t o) {
    return true;
}

int rt_is_true(mp_obj_t arg) {
    DEBUG_OP_printf("is true %p\n", arg);
    if (MP_OBJ_IS_SMALL_INT(arg)) {
        if (MP_OBJ_SMALL_INT_VALUE(arg) == 0) {
            return 0;
        } else {
            return 1;
        }
    } else if (arg == mp_const_none) {
        return 0;
    } else if (arg == mp_const_false) {
        return 0;
    } else if (arg == mp_const_true) {
        return 1;
    } else {
        assert(0);
        return 0;
    }
}

mp_obj_t rt_list_append(mp_obj_t self_in, mp_obj_t arg) {
    return mp_obj_list_append(self_in, arg);
}

Damien's avatar
Damien committed
296
297
298
299
#define PARSE_DEC_IN_INTG (1)
#define PARSE_DEC_IN_FRAC (2)
#define PARSE_DEC_IN_EXP  (3)

300
mp_obj_t rt_load_const_dec(qstr qstr) {
Damien's avatar
Damien committed
301
302
303
304
#if MICROPY_ENABLE_FLOAT
    DEBUG_OP_printf("load '%s'\n", qstr_str(qstr));
    const char *s = qstr_str(qstr);
    int in = PARSE_DEC_IN_INTG;
305
    mp_float_t dec_val = 0;
Damien's avatar
Damien committed
306
307
308
309
310
311
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
    bool exp_neg = false;
    int exp_val = 0;
    int exp_extra = 0;
    bool imag = false;
    for (; *s; s++) {
        int dig = *s;
        if ('0' <= dig && dig <= '9') {
            dig -= '0';
            if (in == PARSE_DEC_IN_EXP) {
                exp_val = 10 * exp_val + dig;
            } else {
                dec_val = 10 * dec_val + dig;
                if (in == PARSE_DEC_IN_FRAC) {
                    exp_extra -= 1;
                }
            }
        } else if (in == PARSE_DEC_IN_INTG && dig == '.') {
            in = PARSE_DEC_IN_FRAC;
        } else if (in != PARSE_DEC_IN_EXP && (dig == 'E' || dig == 'e')) {
            in = PARSE_DEC_IN_EXP;
            if (s[1] == '+') {
                s++;
            } else if (s[1] == '-') {
                s++;
                exp_neg = true;
            }
        } else if (dig == 'J' || dig == 'j') {
            s++;
            imag = true;
            break;
        } else {
            // unknown character
            break;
        }
    }
    if (*s != 0) {
342
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_SyntaxError, "invalid syntax for number"));
Damien's avatar
Damien committed
343
344
345
346
347
348
349
350
351
352
353
354
    }
    if (exp_neg) {
        exp_val = -exp_val;
    }
    exp_val += exp_extra;
    for (; exp_val > 0; exp_val--) {
        dec_val *= 10;
    }
    for (; exp_val < 0; exp_val++) {
        dec_val *= 0.1;
    }
    if (imag) {
355
        return mp_obj_new_complex(0, dec_val);
Damien's avatar
Damien committed
356
    } else {
357
        return mp_obj_new_float(dec_val);
Damien's avatar
Damien committed
358
359
    }
#else
360
    nlr_jump(mp_obj_new_exception_msg(MP_QSTR_SyntaxError, "decimal numbers not supported"));
Damien's avatar
Damien committed
361
362
363
#endif
}

364
mp_obj_t rt_load_const_str(qstr qstr) {
Damien's avatar
Damien committed
365
    DEBUG_OP_printf("load '%s'\n", qstr_str(qstr));
366
    return mp_obj_new_str(qstr);
Damien's avatar
Damien committed
367
368
}

369
mp_obj_t rt_load_name(qstr qstr) {
Damien's avatar
Damien committed
370
    // logic: search locals, globals, builtins
371
    DEBUG_OP_printf("load name %s\n", qstr_str(qstr));
372
    mp_map_elem_t *elem = mp_map_lookup(map_locals, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP);
Damien's avatar
Damien committed
373
    if (elem == NULL) {
374
        elem = mp_map_lookup(map_globals, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP);
Damien's avatar
Damien committed
375
        if (elem == NULL) {
376
            elem = mp_map_lookup(&map_builtins, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP);
377
            if (elem == NULL) {
378
                nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_NameError, "name '%s' is not defined", qstr_str(qstr)));
379
            }
Damien's avatar
Damien committed
380
381
382
383
384
        }
    }
    return elem->value;
}

385
mp_obj_t rt_load_global(qstr qstr) {
386
387
    // logic: search globals, builtins
    DEBUG_OP_printf("load global %s\n", qstr_str(qstr));
388
    mp_map_elem_t *elem = mp_map_lookup(map_globals, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP);
389
    if (elem == NULL) {
390
        elem = mp_map_lookup(&map_builtins, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP);
391
        if (elem == NULL) {
392
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_NameError, "name '%s' is not defined", qstr_str(qstr)));
393
394
395
        }
    }
    return elem->value;
Damien's avatar
Damien committed
396
397
}

398
mp_obj_t rt_load_build_class(void) {
Damien's avatar
Damien committed
399
    DEBUG_OP_printf("load_build_class\n");
400
    mp_map_elem_t *elem = mp_map_lookup(&map_builtins, MP_OBJ_NEW_QSTR(MP_QSTR___build_class__), MP_MAP_LOOKUP);
Damien's avatar
Damien committed
401
    if (elem == NULL) {
402
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_NameError, "name '__build_class__' is not defined"));
Damien's avatar
Damien committed
403
404
405
406
    }
    return elem->value;
}

407
408
mp_obj_t rt_get_cell(mp_obj_t cell) {
    return mp_obj_cell_get(cell);
409
410
}

411
412
void rt_set_cell(mp_obj_t cell, mp_obj_t val) {
    mp_obj_cell_set(cell, val);
413
414
}

415
void rt_store_name(qstr qstr, mp_obj_t obj) {
416
    DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qstr), obj);
417
    mp_map_lookup(map_locals, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = obj;
418
419
}

420
void rt_store_global(qstr qstr, mp_obj_t obj) {
421
    DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qstr), obj);
422
    mp_map_lookup(map_globals, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = obj;
Damien's avatar
Damien committed
423
424
}

425
mp_obj_t rt_unary_op(int op, mp_obj_t arg) {
Damien's avatar
Damien committed
426
    DEBUG_OP_printf("unary %d %p\n", op, arg);
427
428
    if (MP_OBJ_IS_SMALL_INT(arg)) {
        mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(arg);
Damien's avatar
Damien committed
429
        switch (op) {
430
            case RT_UNARY_OP_NOT: if (val != 0) { return mp_const_true;} else { return mp_const_false; }
Damien's avatar
Damien committed
431
432
433
434
435
436
            case RT_UNARY_OP_POSITIVE: break;
            case RT_UNARY_OP_NEGATIVE: val = -val; break;
            case RT_UNARY_OP_INVERT: val = ~val; break;
            default: assert(0); val = 0;
        }
        if (fit_small_int(val)) {
437
438
439
440
441
442
443
444
445
446
447
448
449
            return MP_OBJ_NEW_SMALL_INT(val);
        } else {
            // TODO make a bignum
            assert(0);
            return mp_const_none;
        }
    } else { // will be an object (small ints are caught in previous if)
        mp_obj_base_t *o = arg;
        if (o->type->unary_op != NULL) {
            mp_obj_t result = o->type->unary_op(op, arg);
            if (result != NULL) {
                return result;
            }
Damien's avatar
Damien committed
450
        }
451
        // TODO specify in error message what the operator is
452
        nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "bad operand type for unary operator: '%s'", o->type->name));
Damien's avatar
Damien committed
453
    }
Damien's avatar
Damien committed
454
455
}

456
mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
Damien's avatar
Damien committed
457
    DEBUG_OP_printf("binary %d %p %p\n", op, lhs, rhs);
458
459
460
461
462
463
464
465
466
467

    // TODO correctly distinguish inplace operators for mutable objects
    // lookup logic that CPython uses for +=:
    //   check for implemented +=
    //   then check for implemented +
    //   then check for implemented seq.inplace_concat
    //   then check for implemented seq.concat
    //   then fail
    // note that list does not implement + or +=, so that inplace_concat is reached first for +=

468
    if (MP_OBJ_IS_SMALL_INT(lhs)) {
469
        mp_small_int_t lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs);
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
        if (MP_OBJ_IS_SMALL_INT(rhs)) {
            mp_small_int_t rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs);
            switch (op) {
                case RT_BINARY_OP_OR:
                case RT_BINARY_OP_INPLACE_OR: lhs_val |= rhs_val; break;
                case RT_BINARY_OP_XOR:
                case RT_BINARY_OP_INPLACE_XOR: lhs_val ^= rhs_val; break;
                case RT_BINARY_OP_AND:
                case RT_BINARY_OP_INPLACE_AND: lhs_val &= rhs_val; break;
                case RT_BINARY_OP_LSHIFT:
                case RT_BINARY_OP_INPLACE_LSHIFT: lhs_val <<= rhs_val; break;
                case RT_BINARY_OP_RSHIFT:
                case RT_BINARY_OP_INPLACE_RSHIFT: lhs_val >>= rhs_val; break;
                case RT_BINARY_OP_ADD:
                case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break;
                case RT_BINARY_OP_SUBTRACT:
                case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break;
                case RT_BINARY_OP_MULTIPLY:
                case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break;
                case RT_BINARY_OP_FLOOR_DIVIDE:
                case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: lhs_val /= rhs_val; break;
    #if MICROPY_ENABLE_FLOAT
                case RT_BINARY_OP_TRUE_DIVIDE:
                case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val);
    #endif

                // TODO implement modulo as specified by Python
                case RT_BINARY_OP_MODULO:
                case RT_BINARY_OP_INPLACE_MODULO: lhs_val %= rhs_val; break;

                // TODO check for negative power, and overflow
                case RT_BINARY_OP_POWER:
                case RT_BINARY_OP_INPLACE_POWER:
                {
                    int ans = 1;
                    while (rhs_val > 0) {
                        if (rhs_val & 1) {
                            ans *= lhs_val;
                        }
                        lhs_val *= lhs_val;
                        rhs_val /= 2;
511
                    }
512
513
                    lhs_val = ans;
                    break;
514
                }
515

516
517
518
519
520
521
522
523
524
                default: assert(0);
            }
            if (fit_small_int(lhs_val)) {
                return MP_OBJ_NEW_SMALL_INT(lhs_val);
            }
        } else if (MP_OBJ_IS_TYPE(rhs, &float_type)) {
            return mp_obj_float_binary_op(op, lhs_val, rhs);
        } else if (MP_OBJ_IS_TYPE(rhs, &complex_type)) {
            return mp_obj_complex_binary_op(op, lhs_val, 0, rhs);
525
526
527
528
529
530
531
        }
    } else if (MP_OBJ_IS_OBJ(lhs)) {
        mp_obj_base_t *o = lhs;
        if (o->type->binary_op != NULL) {
            mp_obj_t result = o->type->binary_op(op, lhs, rhs);
            if (result != NULL) {
                return result;
Damien's avatar
Damien committed
532
            }
Damien's avatar
Damien committed
533
534
        }
    }
535
536

    // TODO specify in error message what the operator is
537
    nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "unsupported operand type for binary operator: '%s'", mp_obj_get_type_str(lhs)));
Damien's avatar
Damien committed
538
539
}

540
mp_obj_t rt_compare_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
Damien's avatar
Damien committed
541
    DEBUG_OP_printf("compare %d %p %p\n", op, lhs, rhs);
542
543
544

    // deal with == and !=
    if (op == RT_COMPARE_OP_EQUAL || op == RT_COMPARE_OP_NOT_EQUAL) {
545
        if (mp_obj_equal(lhs, rhs)) {
546
            if (op == RT_COMPARE_OP_EQUAL) {
547
                return mp_const_true;
548
            } else {
549
                return mp_const_false;
550
551
552
            }
        } else {
            if (op == RT_COMPARE_OP_EQUAL) {
553
                return mp_const_false;
554
            } else {
555
                return mp_const_true;
556
557
558
559
            }
        }
    }

560
561
562
563
564
565
566
567
568
569
570
571
    // deal with exception_match
    if (op == RT_COMPARE_OP_EXCEPTION_MATCH) {
        // TODO properly! at the moment it just compares the exception identifier for equality
        if (MP_OBJ_IS_TYPE(lhs, &exception_type) && MP_OBJ_IS_TYPE(rhs, &exception_type)) {
            if (mp_obj_exception_get_type(lhs) == mp_obj_exception_get_type(rhs)) {
                return mp_const_true;
            } else {
                return mp_const_false;
            }
        }
    }

572
    // deal with small ints
573
574
575
    if (MP_OBJ_IS_SMALL_INT(lhs) && MP_OBJ_IS_SMALL_INT(rhs)) {
        mp_small_int_t lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs);
        mp_small_int_t rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs);
576
577
578
579
580
581
582
583
584
        int cmp;
        switch (op) {
            case RT_COMPARE_OP_LESS: cmp = lhs_val < rhs_val; break;
            case RT_COMPARE_OP_MORE: cmp = lhs_val > rhs_val; break;
            case RT_COMPARE_OP_LESS_EQUAL: cmp = lhs_val <= rhs_val; break;
            case RT_COMPARE_OP_MORE_EQUAL: cmp = lhs_val >= rhs_val; break;
            default: assert(0); cmp = 0;
        }
        if (cmp) {
585
            return mp_const_true;
586
        } else {
587
            return mp_const_false;
588
589
590
591
592
        }
    }

#if MICROPY_ENABLE_FLOAT
    // deal with floats
593
594
595
    if (MP_OBJ_IS_TYPE(lhs, &float_type) || MP_OBJ_IS_TYPE(rhs, &float_type)) {
        mp_float_t lhs_val = mp_obj_get_float(lhs);
        mp_float_t rhs_val = mp_obj_get_float(rhs);
Damien's avatar
Damien committed
596
597
        int cmp;
        switch (op) {
598
599
600
601
            case RT_COMPARE_OP_LESS: cmp = lhs_val < rhs_val; break;
            case RT_COMPARE_OP_MORE: cmp = lhs_val > rhs_val; break;
            case RT_COMPARE_OP_LESS_EQUAL: cmp = lhs_val <= rhs_val; break;
            case RT_COMPARE_OP_MORE_EQUAL: cmp = lhs_val >= rhs_val; break;
Damien's avatar
Damien committed
602
603
604
            default: assert(0); cmp = 0;
        }
        if (cmp) {
605
            return mp_const_true;
Damien's avatar
Damien committed
606
        } else {
607
            return mp_const_false;
Damien's avatar
Damien committed
608
609
        }
    }
610
#endif
611
612

    // not implemented
Damien's avatar
Damien committed
613
    assert(0);
614
    return mp_const_none;
Damien's avatar
Damien committed
615
616
}

617
mp_obj_t rt_make_function_from_id(int unique_code_id) {
618
619
    DEBUG_OP_printf("make_function_from_id %d\n", unique_code_id);
    if (unique_code_id < 1 || unique_code_id >= next_unique_code_id) {
Damien's avatar
Damien committed
620
        // illegal code id
621
        return mp_const_none;
Damien's avatar
Damien committed
622
    }
623
624
625
626

    // make the function, depending on the code kind
    mp_code_t *c = &unique_codes[unique_code_id];
    mp_obj_t fun;
Damien's avatar
Damien committed
627
    switch (c->kind) {
628
        case MP_CODE_BYTE:
Damien George's avatar
Damien George committed
629
            fun = mp_obj_new_fun_bc(c->n_args, c->n_locals + c->n_stack, c->u_byte.code);
630
            break;
631
        case MP_CODE_NATIVE:
Damien's avatar
Damien committed
632
            switch (c->n_args) {
633
634
635
636
                case 0: fun = rt_make_function_0(c->u_native.fun); break;
                case 1: fun = rt_make_function_1((mp_fun_1_t)c->u_native.fun); break;
                case 2: fun = rt_make_function_2((mp_fun_2_t)c->u_native.fun); break;
                default: assert(0); fun = mp_const_none;
Damien's avatar
Damien committed
637
638
            }
            break;
639
640
        case MP_CODE_INLINE_ASM:
            fun = mp_obj_new_fun_asm(c->n_args, c->u_inline_asm.fun);
Damien's avatar
Damien committed
641
642
643
            break;
        default:
            assert(0);
644
            fun = mp_const_none;
Damien's avatar
Damien committed
645
    }
646
647
648

    // check for generator functions and if so wrap in generator object
    if (c->is_generator) {
Damien George's avatar
Damien George committed
649
        fun = mp_obj_new_gen_wrap(c->n_locals, c->n_stack, fun);
650
651
    }

652
    return fun;
Damien's avatar
Damien committed
653
654
}

655
mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple) {
Damien George's avatar
Damien George committed
656
    DEBUG_OP_printf("make_closure_from_id %d\n", unique_code_id);
657
658
    // make function object
    mp_obj_t ffun = rt_make_function_from_id(unique_code_id);
Damien's avatar
Damien committed
659
    // wrap function in closure object
660
    return mp_obj_new_closure(ffun, closure_tuple);
Damien's avatar
Damien committed
661
662
}

663
mp_obj_t rt_call_function_0(mp_obj_t fun) {
664
665
666
    return rt_call_function_n(fun, 0, NULL);
}

667
mp_obj_t rt_call_function_1(mp_obj_t fun, mp_obj_t arg) {
668
669
670
    return rt_call_function_n(fun, 1, &arg);
}

671
672
mp_obj_t rt_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
    mp_obj_t args[2];
673
674
675
676
677
678
    args[1] = arg1;
    args[0] = arg2;
    return rt_call_function_n(fun, 2, args);
}

// args are in reverse order in the array
679
680
681
mp_obj_t rt_call_function_n(mp_obj_t fun_in, int n_args, const mp_obj_t *args) {
    // TODO improve this: fun object can specify its type and we parse here the arguments,
    // passing to the function arrays of fixed and keyword arguments
682

683
    DEBUG_OP_printf("calling function %p(n_args=%d, args=%p)\n", fun_in, n_args, args);
684

685
    if (MP_OBJ_IS_SMALL_INT(fun_in)) {
686
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not callable"));
687
688
689
690
    } else {
        mp_obj_base_t *fun = fun_in;
        if (fun->type->call_n != NULL) {
            return fun->type->call_n(fun_in, n_args, args);
691
        } else {
692
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not callable", fun->type->name));
693
        }
Damien's avatar
Damien committed
694
695
696
    }
}

697
698
// args are in reverse order in the array; keyword arguments come first, value then key
// eg: (value1, key1, value0, key0, arg1, arg0)
699
700
701
702
703
704
705
706
707
708
709
mp_obj_t rt_call_function_n_kw(mp_obj_t fun_in, uint n_args, uint n_kw, const mp_obj_t *args) {
    // TODO merge this and _n into a single, smarter thing
    DEBUG_OP_printf("calling function %p(n_args=%d, n_kw=%d, args=%p)\n", fun_in, n_args, n_kw, args);

    if (MP_OBJ_IS_SMALL_INT(fun_in)) {
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not callable"));
    } else {
        mp_obj_base_t *fun = fun_in;
        if (fun->type->call_n_kw != NULL) {
            return fun->type->call_n_kw(fun_in, n_args, n_kw, args);
        } else {
710
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not callable", fun->type->name));
711
712
        }
    }
713
714
}

715
716
// args contains: arg(n_args-1)  arg(n_args-2)  ...  arg(0)  self/NULL  fun
// if n_args==0 then there are only self/NULL and fun
717
mp_obj_t rt_call_method_n(uint n_args, const mp_obj_t *args) {
718
    DEBUG_OP_printf("call method %p(self=%p, n_args=%u)\n", args[n_args + 1], args[n_args], n_args);
719
720
721
    return rt_call_function_n(args[n_args + 1], n_args + ((args[n_args] == NULL) ? 0 : 1), args);
}

722
// args contains: kw_val(n_kw-1)  kw_key(n_kw-1) ... kw_val(0)  kw_key(0)  arg(n_args-1)  arg(n_args-2)  ...  arg(0)  self/NULL  fun
723
mp_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args) {
724
725
726
727
728
    uint n = n_args + 2 * n_kw;
    DEBUG_OP_printf("call method %p(self=%p, n_args=%u, n_kw=%u)\n", args[n + 1], args[n], n_args, n_kw);
    return rt_call_function_n_kw(args[n + 1], n_args + ((args[n] == NULL) ? 0 : 1), n_kw, args);
}

729
// items are in reverse order
730
731
mp_obj_t rt_build_tuple(int n_args, mp_obj_t *items) {
    return mp_obj_new_tuple_reverse(n_args, items);
732
733
}

Damien's avatar
Damien committed
734
// items are in reverse order
735
736
mp_obj_t rt_build_list(int n_args, mp_obj_t *items) {
    return mp_obj_new_list_reverse(n_args, items);
Damien's avatar
Damien committed
737
738
}

739
740
mp_obj_t rt_build_set(int n_args, mp_obj_t *items) {
    return mp_obj_new_set(n_args, items);
Damien's avatar
Damien committed
741
742
}

743
mp_obj_t rt_store_set(mp_obj_t set, mp_obj_t item) {
744
    mp_obj_set_store(set, item);
Damien's avatar
Damien committed
745
746
747
    return set;
}

748
// unpacked items are stored in order into the array pointed to by items
749
750
751
752
753
754
755
756
void rt_unpack_sequence(mp_obj_t seq_in, uint num, mp_obj_t *items) {
    if (MP_OBJ_IS_TYPE(seq_in, &tuple_type) || MP_OBJ_IS_TYPE(seq_in, &list_type)) {
        uint seq_len;
        mp_obj_t *seq_items;
        if (MP_OBJ_IS_TYPE(seq_in, &tuple_type)) {
            mp_obj_tuple_get(seq_in, &seq_len, &seq_items);
        } else {
            mp_obj_list_get(seq_in, &seq_len, &seq_items);
757
        }
758
        if (seq_len < num) {
759
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_ValueError, "need more than %d values to unpack", (void*)(machine_uint_t)seq_len));
760
        } else if (seq_len > num) {
761
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_ValueError, "too many values to unpack (expected %d)", (void*)(machine_uint_t)num));
762
763
        }
        memcpy(items, seq_items, num * sizeof(mp_obj_t));
764
765
    } else {
        // TODO call rt_getiter and extract via rt_iternext
766
        nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not iterable", mp_obj_get_type_str(seq_in)));
767
768
769
    }
}

770
771
mp_obj_t rt_build_map(int n_args) {
    return mp_obj_new_dict(n_args);
Damien's avatar
Damien committed
772
773
}

774
775
776
mp_obj_t rt_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value) {
    // map should always be a dict
    return mp_obj_dict_store(map, key, value);
Damien's avatar
Damien committed
777
778
}

779
mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
780
781
782
783
784
785
786
787
788
789
    DEBUG_OP_printf("load attr %p.%s\n", base, qstr_str(attr));
    // use load_method
    mp_obj_t dest[2];
    rt_load_method(base, attr, dest);
    if (dest[0] == NULL) {
        // load_method returned just a normal attribute
        return dest[1];
    } else {
        // load_method returned a method, so build a bound method object
        return mp_obj_new_bound_meth(dest[0], dest[1]);
Damien's avatar
Damien committed
790
791
792
    }
}

793
void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
    DEBUG_OP_printf("load method %p.%s\n", base, qstr_str(attr));

    // clear output to indicate no attribute/method found yet
    dest[0] = MP_OBJ_NULL;
    dest[1] = MP_OBJ_NULL;

    // get the type
    mp_obj_type_t *type = mp_obj_get_type(base);

    // if this type can do its own load, then call it
    if (type->load_attr != NULL) {
        type->load_attr(base, attr, dest);
    }

    // if nothing found yet, look for built-in and generic names
    if (dest[1] == NULL) {
        if (attr == MP_QSTR___next__ && type->iternext != NULL) {
            dest[1] = (mp_obj_t)&mp_builtin_next_obj;
            dest[0] = base;
        } else {
            // generic method lookup
            const mp_method_t *meth = type->methods;
            if (meth != NULL) {
                for (; meth->name != NULL; meth++) {
                    if (strcmp(meth->name, qstr_str(attr)) == 0) {
                        dest[1] = (mp_obj_t)meth->fun;
                        dest[0] = base;
                        break;
                    }
823
                }
Damien's avatar
Damien committed
824
825
            }
        }
826
827
    }

828
829
830
831
832
833
834
835
836
    if (dest[1] == NULL) {
        // no attribute/method called attr
        // following CPython, we give a more detailed error message for type objects
        if (MP_OBJ_IS_TYPE(base, &mp_const_type)) {
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_AttributeError, "type object '%s' has no attribute '%s'", ((mp_obj_type_t*)base)->name, qstr_str(attr)));
        } else {
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr)));
        }
    }
837
838
}

839
void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) {
Damien's avatar
Damien committed
840
    DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value);
841
842
843
844
845
    mp_obj_type_t *type = mp_obj_get_type(base);
    if (type->store_attr != NULL) {
        if (type->store_attr(base, attr, value)) {
            return;
        }
846
    }
847
    nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr)));
848
849
}

850
void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
Damien's avatar
Damien committed
851
    DEBUG_OP_printf("store subscr %p[%p] <- %p\n", base, index, value);
852
    if (MP_OBJ_IS_TYPE(base, &list_type)) {
853
        // list store
854
855
856
857
        mp_obj_list_store(base, index, value);
    } else if (MP_OBJ_IS_TYPE(base, &dict_type)) {
        // dict store
        mp_obj_dict_store(base, index, value);
Damien's avatar
Damien committed
858
    } else {
859
        assert(0);
Damien's avatar
Damien committed
860
861
862
    }
}

863
864
mp_obj_t rt_getiter(mp_obj_t o_in) {
    if (MP_OBJ_IS_SMALL_INT(o_in)) {
865
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not iterable"));
866
    } else {
867
868
869
        mp_obj_base_t *o = o_in;
        if (o->type->getiter != NULL) {
            return o->type->getiter(o_in);
870
        } else {
871
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not iterable", o->type->name));
872
        }
873
874
    }
}
875

876
877
mp_obj_t rt_iternext(mp_obj_t o_in) {
    if (MP_OBJ_IS_SMALL_INT(o_in)) {
878
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not an iterator"));
879
880
881
882
    } else {
        mp_obj_base_t *o = o_in;
        if (o->type->iternext != NULL) {
            return o->type->iternext(o_in);
883
        } else {
884
            nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not an iterator", o->type->name));
885
        }
886
887
888
    }
}

889
mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
890
    // build args array
891
892
893
894
    mp_obj_t args[5];
    args[0] = mp_obj_new_str(name);
    args[1] = mp_const_none; // TODO should be globals
    args[2] = mp_const_none; // TODO should be locals
895
896
897
898
    args[3] = fromlist;
    args[4] = level; // must be 0; we don't yet support other values

    // TODO lookup __import__ and call that instead of going straight to builtin implementation
899
    return mp_builtin___import__(5, args);
900
901
}

902
903
mp_obj_t rt_import_from(mp_obj_t module, qstr name) {
    mp_obj_t x = rt_load_attr(module, name);
904
905
906
907
908
909
910
911
    /* TODO convert AttributeError to ImportError
    if (fail) {
        (ImportError, "cannot import name %s", qstr_str(name), NULL)
    }
    */
    return x;
}

912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
mp_map_t *rt_locals_get(void) {
    return map_locals;
}

void rt_locals_set(mp_map_t *m) {
    DEBUG_OP_printf("rt_locals_set(%p)\n", m);
    map_locals = m;
}

mp_map_t *rt_globals_get(void) {
    return map_globals;
}

void rt_globals_set(mp_map_t *m) {
    DEBUG_OP_printf("rt_globals_set(%p)\n", m);
    map_globals = m;
}

930
// these must correspond to the respective enum
931
void *const rt_fun_table[RT_F_NUMBER_OF] = {
932
    rt_load_const_dec,
Damien's avatar
Damien committed
933
934
935
    rt_load_const_str,
    rt_load_name,
    rt_load_global,
936
    rt_load_build_class,
Damien's avatar
Damien committed
937
938
939
    rt_load_attr,
    rt_load_method,
    rt_store_name,
940
    rt_store_attr,
Damien's avatar
Damien committed
941
942
943
    rt_store_subscr,
    rt_is_true,
    rt_unary_op,
944
    rt_build_tuple,
Damien's avatar
Damien committed
945
    rt_build_list,
946
    rt_list_append,
Damien's avatar
Damien committed
947
948
949
    rt_build_map,
    rt_store_map,
    rt_build_set,
950
    rt_store_set,
Damien's avatar
Damien committed
951
    rt_make_function_from_id,
952
    rt_call_function_n,
953
    rt_call_method_n,
Damien's avatar
Damien committed
954
955
    rt_binary_op,
    rt_compare_op,
956
957
    rt_getiter,
    rt_iternext,
Damien's avatar
Damien committed
958
959
960
961
962
963
964
};

/*
void rt_f_vector(rt_fun_kind_t fun_kind) {
    (rt_f_table[fun_kind])();
}
*/