objfun.c 26 KB
Newer Older
1
2
3
4
5
6
/*
 * This file is part of the Micro Python project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013, 2014 Damien P. George
7
 * Copyright (c) 2014 Paul Sokolovsky
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

xbe's avatar
xbe committed
28
#include <stdbool.h>
29
30
31
#include <string.h>
#include <assert.h>

32
#include "mpconfig.h"
33
34
#include "nlr.h"
#include "misc.h"
35
#include "qstr.h"
36
#include "obj.h"
37
#include "objtuple.h"
Paul Sokolovsky's avatar
Paul Sokolovsky committed
38
#include "objfun.h"
Damien George's avatar
Damien George committed
39
#include "runtime0.h"
40
41
#include "runtime.h"
#include "bc.h"
42
#include "stackctrl.h"
43

44
45
46
#if 0 // print debugging info
#define DEBUG_PRINT (1)
#else // don't print debugging info
47
#define DEBUG_printf(...) (void)0
48
49
#endif

50
51
// This binary_op method is used for all function types, and is also
// used to determine if an object is of generic function type.
52
mp_obj_t mp_obj_fun_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
53
54
55
56
57
58
    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);
    }
59
    return MP_OBJ_NULL; // op not supported
60
61
}

62
63
64
65
66
/******************************************************************************/
/* builtin functions                                                          */

// mp_obj_fun_builtin_t defined in obj.h

67
STATIC mp_obj_t fun_builtin_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
68
69
    assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin));
    mp_obj_fun_builtin_t *self = self_in;
70

71
    // check number of arguments
72
    mp_arg_check_num(n_args, n_kw, self->n_args_min, self->n_args_max, self->is_kw);
73

74
    if (self->is_kw) {
75
76
        // function allows keywords

77
78
79
        // 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);
80

81
        return ((mp_fun_kw_t)self->fun)(n_args, args, &kw_args);
82

83
    } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
84
85
86
87
88
89
90
91
92
93
94
        // 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:
95
                return ((mp_fun_2_t)self->fun)(args[0], args[1]);
96

97
            case 3:
98
                return ((mp_fun_3_t)self->fun)(args[0], args[1], args[2]);
99

100
101
102
103
104
105
            default:
                assert(0);
                return mp_const_none;
        }

    } else {
106
        // function takes a variable number of arguments, but no keywords
107

108
        return ((mp_fun_var_t)self->fun)(n_args, args);
109
110
111
    }
}

112
const mp_obj_type_t mp_type_fun_builtin = {
113
    { &mp_type_type },
114
    .name = MP_QSTR_function,
115
116
    .call = fun_builtin_call,
    .binary_op = mp_obj_fun_binary_op,
117
118
};

119
#if 0 // currently unused, and semi-obsolete
Damien George's avatar
Damien George committed
120
mp_obj_t mp_make_function_var(int n_args_min, mp_fun_var_t fun) {
121
    mp_obj_fun_builtin_t *o = m_new_obj(mp_obj_fun_builtin_t);
122
    o->base.type = &mp_type_fun_native;
123
    o->is_kw = false;
124
    o->n_args_min = n_args_min;
125
    o->n_args_max = MP_OBJ_FUN_ARGS_MAX;
126
127
128
129
130
    o->fun = fun;
    return o;
}

// min and max are inclusive
Damien George's avatar
Damien George committed
131
mp_obj_t mp_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
132
    mp_obj_fun_builtin_t *o = m_new_obj(mp_obj_fun_builtin_t);
133
    o->base.type = &mp_type_fun_native;
134
    o->is_kw = false;
135
136
137
138
139
    o->n_args_min = n_args_min;
    o->n_args_max = n_args_max;
    o->fun = fun;
    return o;
}
140
#endif
141
142
143
144

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

145
const char *mp_obj_code_get_name(const byte *code_info) {
146
147
148
149
    qstr block_name = code_info[8] | (code_info[9] << 8) | (code_info[10] << 16) | (code_info[11] << 24);
    return qstr_str(block_name);
}

Paul Sokolovsky's avatar
Paul Sokolovsky committed
150
151
const char *mp_obj_fun_get_name(mp_const_obj_t fun_in) {
    const mp_obj_fun_bc_t *fun = fun_in;
152
    const byte *code_info = fun->bytecode;
153
154
155
    return mp_obj_code_get_name(code_info);
}

156
157
158
159
160
161
162
#if MICROPY_CPYTHON_COMPAT
STATIC void fun_bc_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in, mp_print_kind_t kind) {
    mp_obj_fun_bc_t *o = o_in;
    print(env, "<function %s at 0x%x>", mp_obj_fun_get_name(o), o);
}
#endif

163
#if DEBUG_PRINT
164
STATIC void dump_args(const mp_obj_t *a, int sz) {
165
166
167
168
169
170
    DEBUG_printf("%p: ", a);
    for (int i = 0; i < sz; i++) {
        DEBUG_printf("%p ", a[i]);
    }
    DEBUG_printf("\n");
}
171
172
173
#else
#define dump_args(...) (void)0
#endif
174

175
STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, mp_uint_t expected, mp_uint_t given) {
176
177
178
179
180
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
    // Generic message, to be reused for other argument issues
    nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
        "argument num/types mismatch"));
#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL
181
182
    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
        "function takes %d positional arguments but %d were given", expected, given));
183
184
185
186
187
#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
        "%s() takes %d positional arguments but %d were given",
        mp_obj_fun_get_name(f), expected, given));
#endif
188
189
}

190
191
192
// With this macro you can tune the maximum number of function state bytes
// that will be allocated on the stack.  Any function that needs more
// than this will use the heap.
193
#define VM_MAX_STATE_ON_STACK (10 * sizeof(mp_uint_t))
194
195
196
197

// Set this to enable a simple stack overflow check.
#define VM_DETECT_STACK_OVERFLOW (0)

198
199
// code_state should have ->ip filled in (pointing past code info block),
// as well as ->n_state.
200
void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
201
202
    // This function is pretty complicated.  It's main aim is to be efficient in speed and RAM
    // usage for the common case of positional only args.
203
    mp_obj_fun_bc_t *self = self_in;
204
    mp_uint_t n_state = code_state->n_state;
205
    const byte *ip = code_state->ip;
206
207
208
209
210
211
212
213

    code_state->code_info = self->bytecode;
    code_state->sp = &code_state->state[0] - 1;
    code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;

    // zero out the local stack to begin with
    memset(code_state->state, 0, n_state * sizeof(*code_state->state));

214
    const mp_obj_t *kwargs = args + n_args;
215
216
217

    // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed)
    mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - self->n_pos_args - self->n_kwonly_args];
Damien George's avatar
Damien George committed
218

219
220
    // check positional arguments

221
    if (n_args > self->n_pos_args) {
Damien George's avatar
Damien George committed
222
223
        // given more than enough arguments
        if (!self->takes_var_args) {
224
            fun_pos_args_mismatch(self, self->n_pos_args, n_args);
Damien George's avatar
Damien George committed
225
226
        }
        // put extra arguments in varargs tuple
227
        *var_pos_kw_args-- = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args);
228
        n_args = self->n_pos_args;
229
    } else {
Damien George's avatar
Damien George committed
230
        if (self->takes_var_args) {
231
            DEBUG_printf("passing empty tuple as *args\n");
232
            *var_pos_kw_args-- = mp_const_empty_tuple;
Damien George's avatar
Damien George committed
233
        }
234
235
        // Apply processing and check below only if we don't have kwargs,
        // otherwise, kw handling code below has own extensive checks.
236
        if (n_kw == 0 && !self->has_def_kw_args) {
237
            if (n_args >= self->n_pos_args - self->n_def_args) {
238
                // given enough arguments, but may need to use some default arguments
239
                for (mp_uint_t i = n_args; i < self->n_pos_args; i++) {
240
241
                    code_state->state[n_state - 1 - i] = self->extra_args[i - (self->n_pos_args - self->n_def_args)];
                }
242
            } else {
243
                fun_pos_args_mismatch(self, self->n_pos_args - self->n_def_args, n_args);
244
245
            }
        }
246
    }
Damien George's avatar
Damien George committed
247

248
    // copy positional args into state
249
    for (mp_uint_t i = 0; i < n_args; i++) {
250
251
252
        code_state->state[n_state - 1 - i] = args[i];
    }

253
254
    // check keyword arguments

255
    if (n_kw != 0 || self->has_def_kw_args) {
256
        DEBUG_printf("Initial args: ");
257
        dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
258
259
260
261

        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?
262
            *var_pos_kw_args = dict;
263
        }
264

265
        for (mp_uint_t i = 0; i < n_kw; i++) {
266
            qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]);
267
            for (mp_uint_t j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) {
268
                if (arg_name == self->args[j]) {
269
                    if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
270
                        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
271
272
                            "function got multiple values for argument '%s'", qstr_str(arg_name)));
                    }
273
                    code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
274
275
276
277
278
                    goto continue2;
                }
            }
            // Didn't find name match with positional args
            if (!self->takes_kw_args) {
279
                nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
280
            }
281
            mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
282
283
continue2:;
        }
284

285
        DEBUG_printf("Args with kws flattened: ");
286
        dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
287

288
289
        // fill in defaults for positional args
        mp_obj_t *d = &code_state->state[n_state - self->n_pos_args];
290
        mp_obj_t *s = &self->extra_args[self->n_def_args - 1];
291
        for (int i = self->n_def_args; i > 0; i--, d++, s--) {
292
            if (*d == MP_OBJ_NULL) {
293
                *d = *s;
294
295
            }
        }
296
297
298

        DEBUG_printf("Args after filling default positional: ");
        dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
299

300
        // Check that all mandatory positional args are specified
301
302
        while (d < &code_state->state[n_state]) {
            if (*d++ == MP_OBJ_NULL) {
303
                nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
304
                    "function missing required positional argument #%d", &code_state->state[n_state] - d));
305
306
307
            }
        }

308
        // Check that all mandatory keyword args are specified
309
        // Fill in default kw args if we have them
310
        for (mp_uint_t i = 0; i < self->n_kwonly_args; i++) {
311
            if (code_state->state[n_state - 1 - self->n_pos_args - i] == MP_OBJ_NULL) {
312
313
314
315
316
                mp_map_elem_t *elem = NULL;
                if (self->has_def_kw_args) {
                    elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, MP_OBJ_NEW_QSTR(self->args[self->n_pos_args + i]), MP_MAP_LOOKUP);
                }
                if (elem != NULL) {
317
                    code_state->state[n_state - 1 - self->n_pos_args - i] = elem->value;
318
319
320
321
                } else {
                    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
                        "function missing required keyword argument '%s'", qstr_str(self->args[self->n_pos_args + i])));
                }
322
323
324
            }
        }

325
326
    } else {
        // no keyword arguments given
327
328
329
330
        if (self->n_kwonly_args != 0) {
            nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
                "function missing keyword-only argument"));
        }
331
        if (self->takes_kw_args) {
332
            *var_pos_kw_args = mp_obj_new_dict(0);
333
        }
334
    }
335

336
    // bytecode prelude: initialise closed over variables
337
338
    for (mp_uint_t n_local = *ip++; n_local > 0; n_local--) {
        mp_uint_t local_num = *ip++;
339
340
341
        code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
    }

342
    // now that we skipped over the prelude, set the ip for the VM
343
344
    code_state->ip = ip;

345
346
347
    DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", self->n_pos_args, self->n_kwonly_args);
    dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
    dump_args(code_state->state, n_state);
348
349
350
}


351
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
Paul Sokolovsky's avatar
Paul Sokolovsky committed
352
    MP_STACK_CHECK();
353
354
355
356
357
358
359
360
361
362
363
364

    DEBUG_printf("Input n_args: %d, n_kw: %d\n", n_args, n_kw);
    DEBUG_printf("Input pos args: ");
    dump_args(args, n_args);
    DEBUG_printf("Input kw args: ");
    dump_args(args + n_args, n_kw * 2);
    mp_obj_fun_bc_t *self = self_in;
    DEBUG_printf("Func n_def_args: %d\n", self->n_def_args);

    const byte *ip = self->bytecode;

    // get code info size, and skip line number table
365
    mp_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
366
367
368
    ip += code_info_size;

    // bytecode prelude: state size and exception stack size; 16 bit uints
369
370
    mp_uint_t n_state = ip[0] | (ip[1] << 8);
    mp_uint_t n_exc_stack = ip[2] | (ip[3] << 8);
371
372
373
374
375
376
377
    ip += 4;

#if VM_DETECT_STACK_OVERFLOW
    n_state += 1;
#endif

    // allocate state for locals and stack
378
    mp_uint_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t);
379
380
381
382
383
384
385
386
387
388
    mp_code_state *code_state;
    if (state_size > VM_MAX_STATE_ON_STACK) {
        code_state = m_new_obj_var(mp_code_state, byte, state_size);
    } else {
        code_state = alloca(sizeof(mp_code_state) + state_size);
    }

    code_state->n_state = n_state;
    code_state->ip = ip;
    mp_setup_code_state(code_state, self_in, n_args, n_kw, args);
389
390
391
392

    // execute the byte code with the correct globals context
    mp_obj_dict_t *old_globals = mp_globals_get();
    mp_globals_set(self->globals);
393
    mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(code_state, MP_OBJ_NULL);
394
    mp_globals_set(old_globals);
395
396
397
398
399
400
401
402
403
404
405

#if VM_DETECT_STACK_OVERFLOW
    if (vm_return_kind == MP_VM_RETURN_NORMAL) {
        if (code_state->sp < code_state->state) {
            printf("VM stack underflow: " INT_FMT "\n", code_state->sp - code_state->state);
            assert(0);
        }
    }
    // We can't check the case when an exception is returned in state[n_state - 1]
    // and there are no arguments, because in this case our detection slot may have
    // been overwritten by the returned exception (which is allowed).
406
    if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && self->n_pos_args + self->n_kwonly_args == 0)) {
407
408
        // Just check to see that we have at least 1 null object left in the state.
        bool overflow = true;
409
        for (mp_uint_t i = 0; i < n_state - self->n_pos_args - self->n_kwonly_args; i++) {
410
411
412
413
414
415
416
417
418
419
420
421
            if (code_state->state[i] == MP_OBJ_NULL) {
                overflow = false;
                break;
            }
        }
        if (overflow) {
            printf("VM stack overflow state=%p n_state+1=" UINT_FMT "\n", code_state->state, n_state);
            assert(0);
        }
    }
#endif

422
    mp_obj_t result;
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
    switch (vm_return_kind) {
        case MP_VM_RETURN_NORMAL:
            // return value is in *sp
            result = *code_state->sp;
            break;

        case MP_VM_RETURN_EXCEPTION:
            // return value is in state[n_state - 1]
            result = code_state->state[n_state - 1];
            break;

        case MP_VM_RETURN_YIELD: // byte-code shouldn't yield
        default:
            assert(0);
            result = mp_const_none;
            vm_return_kind = MP_VM_RETURN_NORMAL;
            break;
    }

    // free the state if it was allocated on the heap
    if (state_size > VM_MAX_STATE_ON_STACK) {
        m_del_var(mp_code_state, byte, state_size, code_state);
    }

447
448
449
    if (vm_return_kind == MP_VM_RETURN_NORMAL) {
        return result;
    } else { // MP_VM_RETURN_EXCEPTION
450
        nlr_raise(result);
451
    }
452
453
}

454
const mp_obj_type_t mp_type_fun_bc = {
455
    { &mp_type_type },
456
    .name = MP_QSTR_function,
457
458
459
#if MICROPY_CPYTHON_COMPAT
    .print = fun_bc_print,
#endif
460
    .call = fun_bc_call,
461
    .binary_op = mp_obj_fun_binary_op,
462
463
};

464
465
466
mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, qstr *args, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code) {
    mp_uint_t n_def_args = 0;
    mp_uint_t n_extra_args = 0;
467
468
    mp_obj_tuple_t *def_args = def_args_in;
    if (def_args != MP_OBJ_NULL) {
469
        assert(MP_OBJ_IS_TYPE(def_args, &mp_type_tuple));
470
        n_def_args = def_args->len;
Damien George's avatar
Damien George committed
471
472
        n_extra_args = def_args->len;
    }
473
474
475
    if (def_kw_args != MP_OBJ_NULL) {
        n_extra_args += 1;
    }
Damien George's avatar
Damien George committed
476
    mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args);
477
    o->base.type = &mp_type_fun_bc;
Damien George's avatar
Damien George committed
478
    o->globals = mp_globals_get();
479
    o->args = args;
480
481
    o->n_pos_args = n_pos_args;
    o->n_kwonly_args = n_kwonly_args;
482
    o->n_def_args = n_def_args;
483
    o->has_def_kw_args = def_kw_args != MP_OBJ_NULL;
Damien George's avatar
Damien George committed
484
485
    o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0;
    o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0;
486
    o->bytecode = code;
487
    if (def_args != MP_OBJ_NULL) {
488
        memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
489
490
    }
    if (def_kw_args != MP_OBJ_NULL) {
491
        o->extra_args[n_def_args] = def_kw_args;
492
    }
493
494
495
    return o;
}

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
/******************************************************************************/
/* native functions                                                           */

#if MICROPY_EMIT_NATIVE

typedef struct _mp_obj_fun_native_t {
    mp_obj_base_t base;
    mp_uint_t n_args;
    void *fun_data; // GC must be able to trace this pointer
    // TODO add mp_map_t *globals
} mp_obj_fun_native_t;

typedef mp_obj_t (*native_fun_0_t)();
typedef mp_obj_t (*native_fun_1_t)(mp_obj_t);
typedef mp_obj_t (*native_fun_2_t)(mp_obj_t, mp_obj_t);
typedef mp_obj_t (*native_fun_3_t)(mp_obj_t, mp_obj_t, mp_obj_t);

513
STATIC mp_obj_t fun_native_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
    mp_obj_fun_native_t *self = self_in;

    mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false);

    void *fun = MICROPY_MAKE_POINTER_CALLABLE(self->fun_data);

    switch (n_args) {
        case 0:
            return ((native_fun_0_t)fun)();

        case 1:
            return ((native_fun_1_t)fun)(args[0]);

        case 2:
            return ((native_fun_2_t)fun)(args[0], args[1]);

        case 3:
            return ((native_fun_3_t)fun)(args[0], args[1], args[2]);

        default:
            assert(0);
            return mp_const_none;
    }
}

STATIC const mp_obj_type_t mp_type_fun_native = {
    { &mp_type_type },
    .name = MP_QSTR_function,
    .call = fun_native_call,
    .binary_op = mp_obj_fun_binary_op,
};

mp_obj_t mp_obj_new_fun_native(mp_uint_t n_args, void *fun_data) {
    assert(0 <= n_args && n_args <= 3);
    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
    o->base.type = &mp_type_fun_native;
    o->n_args = n_args;
    o->fun_data = fun_data;
    return o;
}

#endif // MICROPY_EMIT_NATIVE

557
558
559
560
561
562
563
/******************************************************************************/
/* viper functions                                                            */

#if MICROPY_EMIT_NATIVE

typedef struct _mp_obj_fun_viper_t {
    mp_obj_base_t base;
564
565
    mp_uint_t n_args;
    void *fun_data; // GC must be able to trace this pointer
566
567
568
569
570
571
572
573
    mp_uint_t type_sig;
} mp_obj_fun_viper_t;

typedef mp_uint_t (*viper_fun_0_t)();
typedef mp_uint_t (*viper_fun_1_t)(mp_uint_t);
typedef mp_uint_t (*viper_fun_2_t)(mp_uint_t, mp_uint_t);
typedef mp_uint_t (*viper_fun_3_t)(mp_uint_t, mp_uint_t, mp_uint_t);

574
STATIC mp_obj_t fun_viper_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
575
576
577
578
    mp_obj_fun_viper_t *self = self_in;

    mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false);

579
580
    void *fun = MICROPY_MAKE_POINTER_CALLABLE(self->fun_data);

581
582
    mp_uint_t ret;
    if (n_args == 0) {
583
        ret = ((viper_fun_0_t)fun)();
584
    } else if (n_args == 1) {
585
        ret = ((viper_fun_1_t)fun)(mp_convert_obj_to_native(args[0], self->type_sig >> 2));
586
    } else if (n_args == 2) {
587
        ret = ((viper_fun_2_t)fun)(mp_convert_obj_to_native(args[0], self->type_sig >> 2), mp_convert_obj_to_native(args[1], self->type_sig >> 4));
588
    } else if (n_args == 3) {
589
        ret = ((viper_fun_3_t)fun)(mp_convert_obj_to_native(args[0], self->type_sig >> 2), mp_convert_obj_to_native(args[1], self->type_sig >> 4), mp_convert_obj_to_native(args[2], self->type_sig >> 6));
590
591
592
593
594
    } else {
        assert(0);
        ret = 0;
    }

595
    return mp_convert_native_to_obj(ret, self->type_sig);
596
597
598
599
600
601
}

STATIC const mp_obj_type_t mp_type_fun_viper = {
    { &mp_type_type },
    .name = MP_QSTR_function,
    .call = fun_viper_call,
602
    .binary_op = mp_obj_fun_binary_op,
603
604
};

605
mp_obj_t mp_obj_new_fun_viper(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig) {
606
607
608
    mp_obj_fun_viper_t *o = m_new_obj(mp_obj_fun_viper_t);
    o->base.type = &mp_type_fun_viper;
    o->n_args = n_args;
609
    o->fun_data = fun_data;
610
611
612
613
614
615
    o->type_sig = type_sig;
    return o;
}

#endif // MICROPY_EMIT_NATIVE

616
617
618
/******************************************************************************/
/* inline assembler functions                                                 */

619
620
#if MICROPY_EMIT_INLINE_THUMB

621
622
typedef struct _mp_obj_fun_asm_t {
    mp_obj_base_t base;
623
624
    mp_uint_t n_args;
    void *fun_data; // GC must be able to trace this pointer
625
626
} mp_obj_fun_asm_t;

627
628
629
630
typedef mp_uint_t (*inline_asm_fun_0_t)();
typedef mp_uint_t (*inline_asm_fun_1_t)(mp_uint_t);
typedef mp_uint_t (*inline_asm_fun_2_t)(mp_uint_t, mp_uint_t);
typedef mp_uint_t (*inline_asm_fun_3_t)(mp_uint_t, mp_uint_t, mp_uint_t);
631
632

// convert a Micro Python object to a sensible value for inline asm
633
STATIC mp_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
634
635
636
637
638
639
640
641
642
    // 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;
643
    } else if (MP_OBJ_IS_STR(obj)) {
644
        // pointer to the string (it's probably constant though!)
645
        uint l;
646
        return (mp_uint_t)mp_obj_str_get_data(obj, &l);
647
648
649
    } else {
        mp_obj_type_t *type = mp_obj_get_type(obj);
        if (0) {
650
#if MICROPY_PY_BUILTINS_FLOAT
651
652
        } else if (type == &mp_type_float) {
            // convert float to int (could also pass in float registers)
653
            return (mp_int_t)mp_obj_float_get(obj);
654
#endif
655
656
657
658
659
        } 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);
660
            return (mp_uint_t)items;
661
662
663
664
665
        } 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);
666
            return (mp_uint_t)items;
667
        } else {
668
            mp_buffer_info_t bufinfo;
669
            if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_WRITE)) {
670
                // supports the buffer protocol, return a pointer to the data
671
                return (mp_uint_t)bufinfo.buf;
672
673
            } else {
                // just pass along a pointer to the object
674
                return (mp_uint_t)obj;
675
            }
676
        }
677
678
679
680
    }
}

// convert a return value from inline asm to a sensible Micro Python object
681
STATIC mp_obj_t convert_val_from_inline_asm(mp_uint_t val) {
682
683
684
    return MP_OBJ_NEW_SMALL_INT(val);
}

685
STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
686
687
    mp_obj_fun_asm_t *self = self_in;

688
    mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false);
689

690
691
    void *fun = MICROPY_MAKE_POINTER_CALLABLE(self->fun_data);

692
    mp_uint_t ret;
693
    if (n_args == 0) {
694
        ret = ((inline_asm_fun_0_t)fun)();
695
    } else if (n_args == 1) {
696
        ret = ((inline_asm_fun_1_t)fun)(convert_obj_for_inline_asm(args[0]));
697
    } else if (n_args == 2) {
698
        ret = ((inline_asm_fun_2_t)fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]));
699
    } else if (n_args == 3) {
700
        ret = ((inline_asm_fun_3_t)fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]), convert_obj_for_inline_asm(args[2]));
701
702
703
704
705
706
707
708
    } else {
        assert(0);
        ret = 0;
    }

    return convert_val_from_inline_asm(ret);
}

709
STATIC const mp_obj_type_t mp_type_fun_asm = {
710
    { &mp_type_type },
711
    .name = MP_QSTR_function,
712
    .call = fun_asm_call,
713
    .binary_op = mp_obj_fun_binary_op,
714
715
};

716
mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data) {
717
    mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
718
    o->base.type = &mp_type_fun_asm;
719
    o->n_args = n_args;
720
    o->fun_data = fun_data;
721
722
    return o;
}
723
724

#endif // MICROPY_EMIT_INLINE_THUMB