objfun.c 11.5 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
17
18
19
20
21
#include "runtime.h"
#include "bc.h"

/******************************************************************************/
/* native functions                                                           */

// mp_obj_fun_native_t defined in obj.h

22
STATIC void check_nargs(mp_obj_fun_native_t *self, int n_args, int n_kw) {
23
    if (n_kw && !self->is_kw) {
24
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError,
25
26
27
28
29
                                          "function does not take keyword arguments"));
    }

    if (self->n_args_min == self->n_args_max) {
        if (n_args != self->n_args_min) {
30
            nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
31
                                                     "function takes %d positional arguments but %d were given",
32
                                                     self->n_args_min, n_args));
33
34
35
        }
    } else {
        if (n_args < self->n_args_min) {
36
            nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
37
                                                    "<fun name>() missing %d required positional arguments: <list of names of params>",
38
                                                    self->n_args_min - n_args));
39
        } else if (n_args > self->n_args_max) {
40
            nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
41
                                                     "<fun name> expected at most %d arguments, got %d",
42
                                                     self->n_args_max, n_args));
43
44
45
46
        }
    }
}

47
STATIC mp_obj_t fun_native_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
48
    assert(MP_OBJ_IS_TYPE(self_in, &fun_native_type));
49
    mp_obj_fun_native_t *self = self_in;
50

51
    // check number of arguments
52
53
    check_nargs(self, n_args, n_kw);

54
    if (self->is_kw) {
55
56
        // function allows keywords

57
58
59
        // 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);
60

61
        return ((mp_fun_kw_t)self->fun)(n_args, args, &kw_args);
62

63
    } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
64
65
66
67
68
69
70
71
72
73
74
        // 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:
75
                return ((mp_fun_2_t)self->fun)(args[0], args[1]);
76

77
            case 3:
78
                return ((mp_fun_3_t)self->fun)(args[0], args[1], args[2]);
79

80
81
82
83
84
85
            default:
                assert(0);
                return mp_const_none;
        }

    } else {
86
        // function takes a variable number of arguments, but no keywords
87

88
        return ((mp_fun_var_t)self->fun)(n_args, args);
89
90
91
92
    }
}

const mp_obj_type_t fun_native_type = {
93
    { &mp_type_type },
94
    .name = MP_QSTR_function,
95
    .call = fun_native_call,
96
97
};

98
99
// fun must have the correct signature for n_args fixed arguments
mp_obj_t rt_make_function_n(int n_args, void *fun) {
100
101
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &fun_native_type;
102
    o->is_kw = false;
103
104
    o->n_args_min = n_args;
    o->n_args_max = n_args;
105
106
107
108
    o->fun = fun;
    return o;
}

109
110
111
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;
112
    o->is_kw = false;
113
114
115
116
117
118
119
120
121
122
    o->n_args_min = n_args_min;
    o->n_args_max = ~((machine_uint_t)0);
    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;
123
    o->is_kw = false;
124
125
126
127
128
129
130
131
132
133
134
    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;
135
    mp_map_t *globals;      // the context within which this function was defined
Damien George's avatar
Damien George committed
136
137
138
139
140
141
    struct {
        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
    };
142
143
    uint n_state;           // total state size for the executing function (incl args, locals, stack)
    const byte *bytecode;   // bytecode for the function
Damien George's avatar
Damien George committed
144
    mp_obj_t extra_args[];  // values of default args (if any), plus a slot at the end for var args (if it takes them)
145
146
} mp_obj_fun_bc_t;

147
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
148
149
    mp_obj_fun_bc_t *self = self_in;

Damien George's avatar
Damien George committed
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
    mp_obj_t *extra_args = self->extra_args + self->n_def_args;
    uint n_extra_args = 0;

    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;
    } else if (n_args >= self->n_args - self->n_def_args) {
        // given enough arguments, but may need to use some default arguments
        if (self->takes_var_args) {
            *extra_args = mp_const_empty_tuple;
            n_extra_args = 1;
        }
        extra_args -= self->n_args - n_args;
        n_extra_args += self->n_args - n_args;
    } else {
        goto arg_error;
172
    }
Damien George's avatar
Damien George committed
173

174
    if (n_kw != 0) {
175
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
176
    }
177

178
    mp_map_t *old_globals = rt_globals_get();
179
    rt_globals_set(self->globals);
180
    mp_obj_t result;
Damien George's avatar
Damien George committed
181
    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);
182
183
    rt_globals_set(old_globals);

184
185
186
187
188
    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
189
190
191

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));
192
193
194
}

const mp_obj_type_t fun_bc_type = {
195
    { &mp_type_type },
196
    .name = MP_QSTR_function,
197
    .call = fun_bc_call,
198
199
};

Damien George's avatar
Damien George committed
200
201
202
mp_obj_t mp_obj_new_fun_bc(uint scope_flags, uint n_args, mp_obj_t def_args_in, uint n_state, const byte *code) {
    uint n_def_args = 0;
    uint n_extra_args = 0;
203
204
205
    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
206
207
208
209
        n_extra_args = def_args->len;
    }
    if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
        n_extra_args += 1;
210
    }
Damien George's avatar
Damien George committed
211
    mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args);
212
    o->base.type = &fun_bc_type;
213
    o->globals = rt_globals_get();
214
    o->n_args = n_args;
215
    o->n_def_args = n_def_args;
Damien George's avatar
Damien George committed
216
217
    o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0;
    o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0;
218
    o->n_state = n_state;
219
    o->bytecode = code;
220
    if (def_args != MP_OBJ_NULL) {
Damien George's avatar
Damien George committed
221
        memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
222
    }
223
224
225
    return o;
}

226
227
228
229
230
231
232
233
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;
}

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/******************************************************************************/
/* 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
249
STATIC machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
250
251
252
253
254
255
256
257
258
    // 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;
259
    } else if (MP_OBJ_IS_STR(obj)) {
260
        // pointer to the string (it's probably constant though!)
261
262
        uint l;
        return (machine_uint_t)mp_obj_str_get_data(obj, &l);
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#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
287
STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
288
289
290
    return MP_OBJ_NEW_SMALL_INT(val);
}

291
STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
292
293
294
    mp_obj_fun_asm_t *self = self_in;

    if (n_args != self->n_args) {
295
        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));
296
    }
297
    if (n_kw != 0) {
298
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
299
    }
300
301
302
303
304
305
306

    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) {
307
        ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]));
308
    } else if (n_args == 3) {
309
        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]));
310
311
312
313
314
315
316
317
    } else {
        assert(0);
        ret = 0;
    }

    return convert_val_from_inline_asm(ret);
}

318
STATIC const mp_obj_type_t fun_asm_type = {
319
    { &mp_type_type },
320
    .name = MP_QSTR_function,
321
    .call = fun_asm_call,
322
323
324
325
326
327
328
329
330
};

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;
}