objfun.c 15.4 KB
Newer Older
xbe's avatar
xbe committed
1
#include <stdbool.h>
2
3
4
5
6
7
8
#include <stdlib.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"
Damien George's avatar
Damien George committed
12
#include "runtime0.h"
13
14
15
#include "runtime.h"
#include "bc.h"

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

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

// mp_obj_fun_native_t defined in obj.h

27
STATIC void check_nargs(mp_obj_fun_native_t *self, int n_args, int n_kw) {
Damien George's avatar
Damien George committed
28
    mp_check_nargs(n_args, self->n_args_min, self->n_args_max, n_kw, self->is_kw);
Dave Hylands's avatar
Dave Hylands committed
29
30
}

Damien George's avatar
Damien George committed
31
void mp_check_nargs(int n_args, machine_uint_t n_args_min, machine_uint_t n_args_max, int n_kw, bool is_kw) {
Dave Hylands's avatar
Dave Hylands committed
32
    if (n_kw && !is_kw) {
33
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError,
34
35
36
                                          "function does not take keyword arguments"));
    }

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

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

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

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

66
67
68
        // 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);
69

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

72
    } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
73
74
75
76
77
78
79
80
81
82
83
        // 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:
84
                return ((mp_fun_2_t)self->fun)(args[0], args[1]);
85

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

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

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

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

101
const mp_obj_type_t mp_type_fun_native = {
102
    { &mp_type_type },
103
    .name = MP_QSTR_function,
104
    .call = fun_native_call,
105
106
};

107
// fun must have the correct signature for n_args fixed arguments
Damien George's avatar
Damien George committed
108
mp_obj_t mp_make_function_n(int n_args, void *fun) {
109
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
110
    o->base.type = &mp_type_fun_native;
111
    o->is_kw = false;
112
113
    o->n_args_min = n_args;
    o->n_args_max = n_args;
114
115
116
117
    o->fun = fun;
    return o;
}

Damien George's avatar
Damien George committed
118
mp_obj_t mp_make_function_var(int n_args_min, mp_fun_var_t fun) {
119
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
120
    o->base.type = &mp_type_fun_native;
121
    o->is_kw = false;
122
    o->n_args_min = n_args_min;
123
    o->n_args_max = MP_OBJ_FUN_ARGS_MAX;
124
125
126
127
128
    o->fun = fun;
    return o;
}

// min and max are inclusive
Damien George's avatar
Damien George committed
129
mp_obj_t mp_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
130
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
131
    o->base.type = &mp_type_fun_native;
132
    o->is_kw = false;
133
134
135
136
137
138
139
140
141
142
143
    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;
144
    mp_map_t *globals;      // the context within which this function was defined
145
146
147
148
    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
149
    const byte *bytecode;   // bytecode for the function
150
    qstr *args;             // argument names (needed to resolve positional args passed as keywords)
151
    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)
152
153
} mp_obj_fun_bc_t;

154
155
156
157
158
159
160
161
162
163
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
}

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

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

173

174
175
    // check positional arguments

Damien George's avatar
Damien George committed
176
177
178
179
180
181
182
183
184
    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;
185
    } else {
Damien George's avatar
Damien George committed
186
        if (self->takes_var_args) {
187
            DEBUG_printf("passing empty tuple as *args\n");
Damien George's avatar
Damien George committed
188
189
190
            *extra_args = mp_const_empty_tuple;
            n_extra_args = 1;
        }
191
192
193
194
195
196
197
198
199
200
201
        // 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;
            }
        }
202
    }
Damien George's avatar
Damien George committed
203

204
205
    // check keyword arguments

206
    if (n_kw != 0) {
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
        // 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?
223
224
        }
        for (uint i = 0; i < n_kw; i++) {
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
            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"));
            }
240
            mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
241
242
243
244
245
246
247
248
249
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--) {
250
            if (*d == MP_OBJ_NULL) {
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
                *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;
271
272
273
274
275
276
277
        }
    } else {
        // no keyword arguments given
        if (self->takes_kw_args) {
            extra_args[n_extra_args] = mp_obj_new_dict(0);
            n_extra_args += 1;
        }
278
    }
279

Damien George's avatar
Damien George committed
280
281
    mp_map_t *old_globals = mp_globals_get();
    mp_globals_set(self->globals);
282
    mp_obj_t result;
283
284
285
    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);
286
    mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code(self->bytecode, args, n_args, extra_args, n_extra_args, &result);
Damien George's avatar
Damien George committed
287
    mp_globals_set(old_globals);
288

289
290
291
292
293
    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
294
295
296

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

299
const mp_obj_type_t mp_type_fun_bc = {
300
    { &mp_type_type },
301
    .name = MP_QSTR_function,
302
    .call = fun_bc_call,
303
304
};

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

334
void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, const byte **code) {
335
    assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_bc));
336
337
338
339
340
    mp_obj_fun_bc_t *self = self_in;
    *n_args = self->n_args;
    *code = self->bytecode;
}

341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/******************************************************************************/
/* 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
356
STATIC machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
357
358
359
360
361
362
363
364
365
    // 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;
366
    } else if (MP_OBJ_IS_STR(obj)) {
367
        // pointer to the string (it's probably constant though!)
368
369
        uint l;
        return (machine_uint_t)mp_obj_str_get_data(obj, &l);
370
#if MICROPY_ENABLE_FLOAT
371
    } else if (MP_OBJ_IS_TYPE(obj, &mp_type_float)) {
372
373
374
        // convert float to int (could also pass in float registers)
        return (machine_int_t)mp_obj_float_get(obj);
#endif
375
    } else if (MP_OBJ_IS_TYPE(obj, &mp_type_tuple)) {
376
377
378
379
380
        // 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;
381
    } else if (MP_OBJ_IS_TYPE(obj, &mp_type_list)) {
382
383
384
385
386
387
388
389
390
391
392
393
        // 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
394
STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
395
396
397
    return MP_OBJ_NEW_SMALL_INT(val);
}

398
STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
399
400
401
    mp_obj_fun_asm_t *self = self_in;

    if (n_args != self->n_args) {
402
        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));
403
    }
404
    if (n_kw != 0) {
405
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
406
    }
407
408
409
410
411
412
413

    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) {
414
        ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]));
415
    } else if (n_args == 3) {
416
        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]));
417
418
419
420
421
422
423
424
    } else {
        assert(0);
        ret = 0;
    }

    return convert_val_from_inline_asm(ret);
}

425
STATIC const mp_obj_type_t mp_type_fun_asm = {
426
    { &mp_type_type },
427
    .name = MP_QSTR_function,
428
    .call = fun_asm_call,
429
430
431
432
};

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);
433
    o->base.type = &mp_type_fun_asm;
434
435
436
437
    o->n_args = n_args;
    o->fun = fun;
    return o;
}