blob: b2837be5f0143eb9dd20c9ae4afbe833280400fc [file] [log] [blame]
Damiend99b0522013-12-21 18:17:45 +00001#include <stdlib.h>
2#include <stdint.h>
3#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 George66028ab2014-01-03 14:03:48 +000012#include "map.h"
Damien George2e482cd2014-02-16 00:01:29 +000013#include "runtime0.h"
Damiend99b0522013-12-21 18:17:45 +000014#include "runtime.h"
15#include "bc.h"
16
17/******************************************************************************/
18/* native functions */
19
20// mp_obj_fun_native_t defined in obj.h
21
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +020022STATIC void check_nargs(mp_obj_fun_native_t *self, int n_args, int n_kw) {
John R. Lenton88cb1e62014-01-13 19:55:18 +000023 if (n_kw && !self->is_kw) {
Damien Georgec5966122014-02-15 16:10:44 +000024 nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000025 "function does not take keyword arguments"));
26 }
27
28 if (self->n_args_min == self->n_args_max) {
29 if (n_args != self->n_args_min) {
Damien Georgec5966122014-02-15 16:10:44 +000030 nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000031 "function takes %d positional arguments but %d were given",
Damien George099a9cb2014-02-12 23:02:19 +000032 self->n_args_min, n_args));
John R. Lenton88cb1e62014-01-13 19:55:18 +000033 }
34 } else {
35 if (n_args < self->n_args_min) {
Damien Georgec5966122014-02-15 16:10:44 +000036 nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000037 "<fun name>() missing %d required positional arguments: <list of names of params>",
Damien George099a9cb2014-02-12 23:02:19 +000038 self->n_args_min - n_args));
John R. Lenton88cb1e62014-01-13 19:55:18 +000039 } else if (n_args > self->n_args_max) {
Damien Georgec5966122014-02-15 16:10:44 +000040 nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000041 "<fun name> expected at most %d arguments, got %d",
Damien George099a9cb2014-02-12 23:02:19 +000042 self->n_args_max, n_args));
John R. Lenton88cb1e62014-01-13 19:55:18 +000043 }
44 }
45}
46
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +020047STATIC mp_obj_t fun_native_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
Damien George20006db2014-01-18 14:10:48 +000048 assert(MP_OBJ_IS_TYPE(self_in, &fun_native_type));
Damiend99b0522013-12-21 18:17:45 +000049 mp_obj_fun_native_t *self = self_in;
Damien George20006db2014-01-18 14:10:48 +000050
John R. Lenton88cb1e62014-01-13 19:55:18 +000051 // check number of arguments
Damien George20006db2014-01-18 14:10:48 +000052 check_nargs(self, n_args, n_kw);
53
John R. Lentonc06763a2014-01-07 17:29:16 +000054 if (self->is_kw) {
Damien George20006db2014-01-18 14:10:48 +000055 // function allows keywords
56
Damien George0a587b82014-02-08 18:53:41 +000057 // we create a map directly from the given args array
58 mp_map_t kw_args;
59 mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
Damien George20006db2014-01-18 14:10:48 +000060
Damien George0a587b82014-02-08 18:53:41 +000061 return ((mp_fun_kw_t)self->fun)(n_args, args, &kw_args);
Damien George20006db2014-01-18 14:10:48 +000062
Paul Sokolovsky9d95a2b2014-01-26 01:58:51 +020063 } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
Damiend99b0522013-12-21 18:17:45 +000064 // function requires a fixed number of arguments
65
Damiend99b0522013-12-21 18:17:45 +000066 // dispatch function call
67 switch (self->n_args_min) {
68 case 0:
69 return ((mp_fun_0_t)self->fun)();
70
71 case 1:
72 return ((mp_fun_1_t)self->fun)(args[0]);
73
74 case 2:
Damien George20006db2014-01-18 14:10:48 +000075 return ((mp_fun_2_t)self->fun)(args[0], args[1]);
Damiend99b0522013-12-21 18:17:45 +000076
John R. Lenton45a87442014-01-04 01:15:01 +000077 case 3:
Damien George20006db2014-01-18 14:10:48 +000078 return ((mp_fun_3_t)self->fun)(args[0], args[1], args[2]);
John R. Lenton45a87442014-01-04 01:15:01 +000079
Damiend99b0522013-12-21 18:17:45 +000080 default:
81 assert(0);
82 return mp_const_none;
83 }
84
85 } else {
Damien George20006db2014-01-18 14:10:48 +000086 // function takes a variable number of arguments, but no keywords
Damiend99b0522013-12-21 18:17:45 +000087
Damien George20006db2014-01-18 14:10:48 +000088 return ((mp_fun_var_t)self->fun)(n_args, args);
Damiend99b0522013-12-21 18:17:45 +000089 }
90}
91
92const mp_obj_type_t fun_native_type = {
Damien Georgec5966122014-02-15 16:10:44 +000093 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +000094 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +000095 .call = fun_native_call,
Damiend99b0522013-12-21 18:17:45 +000096};
97
Damien Georgef62d33a2014-01-13 19:50:05 +000098// fun must have the correct signature for n_args fixed arguments
99mp_obj_t rt_make_function_n(int n_args, void *fun) {
Damiend99b0522013-12-21 18:17:45 +0000100 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
101 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800102 o->is_kw = false;
Damien Georgef62d33a2014-01-13 19:50:05 +0000103 o->n_args_min = n_args;
104 o->n_args_max = n_args;
John R. Lenton45a87442014-01-04 01:15:01 +0000105 o->fun = fun;
106 return o;
107}
108
Damiend99b0522013-12-21 18:17:45 +0000109mp_obj_t rt_make_function_var(int n_args_min, mp_fun_var_t fun) {
110 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
111 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800112 o->is_kw = false;
Damiend99b0522013-12-21 18:17:45 +0000113 o->n_args_min = n_args_min;
114 o->n_args_max = ~((machine_uint_t)0);
115 o->fun = fun;
116 return o;
117}
118
119// min and max are inclusive
120mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
121 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
122 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800123 o->is_kw = false;
Damiend99b0522013-12-21 18:17:45 +0000124 o->n_args_min = n_args_min;
125 o->n_args_max = n_args_max;
126 o->fun = fun;
127 return o;
128}
129
130/******************************************************************************/
131/* byte code functions */
132
133typedef struct _mp_obj_fun_bc_t {
134 mp_obj_base_t base;
Damien George66028ab2014-01-03 14:03:48 +0000135 mp_map_t *globals; // the context within which this function was defined
Damien George2e482cd2014-02-16 00:01:29 +0000136 struct {
137 machine_uint_t n_args : 15; // number of arguments this function takes
138 machine_uint_t n_def_args : 15; // number of default arguments
139 machine_uint_t takes_var_args : 1; // set if this function takes variable args
140 machine_uint_t takes_kw_args : 1; // set if this function takes keyword args
141 };
Damien George66028ab2014-01-03 14:03:48 +0000142 uint n_state; // total state size for the executing function (incl args, locals, stack)
143 const byte *bytecode; // bytecode for the function
Damien Georgee5d371b2014-02-16 00:17:42 +0000144 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 +0000145} mp_obj_fun_bc_t;
146
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200147STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
Damiend99b0522013-12-21 18:17:45 +0000148 mp_obj_fun_bc_t *self = self_in;
149
Damien Georgee5d371b2014-02-16 00:17:42 +0000150 const mp_obj_t *kwargs = args + n_args;
Damien George2e482cd2014-02-16 00:01:29 +0000151 mp_obj_t *extra_args = self->extra_args + self->n_def_args;
152 uint n_extra_args = 0;
153
Damien Georgee5d371b2014-02-16 00:17:42 +0000154 // check positional arguments
155
Damien George2e482cd2014-02-16 00:01:29 +0000156 if (n_args > self->n_args) {
157 // given more than enough arguments
158 if (!self->takes_var_args) {
159 goto arg_error;
160 }
161 // put extra arguments in varargs tuple
162 *extra_args = mp_obj_new_tuple(n_args - self->n_args, args + self->n_args);
163 n_extra_args = 1;
164 n_args = self->n_args;
165 } else if (n_args >= self->n_args - self->n_def_args) {
166 // given enough arguments, but may need to use some default arguments
167 if (self->takes_var_args) {
168 *extra_args = mp_const_empty_tuple;
169 n_extra_args = 1;
170 }
171 extra_args -= self->n_args - n_args;
172 n_extra_args += self->n_args - n_args;
173 } else {
174 goto arg_error;
Damiend99b0522013-12-21 18:17:45 +0000175 }
Damien George2e482cd2014-02-16 00:01:29 +0000176
Damien Georgee5d371b2014-02-16 00:17:42 +0000177 // check keyword arguments
178
Damien George20006db2014-01-18 14:10:48 +0000179 if (n_kw != 0) {
Damien Georgee5d371b2014-02-16 00:17:42 +0000180 // keyword arguments given
181 if (!self->takes_kw_args) {
182 nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
183 }
184 mp_obj_t dict = mp_obj_new_dict(n_kw);
185 for (uint i = 0; i < n_kw; i++) {
186 mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
187 }
188 extra_args[n_extra_args] = dict;
189 n_extra_args += 1;
190 } else {
191 // no keyword arguments given
192 if (self->takes_kw_args) {
193 extra_args[n_extra_args] = mp_obj_new_dict(0);
194 n_extra_args += 1;
195 }
Damien George20006db2014-01-18 14:10:48 +0000196 }
Damiend99b0522013-12-21 18:17:45 +0000197
Damien George66028ab2014-01-03 14:03:48 +0000198 mp_map_t *old_globals = rt_globals_get();
Damien Georgefb083ea2014-02-01 18:29:40 +0000199 rt_globals_set(self->globals);
Damien Georgec8f78bc2014-02-15 22:55:00 +0000200 mp_obj_t result;
Damien George2e482cd2014-02-16 00:01:29 +0000201 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);
Damien Georgefb083ea2014-02-01 18:29:40 +0000202 rt_globals_set(old_globals);
203
Damien Georgec8f78bc2014-02-15 22:55:00 +0000204 if (vm_return_kind == MP_VM_RETURN_NORMAL) {
205 return result;
206 } else { // MP_VM_RETURN_EXCEPTION
207 nlr_jump(result);
208 }
Damien George2e482cd2014-02-16 00:01:29 +0000209
210arg_error:
211 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));
Damiend99b0522013-12-21 18:17:45 +0000212}
213
214const mp_obj_type_t fun_bc_type = {
Damien Georgec5966122014-02-15 16:10:44 +0000215 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000216 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +0000217 .call = fun_bc_call,
Damiend99b0522013-12-21 18:17:45 +0000218};
219
Damien George2e482cd2014-02-16 00:01:29 +0000220mp_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) {
221 uint n_def_args = 0;
222 uint n_extra_args = 0;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200223 mp_obj_tuple_t *def_args = def_args_in;
224 if (def_args != MP_OBJ_NULL) {
225 n_def_args = def_args->len;
Damien George2e482cd2014-02-16 00:01:29 +0000226 n_extra_args = def_args->len;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200227 }
Damien George2e482cd2014-02-16 00:01:29 +0000228 if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
229 n_extra_args += 1;
230 }
Damien Georgee5d371b2014-02-16 00:17:42 +0000231 if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
232 n_extra_args += 1;
233 }
Damien George2e482cd2014-02-16 00:01:29 +0000234 mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args);
Damiend99b0522013-12-21 18:17:45 +0000235 o->base.type = &fun_bc_type;
Damien George66028ab2014-01-03 14:03:48 +0000236 o->globals = rt_globals_get();
Damiend99b0522013-12-21 18:17:45 +0000237 o->n_args = n_args;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200238 o->n_def_args = n_def_args;
Damien George2e482cd2014-02-16 00:01:29 +0000239 o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0;
240 o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0;
Damiend99b0522013-12-21 18:17:45 +0000241 o->n_state = n_state;
Damien George66028ab2014-01-03 14:03:48 +0000242 o->bytecode = code;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200243 if (def_args != MP_OBJ_NULL) {
Damien George2e482cd2014-02-16 00:01:29 +0000244 memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
Paul Sokolovsky90750022014-02-01 15:05:04 +0200245 }
Damiend99b0522013-12-21 18:17:45 +0000246 return o;
247}
248
Damien George66028ab2014-01-03 14:03:48 +0000249void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code) {
250 assert(MP_OBJ_IS_TYPE(self_in, &fun_bc_type));
251 mp_obj_fun_bc_t *self = self_in;
252 *n_args = self->n_args;
253 *n_state = self->n_state;
254 *code = self->bytecode;
255}
256
Damiend99b0522013-12-21 18:17:45 +0000257/******************************************************************************/
258/* inline assembler functions */
259
260typedef struct _mp_obj_fun_asm_t {
261 mp_obj_base_t base;
262 int n_args;
263 void *fun;
264} mp_obj_fun_asm_t;
265
266typedef machine_uint_t (*inline_asm_fun_0_t)();
267typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
268typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
269typedef machine_uint_t (*inline_asm_fun_3_t)(machine_uint_t, machine_uint_t, machine_uint_t);
270
271// convert a Micro Python object to a sensible value for inline asm
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200272STATIC machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
Damiend99b0522013-12-21 18:17:45 +0000273 // TODO for byte_array, pass pointer to the array
274 if (MP_OBJ_IS_SMALL_INT(obj)) {
275 return MP_OBJ_SMALL_INT_VALUE(obj);
276 } else if (obj == mp_const_none) {
277 return 0;
278 } else if (obj == mp_const_false) {
279 return 0;
280 } else if (obj == mp_const_true) {
281 return 1;
Damien George5fa93b62014-01-22 14:35:10 +0000282 } else if (MP_OBJ_IS_STR(obj)) {
Damiend99b0522013-12-21 18:17:45 +0000283 // pointer to the string (it's probably constant though!)
Damien George5fa93b62014-01-22 14:35:10 +0000284 uint l;
285 return (machine_uint_t)mp_obj_str_get_data(obj, &l);
Damiend99b0522013-12-21 18:17:45 +0000286#if MICROPY_ENABLE_FLOAT
287 } else if (MP_OBJ_IS_TYPE(obj, &float_type)) {
288 // convert float to int (could also pass in float registers)
289 return (machine_int_t)mp_obj_float_get(obj);
290#endif
291 } else if (MP_OBJ_IS_TYPE(obj, &tuple_type)) {
292 // pointer to start of tuple (could pass length, but then could use len(x) for that)
293 uint len;
294 mp_obj_t *items;
295 mp_obj_tuple_get(obj, &len, &items);
296 return (machine_uint_t)items;
297 } else if (MP_OBJ_IS_TYPE(obj, &list_type)) {
298 // pointer to start of list (could pass length, but then could use len(x) for that)
299 uint len;
300 mp_obj_t *items;
301 mp_obj_list_get(obj, &len, &items);
302 return (machine_uint_t)items;
303 } else {
304 // just pass along a pointer to the object
305 return (machine_uint_t)obj;
306 }
307}
308
309// convert a return value from inline asm to a sensible Micro Python object
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200310STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
Damiend99b0522013-12-21 18:17:45 +0000311 return MP_OBJ_NEW_SMALL_INT(val);
312}
313
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200314STATIC 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 +0000315 mp_obj_fun_asm_t *self = self_in;
316
317 if (n_args != self->n_args) {
Damien Georgec5966122014-02-15 16:10:44 +0000318 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));
Damiend99b0522013-12-21 18:17:45 +0000319 }
Damien George20006db2014-01-18 14:10:48 +0000320 if (n_kw != 0) {
Damien Georgec5966122014-02-15 16:10:44 +0000321 nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
Damien George20006db2014-01-18 14:10:48 +0000322 }
Damiend99b0522013-12-21 18:17:45 +0000323
324 machine_uint_t ret;
325 if (n_args == 0) {
326 ret = ((inline_asm_fun_0_t)self->fun)();
327 } else if (n_args == 1) {
328 ret = ((inline_asm_fun_1_t)self->fun)(convert_obj_for_inline_asm(args[0]));
329 } else if (n_args == 2) {
Damien George20006db2014-01-18 14:10:48 +0000330 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 +0000331 } else if (n_args == 3) {
Damien George20006db2014-01-18 14:10:48 +0000332 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 +0000333 } else {
334 assert(0);
335 ret = 0;
336 }
337
338 return convert_val_from_inline_asm(ret);
339}
340
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200341STATIC const mp_obj_type_t fun_asm_type = {
Damien Georgec5966122014-02-15 16:10:44 +0000342 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000343 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +0000344 .call = fun_asm_call,
Damiend99b0522013-12-21 18:17:45 +0000345};
346
347mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) {
348 mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
349 o->base.type = &fun_asm_type;
350 o->n_args = n_args;
351 o->fun = fun;
352 return o;
353}