blob: d006c8715adbd9ce703e0f496190f6e144d0b197 [file] [log] [blame]
xbeefe34222014-03-16 00:14:26 -07001#include <stdbool.h>
Damiend99b0522013-12-21 18:17:45 +00002#include <stdlib.h>
Damiend99b0522013-12-21 18:17:45 +00003#include <string.h>
4#include <assert.h>
5
6#include "nlr.h"
7#include "misc.h"
8#include "mpconfig.h"
Damien George55baff42014-01-21 21:40:13 +00009#include "qstr.h"
Damiend99b0522013-12-21 18:17:45 +000010#include "obj.h"
Paul Sokolovsky90750022014-02-01 15:05:04 +020011#include "objtuple.h"
Damien George2e482cd2014-02-16 00:01:29 +000012#include "runtime0.h"
Damiend99b0522013-12-21 18:17:45 +000013#include "runtime.h"
14#include "bc.h"
15
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +020016#if 0 // print debugging info
17#define DEBUG_PRINT (1)
18#else // don't print debugging info
Damien George41eb6082014-02-26 22:40:35 +000019#define DEBUG_printf(...) (void)0
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +020020#endif
21
Damiend99b0522013-12-21 18:17:45 +000022/******************************************************************************/
23/* native functions */
24
25// mp_obj_fun_native_t defined in obj.h
26
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +020027STATIC void check_nargs(mp_obj_fun_native_t *self, int n_args, int n_kw) {
Damien Georged17926d2014-03-30 13:35:08 +010028 mp_check_nargs(n_args, self->n_args_min, self->n_args_max, n_kw, self->is_kw);
Dave Hylands51dabac2014-02-17 17:57:13 -080029}
30
Damien Georged17926d2014-03-30 13:35:08 +010031void mp_check_nargs(int n_args, machine_uint_t n_args_min, machine_uint_t n_args_max, int n_kw, bool is_kw) {
Dave Hylands51dabac2014-02-17 17:57:13 -080032 if (n_kw && !is_kw) {
Damien Georgeea13f402014-04-05 18:32:08 +010033 nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000034 "function does not take keyword arguments"));
35 }
36
Dave Hylands51dabac2014-02-17 17:57:13 -080037 if (n_args_min == n_args_max) {
38 if (n_args != n_args_min) {
Damien Georgeea13f402014-04-05 18:32:08 +010039 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000040 "function takes %d positional arguments but %d were given",
Dave Hylands51dabac2014-02-17 17:57:13 -080041 n_args_min, n_args));
John R. Lenton88cb1e62014-01-13 19:55:18 +000042 }
43 } else {
Dave Hylands51dabac2014-02-17 17:57:13 -080044 if (n_args < n_args_min) {
Damien Georgeea13f402014-04-05 18:32:08 +010045 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000046 "<fun name>() missing %d required positional arguments: <list of names of params>",
Dave Hylands51dabac2014-02-17 17:57:13 -080047 n_args_min - n_args));
48 } else if (n_args > n_args_max) {
Damien Georgeea13f402014-04-05 18:32:08 +010049 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000050 "<fun name> expected at most %d arguments, got %d",
Dave Hylands51dabac2014-02-17 17:57:13 -080051 n_args_max, n_args));
John R. Lenton88cb1e62014-01-13 19:55:18 +000052 }
53 }
54}
55
Paul Sokolovsky586bfce2014-04-05 13:50:06 +030056STATIC mp_obj_t fun_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
57 switch (op) {
58 case MP_BINARY_OP_EQUAL:
59 // These objects can be equal only if it's the same underlying structure,
60 // we don't even need to check for 2nd arg type.
61 return MP_BOOL(lhs_in == rhs_in);
62 }
63 return NULL;
64}
65
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +020066STATIC mp_obj_t fun_native_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
Damien George3e1a5c12014-03-29 13:43:38 +000067 assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_native));
Damiend99b0522013-12-21 18:17:45 +000068 mp_obj_fun_native_t *self = self_in;
Damien George20006db2014-01-18 14:10:48 +000069
John R. Lenton88cb1e62014-01-13 19:55:18 +000070 // check number of arguments
Damien George20006db2014-01-18 14:10:48 +000071 check_nargs(self, n_args, n_kw);
72
John R. Lentonc06763a2014-01-07 17:29:16 +000073 if (self->is_kw) {
Damien George20006db2014-01-18 14:10:48 +000074 // function allows keywords
75
Damien George0a587b82014-02-08 18:53:41 +000076 // we create a map directly from the given args array
77 mp_map_t kw_args;
78 mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
Damien George20006db2014-01-18 14:10:48 +000079
Damien George0a587b82014-02-08 18:53:41 +000080 return ((mp_fun_kw_t)self->fun)(n_args, args, &kw_args);
Damien George20006db2014-01-18 14:10:48 +000081
Paul Sokolovsky9d95a2b2014-01-26 01:58:51 +020082 } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
Damiend99b0522013-12-21 18:17:45 +000083 // function requires a fixed number of arguments
84
Damiend99b0522013-12-21 18:17:45 +000085 // dispatch function call
86 switch (self->n_args_min) {
87 case 0:
88 return ((mp_fun_0_t)self->fun)();
89
90 case 1:
91 return ((mp_fun_1_t)self->fun)(args[0]);
92
93 case 2:
Damien George20006db2014-01-18 14:10:48 +000094 return ((mp_fun_2_t)self->fun)(args[0], args[1]);
Damiend99b0522013-12-21 18:17:45 +000095
John R. Lenton45a87442014-01-04 01:15:01 +000096 case 3:
Damien George20006db2014-01-18 14:10:48 +000097 return ((mp_fun_3_t)self->fun)(args[0], args[1], args[2]);
John R. Lenton45a87442014-01-04 01:15:01 +000098
Damiend99b0522013-12-21 18:17:45 +000099 default:
100 assert(0);
101 return mp_const_none;
102 }
103
104 } else {
Damien George20006db2014-01-18 14:10:48 +0000105 // function takes a variable number of arguments, but no keywords
Damiend99b0522013-12-21 18:17:45 +0000106
Damien George20006db2014-01-18 14:10:48 +0000107 return ((mp_fun_var_t)self->fun)(n_args, args);
Damiend99b0522013-12-21 18:17:45 +0000108 }
109}
110
Damien George3e1a5c12014-03-29 13:43:38 +0000111const mp_obj_type_t mp_type_fun_native = {
Damien Georgec5966122014-02-15 16:10:44 +0000112 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000113 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +0000114 .call = fun_native_call,
Paul Sokolovsky586bfce2014-04-05 13:50:06 +0300115 .binary_op = fun_binary_op,
Damiend99b0522013-12-21 18:17:45 +0000116};
117
Damien Georgef62d33a2014-01-13 19:50:05 +0000118// fun must have the correct signature for n_args fixed arguments
Damien Georged17926d2014-03-30 13:35:08 +0100119mp_obj_t mp_make_function_n(int n_args, void *fun) {
Damiend99b0522013-12-21 18:17:45 +0000120 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
Damien George3e1a5c12014-03-29 13:43:38 +0000121 o->base.type = &mp_type_fun_native;
Dave Hylands44332ec2014-01-13 08:42:43 -0800122 o->is_kw = false;
Damien Georgef62d33a2014-01-13 19:50:05 +0000123 o->n_args_min = n_args;
124 o->n_args_max = n_args;
John R. Lenton45a87442014-01-04 01:15:01 +0000125 o->fun = fun;
126 return o;
127}
128
Damien Georged17926d2014-03-30 13:35:08 +0100129mp_obj_t mp_make_function_var(int n_args_min, mp_fun_var_t fun) {
Damiend99b0522013-12-21 18:17:45 +0000130 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
Damien George3e1a5c12014-03-29 13:43:38 +0000131 o->base.type = &mp_type_fun_native;
Dave Hylands44332ec2014-01-13 08:42:43 -0800132 o->is_kw = false;
Damiend99b0522013-12-21 18:17:45 +0000133 o->n_args_min = n_args_min;
Damien Georged5e81822014-02-26 17:47:05 +0000134 o->n_args_max = MP_OBJ_FUN_ARGS_MAX;
Damiend99b0522013-12-21 18:17:45 +0000135 o->fun = fun;
136 return o;
137}
138
139// min and max are inclusive
Damien Georged17926d2014-03-30 13:35:08 +0100140mp_obj_t mp_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
Damiend99b0522013-12-21 18:17:45 +0000141 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
Damien George3e1a5c12014-03-29 13:43:38 +0000142 o->base.type = &mp_type_fun_native;
Dave Hylands44332ec2014-01-13 08:42:43 -0800143 o->is_kw = false;
Damiend99b0522013-12-21 18:17:45 +0000144 o->n_args_min = n_args_min;
145 o->n_args_max = n_args_max;
146 o->fun = fun;
147 return o;
148}
149
150/******************************************************************************/
151/* byte code functions */
152
153typedef struct _mp_obj_fun_bc_t {
154 mp_obj_base_t base;
Damien George66028ab2014-01-03 14:03:48 +0000155 mp_map_t *globals; // the context within which this function was defined
Damien George51047752014-02-26 17:40:52 +0000156 machine_uint_t n_args : 15; // number of arguments this function takes
157 machine_uint_t n_def_args : 15; // number of default arguments
158 machine_uint_t takes_var_args : 1; // set if this function takes variable args
159 machine_uint_t takes_kw_args : 1; // set if this function takes keyword args
Damien George66028ab2014-01-03 14:03:48 +0000160 const byte *bytecode; // bytecode for the function
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200161 qstr *args; // argument names (needed to resolve positional args passed as keywords)
Damien Georgee5d371b2014-02-16 00:17:42 +0000162 mp_obj_t extra_args[]; // values of default args (if any), plus a slot at the end for var args and/or kw args (if it takes them)
Damiend99b0522013-12-21 18:17:45 +0000163} mp_obj_fun_bc_t;
164
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200165#if DEBUG_PRINT
Damien Georgef78b6df2014-03-31 15:59:25 +0100166STATIC void dump_args(const mp_obj_t *a, int sz) {
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200167 DEBUG_printf("%p: ", a);
168 for (int i = 0; i < sz; i++) {
169 DEBUG_printf("%p ", a[i]);
170 }
171 DEBUG_printf("\n");
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200172}
Damien Georgef78b6df2014-03-31 15:59:25 +0100173#else
174#define dump_args(...) (void)0
175#endif
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200176
Paul Sokolovsky7fafb282014-03-30 20:21:28 +0300177// If it's possible to call a function without allocating new argument array,
178// this function returns true, together with pointers to 2 subarrays to be used
179// as arguments. Otherwise, it returns false. It is expected that this fucntion
180// will be accompanied by another, mp_obj_fun_prepare_full_args(), which will
181// instead take pointer to full-length out-array, and will fill it in. Rationale
182// being that a caller can try this function and if it succeeds, the function call
183// can be made without allocating extra memory. Otherwise, caller can allocate memory
184// and try "full" function. These functions are expected to be refactoring of
185// code in fun_bc_call() and evenrually replace it.
186bool mp_obj_fun_prepare_simple_args(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args,
187 uint *out_args1_len, const mp_obj_t **out_args1, uint *out_args2_len, const mp_obj_t **out_args2) {
188 mp_obj_fun_bc_t *self = self_in;
189
190 assert(n_kw == 0);
191 assert(self->takes_var_args == 0);
192 assert(self->takes_kw_args == 0);
193
194 mp_obj_t *extra_args = self->extra_args + self->n_def_args;
195 uint n_extra_args = 0;
196
197 if (n_args > self->n_args) {
198 goto arg_error;
199 } else {
200 extra_args -= self->n_args - n_args;
201 n_extra_args += self->n_args - n_args;
202 }
203 *out_args1 = args;
204 *out_args1_len = n_args;
205 *out_args2 = extra_args;
206 *out_args2_len = n_extra_args;
207 return true;
208
209arg_error:
Damien Georgeea13f402014-04-05 18:32:08 +0100210 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));
Paul Sokolovsky7fafb282014-03-30 20:21:28 +0300211}
212
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200213STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200214 DEBUG_printf("Input: ");
215 dump_args(args, n_args);
Damiend99b0522013-12-21 18:17:45 +0000216 mp_obj_fun_bc_t *self = self_in;
217
Damien Georgee5d371b2014-02-16 00:17:42 +0000218 const mp_obj_t *kwargs = args + n_args;
Damien George2e482cd2014-02-16 00:01:29 +0000219 mp_obj_t *extra_args = self->extra_args + self->n_def_args;
220 uint n_extra_args = 0;
221
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200222
Damien Georgee5d371b2014-02-16 00:17:42 +0000223 // check positional arguments
224
Damien George2e482cd2014-02-16 00:01:29 +0000225 if (n_args > self->n_args) {
226 // given more than enough arguments
227 if (!self->takes_var_args) {
228 goto arg_error;
229 }
230 // put extra arguments in varargs tuple
231 *extra_args = mp_obj_new_tuple(n_args - self->n_args, args + self->n_args);
232 n_extra_args = 1;
233 n_args = self->n_args;
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200234 } else {
Damien George2e482cd2014-02-16 00:01:29 +0000235 if (self->takes_var_args) {
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200236 DEBUG_printf("passing empty tuple as *args\n");
Damien George2e482cd2014-02-16 00:01:29 +0000237 *extra_args = mp_const_empty_tuple;
238 n_extra_args = 1;
239 }
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200240 // Apply processing and check below only if we don't have kwargs,
241 // otherwise, kw handling code below has own extensive checks.
242 if (n_kw == 0) {
243 if (n_args >= self->n_args - self->n_def_args) {
244 // given enough arguments, but may need to use some default arguments
245 extra_args -= self->n_args - n_args;
246 n_extra_args += self->n_args - n_args;
247 } else {
248 goto arg_error;
249 }
250 }
Damiend99b0522013-12-21 18:17:45 +0000251 }
Damien George2e482cd2014-02-16 00:01:29 +0000252
Damien Georgee5d371b2014-02-16 00:17:42 +0000253 // check keyword arguments
254
Damien George20006db2014-01-18 14:10:48 +0000255 if (n_kw != 0) {
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200256 // We cannot use dynamically-sized array here, because GCC indeed
257 // deallocates it on leaving defining scope (unlike most static stack allocs).
258 // So, we have 2 choices: allocate it unconditionally at the top of function
259 // (wastes stack), or use alloca which is guaranteed to dealloc on func exit.
260 //mp_obj_t flat_args[self->n_args];
261 mp_obj_t *flat_args = alloca(self->n_args * sizeof(mp_obj_t));
262 for (int i = self->n_args - 1; i >= 0; i--) {
263 flat_args[i] = MP_OBJ_NULL;
Damien Georgee5d371b2014-02-16 00:17:42 +0000264 }
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200265 memcpy(flat_args, args, sizeof(*args) * n_args);
266 DEBUG_printf("Initial args: ");
267 dump_args(flat_args, self->n_args);
268
269 mp_obj_t dict = MP_OBJ_NULL;
270 if (self->takes_kw_args) {
271 dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
272 }
Damien Georgee5d371b2014-02-16 00:17:42 +0000273 for (uint i = 0; i < n_kw; i++) {
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200274 qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]);
275 for (uint j = 0; j < self->n_args; j++) {
276 if (arg_name == self->args[j]) {
277 if (flat_args[j] != MP_OBJ_NULL) {
Damien Georgeea13f402014-04-05 18:32:08 +0100278 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200279 "function got multiple values for argument '%s'", qstr_str(arg_name)));
280 }
281 flat_args[j] = kwargs[2 * i + 1];
282 goto continue2;
283 }
284 }
285 // Didn't find name match with positional args
286 if (!self->takes_kw_args) {
Damien Georgeea13f402014-04-05 18:32:08 +0100287 nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200288 }
Damien Georgee5d371b2014-02-16 00:17:42 +0000289 mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200290continue2:;
Damien Georgee5d371b2014-02-16 00:17:42 +0000291 }
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200292 DEBUG_printf("Args with kws flattened: ");
293 dump_args(flat_args, self->n_args);
294
295 // Now fill in defaults
296 mp_obj_t *d = &flat_args[self->n_args - 1];
297 mp_obj_t *s = &self->extra_args[self->n_def_args - 1];
298 for (int i = self->n_def_args; i > 0; i--) {
Damien George25f5a302014-03-03 23:25:08 +0000299 if (*d == MP_OBJ_NULL) {
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200300 *d-- = *s--;
301 }
302 }
303 DEBUG_printf("Args after filling defaults: ");
304 dump_args(flat_args, self->n_args);
305
306 // Now check that all mandatory args specified
307 while (d >= flat_args) {
308 if (*d-- == MP_OBJ_NULL) {
Damien Georgeea13f402014-04-05 18:32:08 +0100309 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200310 "function missing required positional argument #%d", d - flat_args));
311 }
312 }
313
314 args = flat_args;
315 n_args = self->n_args;
316
317 if (self->takes_kw_args) {
318 extra_args[n_extra_args] = dict;
319 n_extra_args += 1;
320 }
Damien Georgee5d371b2014-02-16 00:17:42 +0000321 } else {
322 // no keyword arguments given
323 if (self->takes_kw_args) {
324 extra_args[n_extra_args] = mp_obj_new_dict(0);
325 n_extra_args += 1;
326 }
Damien George20006db2014-01-18 14:10:48 +0000327 }
Damiend99b0522013-12-21 18:17:45 +0000328
Damien Georged17926d2014-03-30 13:35:08 +0100329 mp_map_t *old_globals = mp_globals_get();
330 mp_globals_set(self->globals);
Damien Georgec8f78bc2014-02-15 22:55:00 +0000331 mp_obj_t result;
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200332 DEBUG_printf("Calling: args=%p, n_args=%d, extra_args=%p, n_extra_args=%d\n", args, n_args, extra_args, n_extra_args);
333 dump_args(args, n_args);
334 dump_args(extra_args, n_extra_args);
Damien Georgebee17b02014-03-27 11:07:04 +0000335 mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code(self->bytecode, args, n_args, extra_args, n_extra_args, &result);
Damien Georged17926d2014-03-30 13:35:08 +0100336 mp_globals_set(old_globals);
Damien Georgefb083ea2014-02-01 18:29:40 +0000337
Damien Georgec8f78bc2014-02-15 22:55:00 +0000338 if (vm_return_kind == MP_VM_RETURN_NORMAL) {
339 return result;
340 } else { // MP_VM_RETURN_EXCEPTION
Damien Georgeea13f402014-04-05 18:32:08 +0100341 nlr_raise(result);
Damien Georgec8f78bc2014-02-15 22:55:00 +0000342 }
Damien George2e482cd2014-02-16 00:01:29 +0000343
344arg_error:
Damien Georgeea13f402014-04-05 18:32:08 +0100345 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));
Damiend99b0522013-12-21 18:17:45 +0000346}
347
Damien George3e1a5c12014-03-29 13:43:38 +0000348const mp_obj_type_t mp_type_fun_bc = {
Damien Georgec5966122014-02-15 16:10:44 +0000349 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000350 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +0000351 .call = fun_bc_call,
Paul Sokolovsky586bfce2014-04-05 13:50:06 +0300352 .binary_op = fun_binary_op,
Damiend99b0522013-12-21 18:17:45 +0000353};
354
Damien Georgebee17b02014-03-27 11:07:04 +0000355mp_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 George2e482cd2014-02-16 00:01:29 +0000356 uint n_def_args = 0;
357 uint n_extra_args = 0;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200358 mp_obj_tuple_t *def_args = def_args_in;
359 if (def_args != MP_OBJ_NULL) {
360 n_def_args = def_args->len;
Damien George2e482cd2014-02-16 00:01:29 +0000361 n_extra_args = def_args->len;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200362 }
Damien George2e482cd2014-02-16 00:01:29 +0000363 if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
364 n_extra_args += 1;
365 }
Damien Georgee5d371b2014-02-16 00:17:42 +0000366 if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
367 n_extra_args += 1;
368 }
Damien George2e482cd2014-02-16 00:01:29 +0000369 mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args);
Damien George3e1a5c12014-03-29 13:43:38 +0000370 o->base.type = &mp_type_fun_bc;
Damien Georged17926d2014-03-30 13:35:08 +0100371 o->globals = mp_globals_get();
Paul Sokolovskyac2e28c2014-02-16 18:30:49 +0200372 o->args = args;
Damiend99b0522013-12-21 18:17:45 +0000373 o->n_args = n_args;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200374 o->n_def_args = n_def_args;
Damien George2e482cd2014-02-16 00:01:29 +0000375 o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0;
376 o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0;
Damien George66028ab2014-01-03 14:03:48 +0000377 o->bytecode = code;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200378 if (def_args != MP_OBJ_NULL) {
Damien George2e482cd2014-02-16 00:01:29 +0000379 memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
Paul Sokolovsky90750022014-02-01 15:05:04 +0200380 }
Damiend99b0522013-12-21 18:17:45 +0000381 return o;
382}
383
Damien Georgebee17b02014-03-27 11:07:04 +0000384void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, const byte **code) {
Damien George3e1a5c12014-03-29 13:43:38 +0000385 assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_bc));
Damien George66028ab2014-01-03 14:03:48 +0000386 mp_obj_fun_bc_t *self = self_in;
387 *n_args = self->n_args;
Damien George66028ab2014-01-03 14:03:48 +0000388 *code = self->bytecode;
389}
390
Damiend99b0522013-12-21 18:17:45 +0000391/******************************************************************************/
392/* inline assembler functions */
393
394typedef struct _mp_obj_fun_asm_t {
395 mp_obj_base_t base;
396 int n_args;
397 void *fun;
398} mp_obj_fun_asm_t;
399
400typedef machine_uint_t (*inline_asm_fun_0_t)();
401typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
402typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
403typedef machine_uint_t (*inline_asm_fun_3_t)(machine_uint_t, machine_uint_t, machine_uint_t);
404
405// convert a Micro Python object to a sensible value for inline asm
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200406STATIC machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
Damiend99b0522013-12-21 18:17:45 +0000407 // TODO for byte_array, pass pointer to the array
408 if (MP_OBJ_IS_SMALL_INT(obj)) {
409 return MP_OBJ_SMALL_INT_VALUE(obj);
410 } else if (obj == mp_const_none) {
411 return 0;
412 } else if (obj == mp_const_false) {
413 return 0;
414 } else if (obj == mp_const_true) {
415 return 1;
Damien George5fa93b62014-01-22 14:35:10 +0000416 } else if (MP_OBJ_IS_STR(obj)) {
Damiend99b0522013-12-21 18:17:45 +0000417 // pointer to the string (it's probably constant though!)
Damien George5fa93b62014-01-22 14:35:10 +0000418 uint l;
419 return (machine_uint_t)mp_obj_str_get_data(obj, &l);
Damiend99b0522013-12-21 18:17:45 +0000420#if MICROPY_ENABLE_FLOAT
Damien George0c36da02014-03-08 15:24:39 +0000421 } else if (MP_OBJ_IS_TYPE(obj, &mp_type_float)) {
Damiend99b0522013-12-21 18:17:45 +0000422 // convert float to int (could also pass in float registers)
423 return (machine_int_t)mp_obj_float_get(obj);
424#endif
Damien George07ddab52014-03-29 13:15:08 +0000425 } else if (MP_OBJ_IS_TYPE(obj, &mp_type_tuple)) {
Damiend99b0522013-12-21 18:17:45 +0000426 // pointer to start of tuple (could pass length, but then could use len(x) for that)
427 uint len;
428 mp_obj_t *items;
429 mp_obj_tuple_get(obj, &len, &items);
430 return (machine_uint_t)items;
Damien George3e1a5c12014-03-29 13:43:38 +0000431 } else if (MP_OBJ_IS_TYPE(obj, &mp_type_list)) {
Damiend99b0522013-12-21 18:17:45 +0000432 // pointer to start of list (could pass length, but then could use len(x) for that)
433 uint len;
434 mp_obj_t *items;
435 mp_obj_list_get(obj, &len, &items);
436 return (machine_uint_t)items;
437 } else {
438 // just pass along a pointer to the object
439 return (machine_uint_t)obj;
440 }
441}
442
443// convert a return value from inline asm to a sensible Micro Python object
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200444STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
Damiend99b0522013-12-21 18:17:45 +0000445 return MP_OBJ_NEW_SMALL_INT(val);
446}
447
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200448STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
Damiend99b0522013-12-21 18:17:45 +0000449 mp_obj_fun_asm_t *self = self_in;
450
451 if (n_args != self->n_args) {
Damien Georgeea13f402014-04-05 18:32:08 +0100452 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));
Damiend99b0522013-12-21 18:17:45 +0000453 }
Damien George20006db2014-01-18 14:10:48 +0000454 if (n_kw != 0) {
Damien Georgeea13f402014-04-05 18:32:08 +0100455 nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
Damien George20006db2014-01-18 14:10:48 +0000456 }
Damiend99b0522013-12-21 18:17:45 +0000457
458 machine_uint_t ret;
459 if (n_args == 0) {
460 ret = ((inline_asm_fun_0_t)self->fun)();
461 } else if (n_args == 1) {
462 ret = ((inline_asm_fun_1_t)self->fun)(convert_obj_for_inline_asm(args[0]));
463 } else if (n_args == 2) {
Damien George20006db2014-01-18 14:10:48 +0000464 ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[0]), convert_obj_for_inline_asm(args[1]));
Damiend99b0522013-12-21 18:17:45 +0000465 } else if (n_args == 3) {
Damien George20006db2014-01-18 14:10:48 +0000466 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]));
Damiend99b0522013-12-21 18:17:45 +0000467 } else {
468 assert(0);
469 ret = 0;
470 }
471
472 return convert_val_from_inline_asm(ret);
473}
474
Damien George3e1a5c12014-03-29 13:43:38 +0000475STATIC const mp_obj_type_t mp_type_fun_asm = {
Damien Georgec5966122014-02-15 16:10:44 +0000476 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000477 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +0000478 .call = fun_asm_call,
Paul Sokolovsky586bfce2014-04-05 13:50:06 +0300479 .binary_op = fun_binary_op,
Damiend99b0522013-12-21 18:17:45 +0000480};
481
482mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) {
483 mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
Damien George3e1a5c12014-03-29 13:43:38 +0000484 o->base.type = &mp_type_fun_asm;
Damiend99b0522013-12-21 18:17:45 +0000485 o->n_args = n_args;
486 o->fun = fun;
487 return o;
488}