blob: 433da039bb2614e9e8173428fa4c7f4601225fa6 [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"
Damiend99b0522013-12-21 18:17:45 +000013#include "runtime.h"
14#include "bc.h"
15
16/******************************************************************************/
17/* native functions */
18
19// mp_obj_fun_native_t defined in obj.h
20
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +020021STATIC void check_nargs(mp_obj_fun_native_t *self, int n_args, int n_kw) {
John R. Lenton88cb1e62014-01-13 19:55:18 +000022 if (n_kw && !self->is_kw) {
Damien Georgec5966122014-02-15 16:10:44 +000023 nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000024 "function does not take keyword arguments"));
25 }
26
27 if (self->n_args_min == self->n_args_max) {
28 if (n_args != self->n_args_min) {
Damien Georgec5966122014-02-15 16:10:44 +000029 nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000030 "function takes %d positional arguments but %d were given",
Damien George099a9cb2014-02-12 23:02:19 +000031 self->n_args_min, n_args));
John R. Lenton88cb1e62014-01-13 19:55:18 +000032 }
33 } else {
34 if (n_args < self->n_args_min) {
Damien Georgec5966122014-02-15 16:10:44 +000035 nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000036 "<fun name>() missing %d required positional arguments: <list of names of params>",
Damien George099a9cb2014-02-12 23:02:19 +000037 self->n_args_min - n_args));
John R. Lenton88cb1e62014-01-13 19:55:18 +000038 } else if (n_args > self->n_args_max) {
Damien Georgec5966122014-02-15 16:10:44 +000039 nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
John R. Lenton88cb1e62014-01-13 19:55:18 +000040 "<fun name> expected at most %d arguments, got %d",
Damien George099a9cb2014-02-12 23:02:19 +000041 self->n_args_max, n_args));
John R. Lenton88cb1e62014-01-13 19:55:18 +000042 }
43 }
44}
45
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +020046STATIC 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 +000047 assert(MP_OBJ_IS_TYPE(self_in, &fun_native_type));
Damiend99b0522013-12-21 18:17:45 +000048 mp_obj_fun_native_t *self = self_in;
Damien George20006db2014-01-18 14:10:48 +000049
John R. Lenton88cb1e62014-01-13 19:55:18 +000050 // check number of arguments
Damien George20006db2014-01-18 14:10:48 +000051 check_nargs(self, n_args, n_kw);
52
John R. Lentonc06763a2014-01-07 17:29:16 +000053 if (self->is_kw) {
Damien George20006db2014-01-18 14:10:48 +000054 // function allows keywords
55
Damien George0a587b82014-02-08 18:53:41 +000056 // we create a map directly from the given args array
57 mp_map_t kw_args;
58 mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
Damien George20006db2014-01-18 14:10:48 +000059
Damien George0a587b82014-02-08 18:53:41 +000060 return ((mp_fun_kw_t)self->fun)(n_args, args, &kw_args);
Damien George20006db2014-01-18 14:10:48 +000061
Paul Sokolovsky9d95a2b2014-01-26 01:58:51 +020062 } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
Damiend99b0522013-12-21 18:17:45 +000063 // function requires a fixed number of arguments
64
Damiend99b0522013-12-21 18:17:45 +000065 // dispatch function call
66 switch (self->n_args_min) {
67 case 0:
68 return ((mp_fun_0_t)self->fun)();
69
70 case 1:
71 return ((mp_fun_1_t)self->fun)(args[0]);
72
73 case 2:
Damien George20006db2014-01-18 14:10:48 +000074 return ((mp_fun_2_t)self->fun)(args[0], args[1]);
Damiend99b0522013-12-21 18:17:45 +000075
John R. Lenton45a87442014-01-04 01:15:01 +000076 case 3:
Damien George20006db2014-01-18 14:10:48 +000077 return ((mp_fun_3_t)self->fun)(args[0], args[1], args[2]);
John R. Lenton45a87442014-01-04 01:15:01 +000078
Damiend99b0522013-12-21 18:17:45 +000079 default:
80 assert(0);
81 return mp_const_none;
82 }
83
84 } else {
Damien George20006db2014-01-18 14:10:48 +000085 // function takes a variable number of arguments, but no keywords
Damiend99b0522013-12-21 18:17:45 +000086
Damien George20006db2014-01-18 14:10:48 +000087 return ((mp_fun_var_t)self->fun)(n_args, args);
Damiend99b0522013-12-21 18:17:45 +000088 }
89}
90
91const mp_obj_type_t fun_native_type = {
Damien Georgec5966122014-02-15 16:10:44 +000092 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +000093 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +000094 .call = fun_native_call,
Damiend99b0522013-12-21 18:17:45 +000095};
96
Damien Georgef62d33a2014-01-13 19:50:05 +000097// fun must have the correct signature for n_args fixed arguments
98mp_obj_t rt_make_function_n(int n_args, void *fun) {
Damiend99b0522013-12-21 18:17:45 +000099 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
100 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800101 o->is_kw = false;
Damien Georgef62d33a2014-01-13 19:50:05 +0000102 o->n_args_min = n_args;
103 o->n_args_max = n_args;
John R. Lenton45a87442014-01-04 01:15:01 +0000104 o->fun = fun;
105 return o;
106}
107
Damiend99b0522013-12-21 18:17:45 +0000108mp_obj_t rt_make_function_var(int n_args_min, mp_fun_var_t fun) {
109 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
110 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800111 o->is_kw = false;
Damiend99b0522013-12-21 18:17:45 +0000112 o->n_args_min = n_args_min;
113 o->n_args_max = ~((machine_uint_t)0);
114 o->fun = fun;
115 return o;
116}
117
118// min and max are inclusive
119mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
120 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
121 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800122 o->is_kw = false;
Damiend99b0522013-12-21 18:17:45 +0000123 o->n_args_min = n_args_min;
124 o->n_args_max = n_args_max;
125 o->fun = fun;
126 return o;
127}
128
129/******************************************************************************/
130/* byte code functions */
131
132typedef struct _mp_obj_fun_bc_t {
133 mp_obj_base_t base;
Damien George66028ab2014-01-03 14:03:48 +0000134 mp_map_t *globals; // the context within which this function was defined
Paul Sokolovsky90750022014-02-01 15:05:04 +0200135 short n_args; // number of arguments this function takes
136 short n_def_args; // number of default arguments
Damien George66028ab2014-01-03 14:03:48 +0000137 uint n_state; // total state size for the executing function (incl args, locals, stack)
138 const byte *bytecode; // bytecode for the function
Paul Sokolovsky90750022014-02-01 15:05:04 +0200139 mp_obj_t def_args[]; // values of default args, if any
Damiend99b0522013-12-21 18:17:45 +0000140} mp_obj_fun_bc_t;
141
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200142STATIC 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 +0000143 mp_obj_fun_bc_t *self = self_in;
144
Paul Sokolovsky90750022014-02-01 15:05:04 +0200145 if (n_args < self->n_args - self->n_def_args || n_args > self->n_args) {
Damien Georgec5966122014-02-15 16:10:44 +0000146 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 +0000147 }
Damien George20006db2014-01-18 14:10:48 +0000148 if (n_kw != 0) {
Damien Georgec5966122014-02-15 16:10:44 +0000149 nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
Damien George20006db2014-01-18 14:10:48 +0000150 }
Damiend99b0522013-12-21 18:17:45 +0000151
Damien Georgefb083ea2014-02-01 18:29:40 +0000152 uint use_def_args = self->n_args - n_args;
Damien George66028ab2014-01-03 14:03:48 +0000153 mp_map_t *old_globals = rt_globals_get();
Damien Georgefb083ea2014-02-01 18:29:40 +0000154 rt_globals_set(self->globals);
155 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);
156 rt_globals_set(old_globals);
157
158 return result;
Damiend99b0522013-12-21 18:17:45 +0000159}
160
161const mp_obj_type_t fun_bc_type = {
Damien Georgec5966122014-02-15 16:10:44 +0000162 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000163 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +0000164 .call = fun_bc_call,
Damiend99b0522013-12-21 18:17:45 +0000165};
166
Paul Sokolovsky90750022014-02-01 15:05:04 +0200167mp_obj_t mp_obj_new_fun_bc(int n_args, mp_obj_t def_args_in, uint n_state, const byte *code) {
168 int n_def_args = 0;
169 mp_obj_tuple_t *def_args = def_args_in;
170 if (def_args != MP_OBJ_NULL) {
171 n_def_args = def_args->len;
172 }
173 mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_def_args);
Damiend99b0522013-12-21 18:17:45 +0000174 o->base.type = &fun_bc_type;
Damien George66028ab2014-01-03 14:03:48 +0000175 o->globals = rt_globals_get();
Damiend99b0522013-12-21 18:17:45 +0000176 o->n_args = n_args;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200177 o->n_def_args = n_def_args;
Damiend99b0522013-12-21 18:17:45 +0000178 o->n_state = n_state;
Damien George66028ab2014-01-03 14:03:48 +0000179 o->bytecode = code;
Paul Sokolovsky90750022014-02-01 15:05:04 +0200180 if (def_args != MP_OBJ_NULL) {
181 memcpy(o->def_args, def_args->items, n_def_args * sizeof(*o->def_args));
182 }
Damiend99b0522013-12-21 18:17:45 +0000183 return o;
184}
185
Damien George66028ab2014-01-03 14:03:48 +0000186void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code) {
187 assert(MP_OBJ_IS_TYPE(self_in, &fun_bc_type));
188 mp_obj_fun_bc_t *self = self_in;
189 *n_args = self->n_args;
190 *n_state = self->n_state;
191 *code = self->bytecode;
192}
193
Damiend99b0522013-12-21 18:17:45 +0000194/******************************************************************************/
195/* inline assembler functions */
196
197typedef struct _mp_obj_fun_asm_t {
198 mp_obj_base_t base;
199 int n_args;
200 void *fun;
201} mp_obj_fun_asm_t;
202
203typedef machine_uint_t (*inline_asm_fun_0_t)();
204typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
205typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
206typedef machine_uint_t (*inline_asm_fun_3_t)(machine_uint_t, machine_uint_t, machine_uint_t);
207
208// convert a Micro Python object to a sensible value for inline asm
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200209STATIC machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
Damiend99b0522013-12-21 18:17:45 +0000210 // TODO for byte_array, pass pointer to the array
211 if (MP_OBJ_IS_SMALL_INT(obj)) {
212 return MP_OBJ_SMALL_INT_VALUE(obj);
213 } else if (obj == mp_const_none) {
214 return 0;
215 } else if (obj == mp_const_false) {
216 return 0;
217 } else if (obj == mp_const_true) {
218 return 1;
Damien George5fa93b62014-01-22 14:35:10 +0000219 } else if (MP_OBJ_IS_STR(obj)) {
Damiend99b0522013-12-21 18:17:45 +0000220 // pointer to the string (it's probably constant though!)
Damien George5fa93b62014-01-22 14:35:10 +0000221 uint l;
222 return (machine_uint_t)mp_obj_str_get_data(obj, &l);
Damiend99b0522013-12-21 18:17:45 +0000223#if MICROPY_ENABLE_FLOAT
224 } else if (MP_OBJ_IS_TYPE(obj, &float_type)) {
225 // convert float to int (could also pass in float registers)
226 return (machine_int_t)mp_obj_float_get(obj);
227#endif
228 } else if (MP_OBJ_IS_TYPE(obj, &tuple_type)) {
229 // pointer to start of tuple (could pass length, but then could use len(x) for that)
230 uint len;
231 mp_obj_t *items;
232 mp_obj_tuple_get(obj, &len, &items);
233 return (machine_uint_t)items;
234 } else if (MP_OBJ_IS_TYPE(obj, &list_type)) {
235 // pointer to start of list (could pass length, but then could use len(x) for that)
236 uint len;
237 mp_obj_t *items;
238 mp_obj_list_get(obj, &len, &items);
239 return (machine_uint_t)items;
240 } else {
241 // just pass along a pointer to the object
242 return (machine_uint_t)obj;
243 }
244}
245
246// convert a return value from inline asm to a sensible Micro Python object
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200247STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
Damiend99b0522013-12-21 18:17:45 +0000248 return MP_OBJ_NEW_SMALL_INT(val);
249}
250
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200251STATIC 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 +0000252 mp_obj_fun_asm_t *self = self_in;
253
254 if (n_args != self->n_args) {
Damien Georgec5966122014-02-15 16:10:44 +0000255 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 +0000256 }
Damien George20006db2014-01-18 14:10:48 +0000257 if (n_kw != 0) {
Damien Georgec5966122014-02-15 16:10:44 +0000258 nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
Damien George20006db2014-01-18 14:10:48 +0000259 }
Damiend99b0522013-12-21 18:17:45 +0000260
261 machine_uint_t ret;
262 if (n_args == 0) {
263 ret = ((inline_asm_fun_0_t)self->fun)();
264 } else if (n_args == 1) {
265 ret = ((inline_asm_fun_1_t)self->fun)(convert_obj_for_inline_asm(args[0]));
266 } else if (n_args == 2) {
Damien George20006db2014-01-18 14:10:48 +0000267 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 +0000268 } else if (n_args == 3) {
Damien George20006db2014-01-18 14:10:48 +0000269 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 +0000270 } else {
271 assert(0);
272 ret = 0;
273 }
274
275 return convert_val_from_inline_asm(ret);
276}
277
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200278STATIC const mp_obj_type_t fun_asm_type = {
Damien Georgec5966122014-02-15 16:10:44 +0000279 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000280 .name = MP_QSTR_function,
Damien George20006db2014-01-18 14:10:48 +0000281 .call = fun_asm_call,
Damiend99b0522013-12-21 18:17:45 +0000282};
283
284mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) {
285 mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
286 o->base.type = &fun_asm_type;
287 o->n_args = n_args;
288 o->fun = fun;
289 return o;
290}