objfun.c 15.6 KB
Newer Older
1
2
3
4
5
6
7
8
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>

#include "nlr.h"
#include "misc.h"
#include "mpconfig.h"
9
#include "qstr.h"
10
#include "obj.h"
11
#include "objtuple.h"
12
#include "map.h"
Damien George's avatar
Damien George committed
13
#include "runtime0.h"
14
15
16
#include "runtime.h"
#include "bc.h"

17
18
19
#if 0 // print debugging info
#define DEBUG_PRINT (1)
#else // don't print debugging info
20
#define DEBUG_printf(...) (void)0
21
22
#endif

23
24
25
26
27
/******************************************************************************/
/* native functions                                                           */

// mp_obj_fun_native_t defined in obj.h

28
STATIC void check_nargs(mp_obj_fun_native_t *self, int n_args, int n_kw) {
Dave Hylands's avatar
Dave Hylands committed
29
30
31
32
33
    rt_check_nargs(n_args, self->n_args_min, self->n_args_max, n_kw, self->is_kw);
}

void rt_check_nargs(int n_args, machine_uint_t n_args_min, machine_uint_t n_args_max, int n_kw, bool is_kw) {
    if (n_kw && !is_kw) {
34
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError,
35
36
37
                                          "function does not take keyword arguments"));
    }

Dave Hylands's avatar
Dave Hylands committed
38
39
    if (n_args_min == n_args_max) {
        if (n_args != n_args_min) {
40
            nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
41
                                                     "function takes %d positional arguments but %d were given",
Dave Hylands's avatar
Dave Hylands committed
42
                                                     n_args_min, n_args));
43
44
        }
    } else {
Dave Hylands's avatar
Dave Hylands committed
45
        if (n_args < n_args_min) {
46
            nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
47
                                                    "<fun name>() missing %d required positional arguments: <list of names of params>",
Dave Hylands's avatar
Dave Hylands committed
48
49
                                                    n_args_min - n_args));
        } else if (n_args > n_args_max) {
50
            nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
51
                                                     "<fun name> expected at most %d arguments, got %d",
Dave Hylands's avatar
Dave Hylands committed
52
                                                     n_args_max, n_args));
53
54
55
56
        }
    }
}

57
STATIC mp_obj_t fun_native_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
58
    assert(MP_OBJ_IS_TYPE(self_in, &fun_native_type));
59
    mp_obj_fun_native_t *self = self_in;
60

61
    // check number of arguments
62
63
    check_nargs(self, n_args, n_kw);

64
    if (self->is_kw) {
65
66
        // function allows keywords

67
68
69
        // we create a map directly from the given args array
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
70

71
        return ((mp_fun_kw_t)self->fun)(n_args, args, &kw_args);
72

73
    } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
74
75
76
77
78
79
80
81
82
83
84
        // function requires a fixed number of arguments

        // dispatch function call
        switch (self->n_args_min) {
            case 0:
                return ((mp_fun_0_t)self->fun)();

            case 1:
                return ((mp_fun_1_t)self->fun)(args[0]);

            case 2:
85
                return ((mp_fun_2_t)self->fun)(args[0], args[1]);
86

87
            case 3:
88
                return ((mp_fun_3_t)self->fun)(args[0], args[1], args[2]);
89

90
91
92
93
94
95
            default:
                assert(0);
                return mp_const_none;
        }

    } else {
96
        // function takes a variable number of arguments, but no keywords
97

98
        return ((mp_fun_var_t)self->fun)(n_args, args);
99
100
101
102
    }
}

const mp_obj_type_t fun_native_type = {
103
    { &mp_type_type },
104
    .name = MP_QSTR_function,
105
    .call = fun_native_call,
106
107
};

108
109
// fun must have the correct signature for n_args fixed arguments
mp_obj_t rt_make_function_n(int n_args, void *fun) {
110
111
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &fun_native_type;
112
    o->is_kw = false;
113
114
    o->n_args_min = n_args;
    o->n_args_max = n_args;
115
116
117
118
    o->fun = fun;
    return o;
}

119
120
121
mp_obj_t rt_make_function_var(int n_args_min, mp_fun_var_t fun) {
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &fun_native_type;
122
    o->is_kw = false;
123
    o->n_args_min = n_args_min;
124
    o->n_args_max = MP_OBJ_FUN_ARGS_MAX;
125
126
127
128
129
130
131
132
    o->fun = fun;
    return o;
}

// min and max are inclusive
mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &fun_native_type;
133
    o->is_kw = false;
134
135
136
137
138
139
140
141
142
143
144
    o->n_args_min = n_args_min;
    o->n_args_max = n_args_max;
    o->fun = fun;
    return o;
}

/******************************************************************************/
/* byte code functions                                                        */

typedef struct _mp_obj_fun_bc_t {
    mp_obj_base_t base;
145
    mp_map_t *globals;      // the context within which this function was defined
146
147
148
149
    machine_uint_t n_args : 15;         // number of arguments this function takes
    machine_uint_t n_def_args : 15;     // number of default arguments
    machine_uint_t takes_var_args : 1;  // set if this function takes variable args
    machine_uint_t takes_kw_args : 1;   // set if this function takes keyword args
150
151
    uint n_state;           // total state size for the executing function (incl args, locals, stack)
    const byte *bytecode;   // bytecode for the function
152
    qstr *args;             // argument names (needed to resolve positional args passed as keywords)
153
    mp_obj_t extra_args[];  // values of default args (if any), plus a slot at the end for var args and/or kw args (if it takes them)
154
155
} mp_obj_fun_bc_t;

156
157
158
159
160
161
162
163
164
165
void dump_args(const mp_obj_t *a, int sz) {
#if DEBUG_PRINT
    DEBUG_printf("%p: ", a);
    for (int i = 0; i < sz; i++) {
        DEBUG_printf("%p ", a[i]);
    }
    DEBUG_printf("\n");
#endif
}

166
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
167
168
    DEBUG_printf("Input: ");
    dump_args(args, n_args);
169
170
    mp_obj_fun_bc_t *self = self_in;

171
    const mp_obj_t *kwargs = args + n_args;
Damien George's avatar
Damien George committed
172
173
174
    mp_obj_t *extra_args = self->extra_args + self->n_def_args;
    uint n_extra_args = 0;

175

176
177
    // check positional arguments

Damien George's avatar
Damien George committed
178
179
180
181
182
183
184
185
186
    if (n_args > self->n_args) {
        // given more than enough arguments
        if (!self->takes_var_args) {
            goto arg_error;
        }
        // put extra arguments in varargs tuple
        *extra_args = mp_obj_new_tuple(n_args - self->n_args, args + self->n_args);
        n_extra_args = 1;
        n_args = self->n_args;
187
    } else {
Damien George's avatar
Damien George committed
188
        if (self->takes_var_args) {
189
            DEBUG_printf("passing empty tuple as *args\n");
Damien George's avatar
Damien George committed
190
191
192
            *extra_args = mp_const_empty_tuple;
            n_extra_args = 1;
        }
193
194
195
196
197
198
199
200
201
202
203
        // Apply processing and check below only if we don't have kwargs,
        // otherwise, kw handling code below has own extensive checks.
        if (n_kw == 0) {
            if (n_args >= self->n_args - self->n_def_args) {
                // given enough arguments, but may need to use some default arguments
                extra_args -= self->n_args - n_args;
                n_extra_args += self->n_args - n_args;
            } else {
                goto arg_error;
            }
        }
204
    }
Damien George's avatar
Damien George committed
205

206
207
    // check keyword arguments

208
    if (n_kw != 0) {
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
        // We cannot use dynamically-sized array here, because GCC indeed
        // deallocates it on leaving defining scope (unlike most static stack allocs).
        // So, we have 2 choices: allocate it unconditionally at the top of function
        // (wastes stack), or use alloca which is guaranteed to dealloc on func exit.
        //mp_obj_t flat_args[self->n_args];
        mp_obj_t *flat_args = alloca(self->n_args * sizeof(mp_obj_t));
        for (int i = self->n_args - 1; i >= 0; i--) {
            flat_args[i] = MP_OBJ_NULL;
        }
        memcpy(flat_args, args, sizeof(*args) * n_args);
        DEBUG_printf("Initial args: ");
        dump_args(flat_args, self->n_args);

        mp_obj_t dict = MP_OBJ_NULL;
        if (self->takes_kw_args) {
            dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
225
226
        }
        for (uint i = 0; i < n_kw; i++) {
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
            qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]);
            for (uint j = 0; j < self->n_args; j++) {
                if (arg_name == self->args[j]) {
                    if (flat_args[j] != MP_OBJ_NULL) {
                        nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
                            "function got multiple values for argument '%s'", qstr_str(arg_name)));
                    }
                    flat_args[j] = kwargs[2 * i + 1];
                    goto continue2;
                }
            }
            // Didn't find name match with positional args
            if (!self->takes_kw_args) {
                nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
            }
242
            mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
243
244
245
246
247
248
249
250
251
continue2:;
        }
        DEBUG_printf("Args with kws flattened: ");
        dump_args(flat_args, self->n_args);

        // Now fill in defaults
        mp_obj_t *d = &flat_args[self->n_args - 1];
        mp_obj_t *s = &self->extra_args[self->n_def_args - 1];
        for (int i = self->n_def_args; i > 0; i--) {
252
            if (*d == MP_OBJ_NULL) {
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
                *d-- = *s--;
            }
        }
        DEBUG_printf("Args after filling defaults: ");
        dump_args(flat_args, self->n_args);

        // Now check that all mandatory args specified
        while (d >= flat_args) {
            if (*d-- == MP_OBJ_NULL) {
                nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
                    "function missing required positional argument #%d", d - flat_args));
            }
        }

        args = flat_args;
        n_args = self->n_args;

        if (self->takes_kw_args) {
            extra_args[n_extra_args] = dict;
            n_extra_args += 1;
273
274
275
276
277
278
279
        }
    } else {
        // no keyword arguments given
        if (self->takes_kw_args) {
            extra_args[n_extra_args] = mp_obj_new_dict(0);
            n_extra_args += 1;
        }
280
    }
281

282
    mp_map_t *old_globals = rt_globals_get();
283
    rt_globals_set(self->globals);
284
    mp_obj_t result;
285
286
287
    DEBUG_printf("Calling: args=%p, n_args=%d, extra_args=%p, n_extra_args=%d\n", args, n_args, extra_args, n_extra_args);
    dump_args(args, n_args);
    dump_args(extra_args, n_extra_args);
Damien George's avatar
Damien George committed
288
    mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code(self->bytecode, args, n_args, extra_args, n_extra_args, self->n_state, &result);
289
290
    rt_globals_set(old_globals);

291
292
293
294
295
    if (vm_return_kind == MP_VM_RETURN_NORMAL) {
        return result;
    } else { // MP_VM_RETURN_EXCEPTION
        nlr_jump(result);
    }
Damien George's avatar
Damien George committed
296
297
298

arg_error:
    nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_args, n_args));
299
300
301
}

const mp_obj_type_t fun_bc_type = {
302
    { &mp_type_type },
303
    .name = MP_QSTR_function,
304
    .call = fun_bc_call,
305
306
};

307
mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t def_args_in, uint n_state, const byte *code) {
Damien George's avatar
Damien George committed
308
309
    uint n_def_args = 0;
    uint n_extra_args = 0;
310
311
312
    mp_obj_tuple_t *def_args = def_args_in;
    if (def_args != MP_OBJ_NULL) {
        n_def_args = def_args->len;
Damien George's avatar
Damien George committed
313
314
315
316
        n_extra_args = def_args->len;
    }
    if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
        n_extra_args += 1;
317
    }
318
319
320
    if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
        n_extra_args += 1;
    }
Damien George's avatar
Damien George committed
321
    mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args);
322
    o->base.type = &fun_bc_type;
323
    o->globals = rt_globals_get();
324
    o->args = args;
325
    o->n_args = n_args;
326
    o->n_def_args = n_def_args;
Damien George's avatar
Damien George committed
327
328
    o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0;
    o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0;
329
    o->n_state = n_state;
330
    o->bytecode = code;
331
    if (def_args != MP_OBJ_NULL) {
Damien George's avatar
Damien George committed
332
        memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
333
    }
334
335
336
    return o;
}

337
338
339
340
341
342
343
344
void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code) {
    assert(MP_OBJ_IS_TYPE(self_in, &fun_bc_type));
    mp_obj_fun_bc_t *self = self_in;
    *n_args = self->n_args;
    *n_state = self->n_state;
    *code = self->bytecode;
}

345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/******************************************************************************/
/* inline assembler functions                                                 */

typedef struct _mp_obj_fun_asm_t {
    mp_obj_base_t base;
    int n_args;
    void *fun;
} mp_obj_fun_asm_t;

typedef machine_uint_t (*inline_asm_fun_0_t)();
typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
typedef machine_uint_t (*inline_asm_fun_3_t)(machine_uint_t, machine_uint_t, machine_uint_t);

// convert a Micro Python object to a sensible value for inline asm
360
STATIC machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
361
362
363
364
365
366
367
368
369
    // TODO for byte_array, pass pointer to the array
    if (MP_OBJ_IS_SMALL_INT(obj)) {
        return MP_OBJ_SMALL_INT_VALUE(obj);
    } else if (obj == mp_const_none) {
        return 0;
    } else if (obj == mp_const_false) {
        return 0;
    } else if (obj == mp_const_true) {
        return 1;
370
    } else if (MP_OBJ_IS_STR(obj)) {
371
        // pointer to the string (it's probably constant though!)
372
373
        uint l;
        return (machine_uint_t)mp_obj_str_get_data(obj, &l);
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
#if MICROPY_ENABLE_FLOAT
    } else if (MP_OBJ_IS_TYPE(obj, &float_type)) {
        // convert float to int (could also pass in float registers)
        return (machine_int_t)mp_obj_float_get(obj);
#endif
    } else if (MP_OBJ_IS_TYPE(obj, &tuple_type)) {
        // pointer to start of tuple (could pass length, but then could use len(x) for that)
        uint len;
        mp_obj_t *items;
        mp_obj_tuple_get(obj, &len, &items);
        return (machine_uint_t)items;
    } else if (MP_OBJ_IS_TYPE(obj, &list_type)) {
        // pointer to start of list (could pass length, but then could use len(x) for that)
        uint len;
        mp_obj_t *items;
        mp_obj_list_get(obj, &len, &items);
        return (machine_uint_t)items;
    } else {
        // just pass along a pointer to the object
        return (machine_uint_t)obj;
    }
}

// convert a return value from inline asm to a sensible Micro Python object
398
STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
399
400
401
    return MP_OBJ_NEW_SMALL_INT(val);
}

402
STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
403
404
405
    mp_obj_fun_asm_t *self = self_in;

    if (n_args != self->n_args) {
406
        nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_args, n_args));
407
    }
408
    if (n_kw != 0) {
409
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
410
    }
411
412
413
414
415
416
417

    machine_uint_t ret;
    if (n_args == 0) {
        ret = ((inline_asm_fun_0_t)self->fun)();
    } else if (n_args == 1) {
        ret = ((inline_asm_fun_1_t)self->fun)(convert_obj_for_inline_asm(args[0]));
    } else if (n_args == 2) {
418
        ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]));
419
    } else if (n_args == 3) {
420
        ret = ((inline_asm_fun_3_t)self->fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]), convert_obj_for_inline_asm(args[2]));
421
422
423
424
425
426
427
428
    } else {
        assert(0);
        ret = 0;
    }

    return convert_val_from_inline_asm(ret);
}

429
STATIC const mp_obj_type_t fun_asm_type = {
430
    { &mp_type_type },
431
    .name = MP_QSTR_function,
432
    .call = fun_asm_call,
433
434
435
436
437
438
439
440
441
};

mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) {
    mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
    o->base.type = &fun_asm_type;
    o->n_args = n_args;
    o->fun = fun;
    return o;
}