objfun.c 16 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"
Paul Sokolovsky's avatar
Paul Sokolovsky committed
12
#include "objfun.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
29
30
31
32
33
34
35
36
37
STATIC mp_obj_t fun_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
    switch (op) {
        case MP_BINARY_OP_EQUAL:
            // These objects can be equal only if it's the same underlying structure,
            // we don't even need to check for 2nd arg type.
            return MP_BOOL(lhs_in == rhs_in);
    }
    return NULL;
}

38
STATIC mp_obj_t fun_native_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
39
    assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_native));
40
    mp_obj_fun_native_t *self = self_in;
41

42
    // check number of arguments
43
    mp_arg_check_num(n_args, n_kw, self->n_args_min, self->n_args_max, self->is_kw);
44

45
    if (self->is_kw) {
46
47
        // function allows keywords

48
49
50
        // 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);
51

52
        return ((mp_fun_kw_t)self->fun)(n_args, args, &kw_args);
53

54
    } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
55
56
57
58
59
60
61
62
63
64
65
        // 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:
66
                return ((mp_fun_2_t)self->fun)(args[0], args[1]);
67

68
            case 3:
69
                return ((mp_fun_3_t)self->fun)(args[0], args[1], args[2]);
70

71
72
73
74
75
76
            default:
                assert(0);
                return mp_const_none;
        }

    } else {
77
        // function takes a variable number of arguments, but no keywords
78

79
        return ((mp_fun_var_t)self->fun)(n_args, args);
80
81
82
    }
}

83
const mp_obj_type_t mp_type_fun_native = {
84
    { &mp_type_type },
85
    .name = MP_QSTR_function,
86
    .call = fun_native_call,
87
    .binary_op = fun_binary_op,
88
89
};

90
// fun must have the correct signature for n_args fixed arguments
Damien George's avatar
Damien George committed
91
mp_obj_t mp_make_function_n(int n_args, void *fun) {
92
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
93
    o->base.type = &mp_type_fun_native;
94
    o->is_kw = false;
95
96
    o->n_args_min = n_args;
    o->n_args_max = n_args;
97
98
99
100
    o->fun = fun;
    return o;
}

Damien George's avatar
Damien George committed
101
mp_obj_t mp_make_function_var(int n_args_min, mp_fun_var_t fun) {
102
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
103
    o->base.type = &mp_type_fun_native;
104
    o->is_kw = false;
105
    o->n_args_min = n_args_min;
106
    o->n_args_max = MP_OBJ_FUN_ARGS_MAX;
107
108
109
110
111
    o->fun = fun;
    return o;
}

// min and max are inclusive
Damien George's avatar
Damien George committed
112
mp_obj_t mp_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
113
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
114
    o->base.type = &mp_type_fun_native;
115
    o->is_kw = false;
116
117
118
119
120
121
122
123
124
    o->n_args_min = n_args_min;
    o->n_args_max = n_args_max;
    o->fun = fun;
    return o;
}

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

125
#if DEBUG_PRINT
126
STATIC void dump_args(const mp_obj_t *a, int sz) {
127
128
129
130
131
132
    DEBUG_printf("%p: ", a);
    for (int i = 0; i < sz; i++) {
        DEBUG_printf("%p ", a[i]);
    }
    DEBUG_printf("\n");
}
133
134
135
#else
#define dump_args(...) (void)0
#endif
136

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// If it's possible to call a function without allocating new argument array,
// this function returns true, together with pointers to 2 subarrays to be used
// as arguments. Otherwise, it returns false. It is expected that this fucntion
// will be accompanied by another, mp_obj_fun_prepare_full_args(), which will
// instead take pointer to full-length out-array, and will fill it in. Rationale
// being that a caller can try this function and if it succeeds, the function call
// can be made without allocating extra memory. Otherwise, caller can allocate memory
// and try "full" function. These functions are expected to be refactoring of
// code in fun_bc_call() and evenrually replace it.
bool mp_obj_fun_prepare_simple_args(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args,
                            uint *out_args1_len, const mp_obj_t **out_args1, uint *out_args2_len, const mp_obj_t **out_args2) {
    mp_obj_fun_bc_t *self = self_in;

    assert(n_kw == 0);
    assert(self->takes_var_args == 0);
    assert(self->takes_kw_args == 0);

    mp_obj_t *extra_args = self->extra_args + self->n_def_args;
    uint n_extra_args = 0;

    if (n_args > self->n_args) {
            goto arg_error;
    } else {
        extra_args -= self->n_args - n_args;
        n_extra_args += self->n_args - n_args;
    }
    *out_args1 = args;
    *out_args1_len = n_args;
    *out_args2 = extra_args;
    *out_args2_len = n_extra_args;
    return true;

arg_error:
170
    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_args, n_args));
171
172
}

173
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
174
175
    DEBUG_printf("Input n_args: %d, n_kw: %d\n", n_args, n_kw);
    DEBUG_printf("Input pos args: ");
176
    dump_args(args, n_args);
177
178
    DEBUG_printf("Input kw args: ");
    dump_args(args + n_args, n_kw * 2);
179
    mp_obj_fun_bc_t *self = self_in;
180
    DEBUG_printf("Func n_def_args: %d\n", self->n_def_args);
181

182
    const mp_obj_t *kwargs = args + n_args;
Damien George's avatar
Damien George committed
183
184
185
    mp_obj_t *extra_args = self->extra_args + self->n_def_args;
    uint n_extra_args = 0;

186

187
188
    // check positional arguments

Damien George's avatar
Damien George committed
189
190
191
    if (n_args > self->n_args) {
        // given more than enough arguments
        if (!self->takes_var_args) {
192
193
            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
                "function takes %d positional arguments but %d were given", self->n_args, n_args));
Damien George's avatar
Damien George committed
194
195
196
197
198
        }
        // 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;
199
    } else {
Damien George's avatar
Damien George committed
200
        if (self->takes_var_args) {
201
            DEBUG_printf("passing empty tuple as *args\n");
Damien George's avatar
Damien George committed
202
203
204
            *extra_args = mp_const_empty_tuple;
            n_extra_args = 1;
        }
205
206
207
208
209
210
211
212
        // 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 {
213
214
215
                nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
                    "function takes at least %d positional arguments but %d were given",
                    self->n_args - self->n_def_args, n_args));
216
217
            }
        }
218
    }
Damien George's avatar
Damien George committed
219

220
221
    // check keyword arguments

222
    if (n_kw != 0) {
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
        // 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?
239
240
        }
        for (uint i = 0; i < n_kw; i++) {
241
242
243
244
            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) {
245
                        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
246
247
248
249
250
251
252
253
                            "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) {
254
                nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
255
            }
256
            mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
257
258
259
260
261
262
263
264
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];
265
        for (int i = self->n_def_args; i > 0; i--, d--, s--) {
266
            if (*d == MP_OBJ_NULL) {
267
                *d = *s;
268
269
270
271
272
273
274
275
            }
        }
        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) {
276
                nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
277
278
279
280
281
282
283
284
285
286
                    "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;
287
288
289
290
291
292
293
        }
    } else {
        // no keyword arguments given
        if (self->takes_kw_args) {
            extra_args[n_extra_args] = mp_obj_new_dict(0);
            n_extra_args += 1;
        }
294
    }
295

296
    mp_obj_dict_t *old_globals = mp_globals_get();
Damien George's avatar
Damien George committed
297
    mp_globals_set(self->globals);
298
    mp_obj_t result;
299
300
301
    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);
302
    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
303
    mp_globals_set(old_globals);
304

305
306
307
    if (vm_return_kind == MP_VM_RETURN_NORMAL) {
        return result;
    } else { // MP_VM_RETURN_EXCEPTION
308
        nlr_raise(result);
309
    }
310
311
}

312
const mp_obj_type_t mp_type_fun_bc = {
313
    { &mp_type_type },
314
    .name = MP_QSTR_function,
315
    .call = fun_bc_call,
316
    .binary_op = fun_binary_op,
317
318
};

319
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
320
321
    uint n_def_args = 0;
    uint n_extra_args = 0;
322
323
    mp_obj_tuple_t *def_args = def_args_in;
    if (def_args != MP_OBJ_NULL) {
324
        assert(MP_OBJ_IS_TYPE(def_args, &mp_type_tuple));
325
        n_def_args = def_args->len;
Damien George's avatar
Damien George committed
326
327
328
329
        n_extra_args = def_args->len;
    }
    if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
        n_extra_args += 1;
330
    }
331
332
333
    if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
        n_extra_args += 1;
    }
Damien George's avatar
Damien George committed
334
    mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args);
335
    o->base.type = &mp_type_fun_bc;
Damien George's avatar
Damien George committed
336
    o->globals = mp_globals_get();
337
    o->args = args;
338
    o->n_args = n_args;
339
    o->n_def_args = n_def_args;
Damien George's avatar
Damien George committed
340
341
    o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0;
    o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0;
342
    o->bytecode = code;
343
    if (def_args != MP_OBJ_NULL) {
Damien George's avatar
Damien George committed
344
        memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
345
    }
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
    return o;
}

/******************************************************************************/
/* 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
364
STATIC machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
365
366
367
368
369
370
371
372
373
    // 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;
374
    } else if (MP_OBJ_IS_STR(obj)) {
375
        // pointer to the string (it's probably constant though!)
376
377
        uint l;
        return (machine_uint_t)mp_obj_str_get_data(obj, &l);
378
379
380
    } else {
        mp_obj_type_t *type = mp_obj_get_type(obj);
        if (0) {
381
#if MICROPY_ENABLE_FLOAT
382
383
384
        } else if (type == &mp_type_float) {
            // convert float to int (could also pass in float registers)
            return (machine_int_t)mp_obj_float_get(obj);
385
#endif
386
387
388
389
390
391
392
393
394
395
396
397
398
        } else if (type == &mp_type_tuple) {
            // 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 (type == &mp_type_list) {
            // 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 {
399
            mp_buffer_info_t bufinfo;
400
            if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_WRITE)) {
401
402
403
404
405
406
                // supports the buffer protocol, return a pointer to the data
                return (machine_uint_t)bufinfo.buf;
            } else {
                // just pass along a pointer to the object
                return (machine_uint_t)obj;
            }
407
        }
408
409
410
411
    }
}

// convert a return value from inline asm to a sensible Micro Python object
412
STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
413
414
415
    return MP_OBJ_NEW_SMALL_INT(val);
}

416
STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
417
418
419
    mp_obj_fun_asm_t *self = self_in;

    if (n_args != self->n_args) {
420
        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_args, n_args));
421
    }
422
    if (n_kw != 0) {
423
        nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
424
    }
425
426
427
428
429
430
431

    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) {
432
        ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]));
433
    } else if (n_args == 3) {
434
        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]));
435
436
437
438
439
440
441
442
    } else {
        assert(0);
        ret = 0;
    }

    return convert_val_from_inline_asm(ret);
}

443
STATIC const mp_obj_type_t mp_type_fun_asm = {
444
    { &mp_type_type },
445
    .name = MP_QSTR_function,
446
    .call = fun_asm_call,
447
    .binary_op = fun_binary_op,
448
449
450
451
};

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);
452
    o->base.type = &mp_type_fun_asm;
453
454
455
456
    o->n_args = n_args;
    o->fun = fun;
    return o;
}