objfun.c 10.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 "mpqstr.h"
10
#include "obj.h"
11
#include "map.h"
12
13
14
15
16
17
18
19
#include "runtime.h"
#include "bc.h"

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

// mp_obj_fun_native_t defined in obj.h

20
mp_obj_t fun_native_call_n_kw(mp_obj_t self_in, int n_args, int n_kw, const mp_obj_t *args);
21
22
23
// args are in reverse order in the array
mp_obj_t fun_native_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
    mp_obj_fun_native_t *self = self_in;
24
25
26
    if (self->is_kw) {
        return fun_native_call_n_kw(self_in, n_args, 0, args);
    }
27
28
29
30
31
    if (self->n_args_min == self->n_args_max) {
        // function requires a fixed number of arguments

        // check number of arguments
        if (n_args != self->n_args_min) {
32
            nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)self->n_args_min, (const char*)(machine_int_t)n_args));
33
34
35
36
37
38
39
40
41
42
43
44
45
        }

        // 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:
                return ((mp_fun_2_t)self->fun)(args[1], args[0]);

46
47
48
            case 3:
                return ((mp_fun_3_t)self->fun)(args[2], args[1], args[0]);

49
50
51
52
53
54
55
56
57
            default:
                assert(0);
                return mp_const_none;
        }

    } else {
        // function takes a variable number of arguments

        if (n_args < self->n_args_min) {
58
            nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "<fun name>() missing %d required positional arguments: <list of names of params>", (const char*)(machine_int_t)(self->n_args_min - n_args)));
59
        } else if (n_args > self->n_args_max) {
60
            nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_TypeError, "<fun name> expected at most %d arguments, got %d", (void*)(machine_int_t)self->n_args_max, (void*)(machine_int_t)n_args));
61
62
63
64
65
66
67
68
69
        }

        // TODO really the args need to be passed in as a Python tuple, as the form f(*[1,2]) can be used to pass var args
        mp_obj_t *args_ordered = m_new(mp_obj_t, n_args);
        for (int i = 0; i < n_args; i++) {
            args_ordered[i] = args[n_args - i - 1];
        }

        mp_obj_t res = ((mp_fun_var_t)self->fun)(n_args, args_ordered);
70
        m_del(mp_obj_t, args_ordered, n_args);
71
72
73
74
75

        return res;
    }
}

76
77
78
79
80
81
82
83
mp_obj_t fun_native_call_n_kw(mp_obj_t self_in, int n_args, int n_kw, const mp_obj_t *args) {
    mp_obj_fun_native_t *self = self_in;

    if (!self->is_kw) {
        nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "function does not take keyword arguments"));
    }

    mp_obj_t *vargs = mp_obj_new_tuple_reverse(n_args, args + 2*n_kw);
84
    mp_map_t *kw_args = mp_map_new(n_kw);
85
86
    for (int i = 0; i < 2*n_kw; i+=2) {
        qstr name = mp_obj_str_get(args[i+1]);
87
        mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = args[i];
88
89
    }
    mp_obj_t res = ((mp_fun_kw_t)self->fun)(vargs, kw_args);
90
    // TODO clean up vargs and kw_args
91
92
93
    return res;
}

94
95
96
const mp_obj_type_t fun_native_type = {
    { &mp_const_type },
    "function",
97
98
    .call_n = fun_native_call_n,
    .call_n_kw = fun_native_call_n_kw,
99
100
101
102
103
};

mp_obj_t rt_make_function_0(mp_fun_0_t fun) {
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &fun_native_type;
104
    o->is_kw = false;
105
106
107
108
109
110
111
112
113
    o->n_args_min = 0;
    o->n_args_max = 0;
    o->fun = fun;
    return o;
}

mp_obj_t rt_make_function_1(mp_fun_1_t fun) {
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &fun_native_type;
114
    o->is_kw = false;
115
116
117
118
119
120
121
122
123
    o->n_args_min = 1;
    o->n_args_max = 1;
    o->fun = fun;
    return o;
}

mp_obj_t rt_make_function_2(mp_fun_2_t fun) {
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &fun_native_type;
124
    o->is_kw = false;
125
126
127
128
129
130
    o->n_args_min = 2;
    o->n_args_max = 2;
    o->fun = fun;
    return o;
}

131
132
133
mp_obj_t rt_make_function_3(mp_fun_3_t fun) {
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &fun_native_type;
134
    o->is_kw = false;
135
136
137
138
139
140
    o->n_args_min = 3;
    o->n_args_max = 3;
    o->fun = fun;
    return o;
}

141
142
143
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;
144
    o->is_kw = false;
145
146
147
148
149
150
151
152
153
154
    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;
155
    o->is_kw = false;
156
157
158
159
160
161
162
163
164
165
166
    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;
167
168
169
170
    mp_map_t *globals;      // the context within which this function was defined
    int n_args;             // number of arguments this function takes
    uint n_state;           // total state size for the executing function (incl args, locals, stack)
    const byte *bytecode;   // bytecode for the function
171
172
173
174
175
176
177
} mp_obj_fun_bc_t;

// args are in reverse order in the array
mp_obj_t fun_bc_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
    mp_obj_fun_bc_t *self = self_in;

    if (n_args != self->n_args) {
178
        nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)self->n_args, (const char*)(machine_int_t)n_args));
179
180
    }

181
182
183
184
185
186
187
188
189
190
191
    // optimisation: allow the compiler to optimise this tail call for
    // the common case when the globals don't need to be changed
    mp_map_t *old_globals = rt_globals_get();
    if (self->globals == old_globals) {
        return mp_execute_byte_code(self->bytecode, args, n_args, self->n_state);
    } else {
        rt_globals_set(self->globals);
        mp_obj_t result = mp_execute_byte_code(self->bytecode, args, n_args, self->n_state);
        rt_globals_set(old_globals);
        return result;
    }
192
193
194
195
196
}

const mp_obj_type_t fun_bc_type = {
    { &mp_const_type },
    "function",
197
    .call_n = fun_bc_call_n,
198
199
200
201
202
};

mp_obj_t mp_obj_new_fun_bc(int n_args, uint n_state, const byte *code) {
    mp_obj_fun_bc_t *o = m_new_obj(mp_obj_fun_bc_t);
    o->base.type = &fun_bc_type;
203
    o->globals = rt_globals_get();
204
205
    o->n_args = n_args;
    o->n_state = n_state;
206
    o->bytecode = code;
207
208
209
    return o;
}

210
211
212
213
214
215
216
217
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;
}

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/******************************************************************************/
/* 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
machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
    // 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;
    } else if (MP_OBJ_IS_TYPE(obj, &str_type)) {
        // pointer to the string (it's probably constant though!)
        return (machine_uint_t)qstr_str(mp_obj_str_get(obj));
#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
mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
    return MP_OBJ_NEW_SMALL_INT(val);
}

// args are in reverse order in the array
mp_obj_t fun_asm_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
    mp_obj_fun_asm_t *self = self_in;

    if (n_args != self->n_args) {
279
        nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)self->n_args, (const char*)(machine_int_t)n_args));
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
    }

    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) {
        ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[1]), convert_obj_for_inline_asm(args[0]));
    } else if (n_args == 3) {
        ret = ((inline_asm_fun_3_t)self->fun)(convert_obj_for_inline_asm(args[2]), convert_obj_for_inline_asm(args[1]), convert_obj_for_inline_asm(args[0]));
    } else {
        assert(0);
        ret = 0;
    }

    return convert_val_from_inline_asm(ret);
}

static const mp_obj_type_t fun_asm_type = {
    { &mp_const_type },
    "function",
302
    .call_n = fun_asm_call_n,
303
304
305
306
307
308
309
310
311
};

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