objfun.c 10.1 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"
13
14
15
16
17
18
19
20
#include "runtime.h"
#include "bc.h"

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

// mp_obj_fun_native_t defined in obj.h

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

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

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

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

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

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

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

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

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

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

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

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

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

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

108
109
110
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;
111
    o->is_kw = false;
112
113
114
115
116
117
118
119
120
121
    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;
122
    o->is_kw = false;
123
124
125
126
127
128
129
130
131
132
133
    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;
134
    mp_map_t *globals;      // the context within which this function was defined
135
136
    short n_args;           // number of arguments this function takes
    short n_def_args;       // number of default arguments
137
138
    uint n_state;           // total state size for the executing function (incl args, locals, stack)
    const byte *bytecode;   // bytecode for the function
139
    mp_obj_t def_args[];    // values of default args, if any
140
141
} mp_obj_fun_bc_t;

142
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
143
144
    mp_obj_fun_bc_t *self = self_in;

145
    if (n_args < self->n_args - self->n_def_args || n_args > self->n_args) {
146
        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));
147
    }
148
    if (n_kw != 0) {
149
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
150
    }
151

152
    uint use_def_args = self->n_args - n_args;
153
    mp_map_t *old_globals = rt_globals_get();
154
155
156
157
158
    rt_globals_set(self->globals);
    mp_obj_t result = mp_execute_byte_code(self->bytecode, args, n_args, self->def_args + self->n_def_args - use_def_args, use_def_args, self->n_state);
    rt_globals_set(old_globals);

    return result;
159
160
161
}

const mp_obj_type_t fun_bc_type = {
162
    { &mp_type_type },
163
    .name = MP_QSTR_function,
164
    .call = fun_bc_call,
165
166
};

167
168
169
170
171
172
173
mp_obj_t mp_obj_new_fun_bc(int n_args, mp_obj_t def_args_in, uint n_state, const byte *code) {
    int n_def_args = 0;
    mp_obj_tuple_t *def_args = def_args_in;
    if (def_args != MP_OBJ_NULL) {
        n_def_args = def_args->len;
    }
    mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_def_args);
174
    o->base.type = &fun_bc_type;
175
    o->globals = rt_globals_get();
176
    o->n_args = n_args;
177
    o->n_def_args = n_def_args;
178
    o->n_state = n_state;
179
    o->bytecode = code;
180
181
182
    if (def_args != MP_OBJ_NULL) {
        memcpy(o->def_args, def_args->items, n_def_args * sizeof(*o->def_args));
    }
183
184
185
    return o;
}

186
187
188
189
190
191
192
193
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;
}

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/******************************************************************************/
/* 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
209
STATIC machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
210
211
212
213
214
215
216
217
218
    // 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;
219
    } else if (MP_OBJ_IS_STR(obj)) {
220
        // pointer to the string (it's probably constant though!)
221
222
        uint l;
        return (machine_uint_t)mp_obj_str_get_data(obj, &l);
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#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
247
STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
248
249
250
    return MP_OBJ_NEW_SMALL_INT(val);
}

251
STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
252
253
254
    mp_obj_fun_asm_t *self = self_in;

    if (n_args != self->n_args) {
255
        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));
256
    }
257
    if (n_kw != 0) {
258
        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
259
    }
260
261
262
263
264
265
266

    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) {
267
        ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]));
268
    } else if (n_args == 3) {
269
        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]));
270
271
272
273
274
275
276
277
    } else {
        assert(0);
        ret = 0;
    }

    return convert_val_from_inline_asm(ret);
}

278
STATIC const mp_obj_type_t fun_asm_type = {
279
    { &mp_type_type },
280
    .name = MP_QSTR_function,
281
    .call = fun_asm_call,
282
283
284
285
286
287
288
289
290
};

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