runtime.c 32.9 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
static mp_code_t *unique_codes;
Damien's avatar
Damien committed
65

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

70
void rt_init(void) {
71
    // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New())
72
    map_locals = map_globals = mp_map_new(MP_MAP_QSTR, 1);
73
    mp_qstr_map_lookup(map_globals, MP_QSTR___name__, true)->value = mp_obj_new_str(MP_QSTR___main__);
74

75
    // init built-in hash table
76
    mp_map_init(&map_builtins, MP_MAP_QSTR, 3);
77
78

    // built-in exceptions (TODO, make these proper classes)
79
80
81
82
83
84
85
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_AttributeError, true)->value = mp_obj_new_exception(MP_QSTR_AttributeError);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_IndexError, true)->value = mp_obj_new_exception(MP_QSTR_IndexError);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_KeyError, true)->value = mp_obj_new_exception(MP_QSTR_KeyError);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_NameError, true)->value = mp_obj_new_exception(MP_QSTR_NameError);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_TypeError, true)->value = mp_obj_new_exception(MP_QSTR_TypeError);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_SyntaxError, true)->value = mp_obj_new_exception(MP_QSTR_SyntaxError);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_ValueError, true)->value = mp_obj_new_exception(MP_QSTR_ValueError);
86
87

    // built-in core functions
88
89
    mp_qstr_map_lookup(&map_builtins, MP_QSTR___build_class__, true)->value = rt_make_function_2(mp_builtin___build_class__);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR___repl_print__, true)->value = rt_make_function_1(mp_builtin___repl_print__);
90
91

    // built-in user functions
92
93
94
95
96
97
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_abs, true)->value = rt_make_function_1(mp_builtin_abs);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_all, true)->value = rt_make_function_1(mp_builtin_all);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_any, true)->value = rt_make_function_1(mp_builtin_any);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_bool, true)->value = rt_make_function_var(0, mp_builtin_bool);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_callable, true)->value = rt_make_function_1(mp_builtin_callable);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_chr, true)->value = rt_make_function_1(mp_builtin_chr);
98
#if MICROPY_ENABLE_FLOAT
99
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_complex, true)->value = (mp_obj_t)&mp_builtin_complex_obj;
100
#endif
101
102
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_dict, true)->value = rt_make_function_0(mp_builtin_dict);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_divmod, true)->value = rt_make_function_2(mp_builtin_divmod);
103
#if MICROPY_ENABLE_FLOAT
104
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_float, true)->value = (mp_obj_t)&mp_builtin_float_obj;
105
#endif
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_hash, true)->value = (mp_obj_t)&mp_builtin_hash_obj;
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_int, true)->value = (mp_obj_t)&mp_builtin_int_obj;
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_iter, true)->value = (mp_obj_t)&mp_builtin_iter_obj;
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_len, true)->value = rt_make_function_1(mp_builtin_len);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_list, true)->value = rt_make_function_var(0, mp_builtin_list);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_max, true)->value = rt_make_function_var(1, mp_builtin_max);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_min, true)->value = rt_make_function_var(1, mp_builtin_min);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_next, true)->value = (mp_obj_t)&mp_builtin_next_obj;
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_ord, true)->value = rt_make_function_1(mp_builtin_ord);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_pow, true)->value = rt_make_function_var(2, mp_builtin_pow);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_print, true)->value = rt_make_function_var(0, mp_builtin_print);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_range, true)->value = rt_make_function_var(1, mp_builtin_range);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_set, true)->value = (mp_obj_t)&mp_builtin_set_obj;
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_sum, true)->value = rt_make_function_var(1, mp_builtin_sum);
    mp_qstr_map_lookup(&map_builtins, MP_QSTR_type, true)->value = (mp_obj_t)&mp_builtin_type_obj;

    next_unique_code_id = 1; // 0 indicates "no code"
Damien's avatar
Damien committed
123
124
    unique_codes = NULL;

125
126
#ifdef WRITE_CODE
    fp_write_code = fopen("out-code", "wb");
127
#endif
Damien's avatar
Damien committed
128
129
}

130
void rt_deinit(void) {
131
132
133
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fclose(fp_write_code);
Damien's avatar
Damien committed
134
    }
135
#endif
Damien's avatar
Damien committed
136
137
}

138
139
int rt_get_unique_code_id(void) {
    return next_unique_code_id++;
Damien's avatar
Damien committed
140
141
}

142
static void alloc_unique_codes(void) {
Damien's avatar
Damien committed
143
    if (unique_codes == NULL) {
144
        unique_codes = m_new(mp_code_t, next_unique_code_id + 10); // XXX hack until we fix the REPL allocation problem
145
        for (int i = 0; i < next_unique_code_id; i++) {
146
            unique_codes[i].kind = MP_CODE_NONE;
147
        }
Damien's avatar
Damien committed
148
    }
149
150
}

Damien George's avatar
Damien George committed
151
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) {
152
153
    alloc_unique_codes();

154
    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id);
155
    unique_codes[unique_code_id].kind = MP_CODE_BYTE;
156
    unique_codes[unique_code_id].n_args = n_args;
157
158
159
    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;
160
161
162
    unique_codes[unique_code_id].u_byte.code = code;
    unique_codes[unique_code_id].u_byte.len = len;

Damien's avatar
Damien committed
163
    //printf("byte code: %d bytes\n", len);
164
165

#ifdef DEBUG_PRINT
166
    DEBUG_printf("assign byte code: id=%d code=%p len=%u n_args=%d\n", unique_code_id, code, len, n_args);
167
168
169
170
171
172
173
    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");
174
175
    extern void mp_show_byte_code(const byte *code, int len);
    mp_show_byte_code(code, len);
176
177
178
179
180
181
182
183

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

186
void rt_assign_native_code(int unique_code_id, void *fun, uint len, int n_args) {
187
188
    alloc_unique_codes();

189
    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id);
190
    unique_codes[unique_code_id].kind = MP_CODE_NATIVE;
Damien's avatar
Damien committed
191
    unique_codes[unique_code_id].n_args = n_args;
192
193
194
    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
195
196
    unique_codes[unique_code_id].u_native.fun = fun;

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

199
#ifdef DEBUG_PRINT
Damien's avatar
Damien committed
200
201
202
203
204
205
206
207
208
209
    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");

210
211
212
213
#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
214
    }
215
216
#endif
#endif
Damien's avatar
Damien committed
217
218
}

219
void rt_assign_inline_asm_code(int unique_code_id, void *fun, uint len, int n_args) {
220
221
222
    alloc_unique_codes();

    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id);
223
    unique_codes[unique_code_id].kind = MP_CODE_INLINE_ASM;
Damien's avatar
Damien committed
224
    unique_codes[unique_code_id].n_args = n_args;
225
226
227
    unique_codes[unique_code_id].n_locals = 0;
    unique_codes[unique_code_id].n_stack = 0;
    unique_codes[unique_code_id].is_generator = false;
228
    unique_codes[unique_code_id].u_inline_asm.fun = fun;
Damien's avatar
Damien committed
229

230
#ifdef DEBUG_PRINT
231
232
233
234
235
236
237
238
239
240
    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");

241
242
243
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fwrite(fun_data, len, 1, fp_write_code);
244
    }
245
246
#endif
#endif
Damien's avatar
Damien committed
247
248
}

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
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
277
278
279
280
#define PARSE_DEC_IN_INTG (1)
#define PARSE_DEC_IN_FRAC (2)
#define PARSE_DEC_IN_EXP  (3)

281
mp_obj_t rt_load_const_dec(qstr qstr) {
Damien's avatar
Damien committed
282
283
284
285
#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;
286
    mp_float_t dec_val = 0;
Damien's avatar
Damien committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
    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) {
323
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_SyntaxError, "invalid syntax for number"));
Damien's avatar
Damien committed
324
325
326
327
328
329
330
331
332
333
334
335
    }
    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) {
336
        return mp_obj_new_complex(0, dec_val);
Damien's avatar
Damien committed
337
    } else {
338
        return mp_obj_new_float(dec_val);
Damien's avatar
Damien committed
339
340
    }
#else
341
    nlr_jump(mp_obj_new_exception_msg(MP_QSTR_SyntaxError, "decimal numbers not supported"));
Damien's avatar
Damien committed
342
343
344
#endif
}

345
mp_obj_t rt_load_const_str(qstr qstr) {
Damien's avatar
Damien committed
346
    DEBUG_OP_printf("load '%s'\n", qstr_str(qstr));
347
    return mp_obj_new_str(qstr);
Damien's avatar
Damien committed
348
349
}

350
mp_obj_t rt_load_name(qstr qstr) {
Damien's avatar
Damien committed
351
    // logic: search locals, globals, builtins
352
    DEBUG_OP_printf("load name %s\n", qstr_str(qstr));
353
    mp_map_elem_t *elem = mp_qstr_map_lookup(map_locals, qstr, false);
Damien's avatar
Damien committed
354
    if (elem == NULL) {
355
        elem = mp_qstr_map_lookup(map_globals, qstr, false);
Damien's avatar
Damien committed
356
        if (elem == NULL) {
357
            elem = mp_qstr_map_lookup(&map_builtins, qstr, false);
358
            if (elem == NULL) {
359
                nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_NameError, "name '%s' is not defined", qstr_str(qstr)));
360
            }
Damien's avatar
Damien committed
361
362
363
364
365
        }
    }
    return elem->value;
}

366
mp_obj_t rt_load_global(qstr qstr) {
367
368
    // logic: search globals, builtins
    DEBUG_OP_printf("load global %s\n", qstr_str(qstr));
369
    mp_map_elem_t *elem = mp_qstr_map_lookup(map_globals, qstr, false);
370
    if (elem == NULL) {
371
        elem = mp_qstr_map_lookup(&map_builtins, qstr, false);
372
        if (elem == NULL) {
373
            nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_NameError, "name '%s' is not defined", qstr_str(qstr)));
374
375
376
        }
    }
    return elem->value;
Damien's avatar
Damien committed
377
378
}

379
mp_obj_t rt_load_build_class(void) {
Damien's avatar
Damien committed
380
    DEBUG_OP_printf("load_build_class\n");
381
    mp_map_elem_t *elem = mp_qstr_map_lookup(&map_builtins, MP_QSTR___build_class__, false);
Damien's avatar
Damien committed
382
    if (elem == NULL) {
383
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_NameError, "name '__build_class__' is not defined"));
Damien's avatar
Damien committed
384
385
386
387
    }
    return elem->value;
}

388
389
mp_obj_t rt_get_cell(mp_obj_t cell) {
    return mp_obj_cell_get(cell);
390
391
}

392
393
void rt_set_cell(mp_obj_t cell, mp_obj_t val) {
    mp_obj_cell_set(cell, val);
394
395
}

396
void rt_store_name(qstr qstr, mp_obj_t obj) {
397
    DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qstr), obj);
398
    mp_qstr_map_lookup(map_locals, qstr, true)->value = obj;
399
400
}

401
void rt_store_global(qstr qstr, mp_obj_t obj) {
402
    DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qstr), obj);
403
    mp_qstr_map_lookup(map_globals, qstr, true)->value = obj;
Damien's avatar
Damien committed
404
405
}

406
mp_obj_t rt_unary_op(int op, mp_obj_t arg) {
Damien's avatar
Damien committed
407
    DEBUG_OP_printf("unary %d %p\n", op, arg);
408
409
    if (MP_OBJ_IS_SMALL_INT(arg)) {
        mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(arg);
Damien's avatar
Damien committed
410
        switch (op) {
411
            case RT_UNARY_OP_NOT: if (val != 0) { return mp_const_true;} else { return mp_const_false; }
Damien's avatar
Damien committed
412
413
414
415
416
417
            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)) {
418
419
420
421
422
423
424
425
426
427
428
429
430
            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
431
        }
432
        // TODO specify in error message what the operator is
433
        nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "bad operand type for unary operator: '%s'", o->type->name));
Damien's avatar
Damien committed
434
    }
Damien's avatar
Damien committed
435
436
}

437
mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
Damien's avatar
Damien committed
438
    DEBUG_OP_printf("binary %d %p %p\n", op, lhs, rhs);
439
440
441
442
443
444
445
446
447
448

    // 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 +=

449
450
451
    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);
Damien's avatar
Damien committed
452
        switch (op) {
453
            case RT_BINARY_OP_OR:
Damien's avatar
Damien committed
454
            case RT_BINARY_OP_INPLACE_OR: lhs_val |= rhs_val; break;
455
            case RT_BINARY_OP_XOR:
Damien's avatar
Damien committed
456
            case RT_BINARY_OP_INPLACE_XOR: lhs_val ^= rhs_val; break;
457
            case RT_BINARY_OP_AND:
Damien's avatar
Damien committed
458
            case RT_BINARY_OP_INPLACE_AND: lhs_val &= rhs_val; break;
459
            case RT_BINARY_OP_LSHIFT:
Damien's avatar
Damien committed
460
            case RT_BINARY_OP_INPLACE_LSHIFT: lhs_val <<= rhs_val; break;
461
            case RT_BINARY_OP_RSHIFT:
Damien's avatar
Damien committed
462
            case RT_BINARY_OP_INPLACE_RSHIFT: lhs_val >>= rhs_val; break;
Damien's avatar
Damien committed
463
            case RT_BINARY_OP_ADD:
Damien's avatar
Damien committed
464
            case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break;
465
            case RT_BINARY_OP_SUBTRACT:
Damien's avatar
Damien committed
466
            case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break;
467
            case RT_BINARY_OP_MULTIPLY:
Damien's avatar
Damien committed
468
            case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break;
469
            case RT_BINARY_OP_FLOOR_DIVIDE:
Damien's avatar
Damien committed
470
            case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: lhs_val /= rhs_val; break;
471
#if MICROPY_ENABLE_FLOAT
472
            case RT_BINARY_OP_TRUE_DIVIDE:
473
            case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val);
Damien's avatar
Damien committed
474
#endif
475
476
477
478
479
480

            // 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
481
482
            case RT_BINARY_OP_POWER:
            case RT_BINARY_OP_INPLACE_POWER:
483
484
485
486
487
488
489
490
            {
                int ans = 1;
                while (rhs_val > 0) {
                    if (rhs_val & 1) {
                        ans *= lhs_val;
                    }
                    lhs_val *= lhs_val;
                    rhs_val /= 2;
491
                }
492
493
494
495
                lhs_val = ans;
                break;
            }

496
            default: assert(0);
Damien's avatar
Damien committed
497
        }
Damien's avatar
Damien committed
498
        if (fit_small_int(lhs_val)) {
499
500
501
502
503
504
505
506
            return MP_OBJ_NEW_SMALL_INT(lhs_val);
        }
    } 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
507
            }
Damien's avatar
Damien committed
508
509
        }
    }
510
511

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

515
mp_obj_t rt_compare_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
Damien's avatar
Damien committed
516
    DEBUG_OP_printf("compare %d %p %p\n", op, lhs, rhs);
517
518
519

    // deal with == and !=
    if (op == RT_COMPARE_OP_EQUAL || op == RT_COMPARE_OP_NOT_EQUAL) {
520
        if (mp_obj_equal(lhs, rhs)) {
521
            if (op == RT_COMPARE_OP_EQUAL) {
522
                return mp_const_true;
523
            } else {
524
                return mp_const_false;
525
526
527
            }
        } else {
            if (op == RT_COMPARE_OP_EQUAL) {
528
                return mp_const_false;
529
            } else {
530
                return mp_const_true;
531
532
533
534
            }
        }
    }

535
536
537
538
539
540
541
542
543
544
545
546
    // 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;
            }
        }
    }

547
    // deal with small ints
548
549
550
    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);
551
552
553
554
555
556
557
558
559
        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) {
560
            return mp_const_true;
561
        } else {
562
            return mp_const_false;
563
564
565
566
567
        }
    }

#if MICROPY_ENABLE_FLOAT
    // deal with floats
568
569
570
    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
571
572
        int cmp;
        switch (op) {
573
574
575
576
            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
577
578
579
            default: assert(0); cmp = 0;
        }
        if (cmp) {
580
            return mp_const_true;
Damien's avatar
Damien committed
581
        } else {
582
            return mp_const_false;
Damien's avatar
Damien committed
583
584
        }
    }
585
#endif
586
587

    // not implemented
Damien's avatar
Damien committed
588
    assert(0);
589
    return mp_const_none;
Damien's avatar
Damien committed
590
591
}

592
mp_obj_t rt_make_function_from_id(int unique_code_id) {
593
594
    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
595
        // illegal code id
596
        return mp_const_none;
Damien's avatar
Damien committed
597
    }
598
599
600
601

    // 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
602
    switch (c->kind) {
603
        case MP_CODE_BYTE:
Damien George's avatar
Damien George committed
604
            fun = mp_obj_new_fun_bc(c->n_args, c->n_locals + c->n_stack, c->u_byte.code);
605
            break;
606
        case MP_CODE_NATIVE:
Damien's avatar
Damien committed
607
            switch (c->n_args) {
608
609
610
611
                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
612
613
            }
            break;
614
615
        case MP_CODE_INLINE_ASM:
            fun = mp_obj_new_fun_asm(c->n_args, c->u_inline_asm.fun);
Damien's avatar
Damien committed
616
617
618
            break;
        default:
            assert(0);
619
            fun = mp_const_none;
Damien's avatar
Damien committed
620
    }
621
622
623

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

627
    return fun;
Damien's avatar
Damien committed
628
629
}

630
mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple) {
Damien George's avatar
Damien George committed
631
    DEBUG_OP_printf("make_closure_from_id %d\n", unique_code_id);
632
633
    // make function object
    mp_obj_t ffun = rt_make_function_from_id(unique_code_id);
Damien's avatar
Damien committed
634
    // wrap function in closure object
635
    return mp_obj_new_closure(ffun, closure_tuple);
Damien's avatar
Damien committed
636
637
}

638
mp_obj_t rt_call_function_0(mp_obj_t fun) {
639
640
641
    return rt_call_function_n(fun, 0, NULL);
}

642
mp_obj_t rt_call_function_1(mp_obj_t fun, mp_obj_t arg) {
643
644
645
    return rt_call_function_n(fun, 1, &arg);
}

646
647
mp_obj_t rt_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
    mp_obj_t args[2];
648
649
650
651
652
653
    args[1] = arg1;
    args[0] = arg2;
    return rt_call_function_n(fun, 2, args);
}

// args are in reverse order in the array
654
655
656
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
657

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

660
    if (MP_OBJ_IS_SMALL_INT(fun_in)) {
661
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not callable"));
662
663
664
665
    } else {
        mp_obj_base_t *fun = fun_in;
        if (fun->type->call_n != NULL) {
            return fun->type->call_n(fun_in, n_args, args);
666
        } else {
667
            nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "'%s' object is not callable", fun->type->name));
668
        }
Damien's avatar
Damien committed
669
670
671
    }
}

672
673
// args are in reverse order in the array; keyword arguments come first, value then key
// eg: (value1, key1, value0, key0, arg1, arg0)
674
mp_obj_t rt_call_function_n_kw(mp_obj_t fun, uint n_args, uint n_kw, const mp_obj_t *args) {
675
676
    // TODO
    assert(0);
677
    return mp_const_none;
678
679
}

680
681
// 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
682
mp_obj_t rt_call_method_n(uint n_args, const mp_obj_t *args) {
683
    DEBUG_OP_printf("call method %p(self=%p, n_args=%u)\n", args[n_args + 1], args[n_args], n_args);
684
685
686
    return rt_call_function_n(args[n_args + 1], n_args + ((args[n_args] == NULL) ? 0 : 1), args);
}

687
// 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
688
mp_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args) {
689
690
691
692
693
    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);
}

694
// items are in reverse order
695
696
mp_obj_t rt_build_tuple(int n_args, mp_obj_t *items) {
    return mp_obj_new_tuple_reverse(n_args, items);
697
698
}

Damien's avatar
Damien committed
699
// items are in reverse order
700
701
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
702
703
}

704
705
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
706
707
}

708
mp_obj_t rt_store_set(mp_obj_t set, mp_obj_t item) {
709
    mp_obj_set_store(set, item);
Damien's avatar
Damien committed
710
711
712
    return set;
}

713
// unpacked items are stored in order into the array pointed to by items
714
715
716
717
718
719
720
721
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);
722
        }
723
        if (seq_len < num) {
724
            nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_ValueError, "need more than %d values to unpack", (void*)(machine_uint_t)seq_len));
725
        } else if (seq_len > num) {
726
            nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_ValueError, "too many values to unpack (expected %d)", (void*)(machine_uint_t)num));
727
728
        }
        memcpy(items, seq_items, num * sizeof(mp_obj_t));
729
730
    } else {
        // TODO call rt_getiter and extract via rt_iternext
731
        nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "'%s' object is not iterable", mp_obj_get_type_str(seq_in)));
732
733
734
    }
}

735
736
mp_obj_t rt_build_map(int n_args) {
    return mp_obj_new_dict(n_args);
Damien's avatar
Damien committed
737
738
}

739
740
741
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
742
743
}

744
mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
745
    DEBUG_OP_printf("load attr %s\n", qstr_str(attr));
746
747
    if (MP_OBJ_IS_TYPE(base, &class_type)) {
        mp_map_elem_t *elem = mp_qstr_map_lookup(mp_obj_class_get_locals(base), attr, false);
Damien's avatar
Damien committed
748
        if (elem == NULL) {
749
750
            // TODO what about generic method lookup?
            goto no_attr;
Damien's avatar
Damien committed
751
752
        }
        return elem->value;
753
754
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        return mp_obj_instance_load_attr(base, attr);
755
    } else if (MP_OBJ_IS_TYPE(base, &module_type)) {
756
        DEBUG_OP_printf("lookup module map %p\n", mp_obj_module_get_globals(base));
757
758
759
760
761
762
        mp_map_elem_t *elem = mp_qstr_map_lookup(mp_obj_module_get_globals(base), attr, false);
        if (elem == NULL) {
            // TODO what about generic method lookup?
            goto no_attr;
        }
        return elem->value;
763
764
765
766
767
768
769
    } else if (MP_OBJ_IS_OBJ(base)) {
        // generic method lookup
        mp_obj_base_t *o = base;
        const mp_method_t *meth = &o->type->methods[0];
        for (; meth->name != NULL; meth++) {
            if (strcmp(meth->name, qstr_str(attr)) == 0) {
                return mp_obj_new_bound_meth(base, (mp_obj_t)meth->fun);
770
771
            }
        }
Damien's avatar
Damien committed
772
    }
773
774

no_attr:
775
    nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr)));
Damien's avatar
Damien committed
776
777
}

778
void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
Damien's avatar
Damien committed
779
    DEBUG_OP_printf("load method %s\n", qstr_str(attr));
780
    if (MP_OBJ_IS_TYPE(base, &gen_instance_type) && attr == MP_QSTR___next__) {
781
        dest[1] = (mp_obj_t)&mp_builtin_next_obj;
782
783
        dest[0] = base;
        return;
784
785
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        mp_obj_instance_load_method(base, attr, dest);
786
        return;
787
788
789
790
    } else if (MP_OBJ_IS_OBJ(base)) {
        // generic method lookup
        mp_obj_base_t *o = base;
        const mp_method_t *meth = &o->type->methods[0];
Damien's avatar
Damien committed
791
792
        for (; meth->name != NULL; meth++) {
            if (strcmp(meth->name, qstr_str(attr)) == 0) {
793
                dest[1] = (mp_obj_t)meth->fun;
Damien's avatar
Damien committed
794
795
796
797
                dest[0] = base;
                return;
            }
        }
798
799
    }

800
    // no method; fallback to load_attr
801
802
803
804
    dest[1] = rt_load_attr(base, attr);
    dest[0] = NULL;
}

805
void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) {
Damien's avatar
Damien committed
806
    DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value);
807
    if (MP_OBJ_IS_TYPE(base, &class_type)) {
Damien's avatar
Damien committed
808
        // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation?
809
810
811
812
        mp_map_t *locals = mp_obj_class_get_locals(base);
        mp_qstr_map_lookup(locals, attr, true)->value = value;
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        mp_obj_instance_store_attr(base, attr, value);
813
814
815
816
    } else if (MP_OBJ_IS_TYPE(base, &module_type)) {
        // TODO CPython allows STORE_ATTR to a module, but is this the correct implementation?
        mp_map_t *globals = mp_obj_module_get_globals(base);
        mp_qstr_map_lookup(globals, attr, true)->value = value;
817
    } else {
818
        nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr)));
819
820
821
    }
}

822
void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
Damien's avatar
Damien committed
823
    DEBUG_OP_printf("store subscr %p[%p] <- %p\n", base, index, value);
824
    if (MP_OBJ_IS_TYPE(base, &list_type)) {
825
        // list store
826
827
828
829
        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
830
    } else {
831
        assert(0);
Damien's avatar
Damien committed
832
833
834
    }
}

835
836
mp_obj_t rt_getiter(mp_obj_t o_in) {
    if (MP_OBJ_IS_SMALL_INT(o_in)) {
837
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not iterable"));
838
    } else {
839
840
841
        mp_obj_base_t *o = o_in;
        if (o->type->getiter != NULL) {
            return o->type->getiter(o_in);
842
        } else {
843
            nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "'%s' object is not iterable", o->type->name));
844
        }
845
846
    }
}
847

848
849
mp_obj_t rt_iternext(mp_obj_t o_in) {
    if (MP_OBJ_IS_SMALL_INT(o_in)) {
850
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "? 'int' object is not iterable"));
851
852
853
854
    } else {
        mp_obj_base_t *o = o_in;
        if (o->type->iternext != NULL) {
            return o->type->iternext(o_in);
855
        } else {
856
            nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "? '%s' object is not iterable", o->type->name));
857
        }
858
859
860
    }
}

861
mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
862
    // build args array
863
864
865
866
    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
867
868
869
870
    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
871
    return mp_builtin___import__(5, args);
872
873
}

874
875
mp_obj_t rt_import_from(mp_obj_t module, qstr name) {
    mp_obj_t x = rt_load_attr(module, name);
876
877
878
879
880
881
882
883
    /* TODO convert AttributeError to ImportError
    if (fail) {
        (ImportError, "cannot import name %s", qstr_str(name), NULL)
    }
    */
    return x;
}

884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
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;
}

902
// these must correspond to the respective enum
903
void *const rt_fun_table[RT_F_NUMBER_OF] = {
904
    rt_load_const_dec,
Damien's avatar
Damien committed
905
906
907
    rt_load_const_str,
    rt_load_name,
    rt_load_global,
908
    rt_load_build_class,
Damien's avatar
Damien committed
909
910
911
    rt_load_attr,
    rt_load_method,
    rt_store_name,
912
    rt_store_attr,
Damien's avatar
Damien committed
913
914
915
    rt_store_subscr,
    rt_is_true,
    rt_unary_op,
916
    rt_build_tuple,
Damien's avatar
Damien committed
917
    rt_build_list,
918
    rt_list_append,
Damien's avatar
Damien committed
919
920
921
    rt_build_map,
    rt_store_map,
    rt_build_set,
922
    rt_store_set,
Damien's avatar
Damien committed
923
    rt_make_function_from_id,
924
    rt_call_function_n,
925
    rt_call_method_n,
Damien's avatar
Damien committed
926
927
    rt_binary_op,
    rt_compare_op,
928
929
    rt_getiter,
    rt_iternext,
Damien's avatar
Damien committed