blob: 3697c6430320b4f8a67d40aca1af5a7ef1da8fcf [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 Georgeeb7bfcb2014-01-04 15:57:35 +00009#include "mpqstr.h"
Damiend99b0522013-12-21 18:17:45 +000010#include "obj.h"
Damien George66028ab2014-01-03 14:03:48 +000011#include "map.h"
Damiend99b0522013-12-21 18:17:45 +000012#include "runtime.h"
13#include "bc.h"
14
15/******************************************************************************/
16/* native functions */
17
18// mp_obj_fun_native_t defined in obj.h
19
John R. Lentonc06763a2014-01-07 17:29:16 +000020mp_obj_t fun_native_call_n_kw(mp_obj_t self_in, int n_args, int n_kw, const mp_obj_t *args);
Damiend99b0522013-12-21 18:17:45 +000021// args are in reverse order in the array
22mp_obj_t fun_native_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
23 mp_obj_fun_native_t *self = self_in;
John R. Lentonc06763a2014-01-07 17:29:16 +000024 if (self->is_kw) {
25 return fun_native_call_n_kw(self_in, n_args, 0, args);
26 }
Damiend99b0522013-12-21 18:17:45 +000027 if (self->n_args_min == self->n_args_max) {
28 // function requires a fixed number of arguments
29
30 // check number of arguments
31 if (n_args != self->n_args_min) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +000032 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));
Damiend99b0522013-12-21 18:17:45 +000033 }
34
35 // dispatch function call
36 switch (self->n_args_min) {
37 case 0:
38 return ((mp_fun_0_t)self->fun)();
39
40 case 1:
41 return ((mp_fun_1_t)self->fun)(args[0]);
42
43 case 2:
44 return ((mp_fun_2_t)self->fun)(args[1], args[0]);
45
John R. Lenton45a87442014-01-04 01:15:01 +000046 case 3:
47 return ((mp_fun_3_t)self->fun)(args[2], args[1], args[0]);
48
Damiend99b0522013-12-21 18:17:45 +000049 default:
50 assert(0);
51 return mp_const_none;
52 }
53
54 } else {
55 // function takes a variable number of arguments
56
57 if (n_args < self->n_args_min) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +000058 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)));
Damiend99b0522013-12-21 18:17:45 +000059 } else if (n_args > self->n_args_max) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +000060 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));
Damiend99b0522013-12-21 18:17:45 +000061 }
62
63 // 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
64 mp_obj_t *args_ordered = m_new(mp_obj_t, n_args);
65 for (int i = 0; i < n_args; i++) {
66 args_ordered[i] = args[n_args - i - 1];
67 }
68
69 mp_obj_t res = ((mp_fun_var_t)self->fun)(n_args, args_ordered);
Damien732407f2013-12-29 19:33:23 +000070 m_del(mp_obj_t, args_ordered, n_args);
Damiend99b0522013-12-21 18:17:45 +000071
72 return res;
73 }
74}
75
John R. Lentonc06763a2014-01-07 17:29:16 +000076mp_obj_t fun_native_call_n_kw(mp_obj_t self_in, int n_args, int n_kw, const mp_obj_t *args) {
77 mp_obj_fun_native_t *self = self_in;
78
79 if (!self->is_kw) {
80 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "function does not take keyword arguments"));
81 }
82
83 mp_obj_t *vargs = mp_obj_new_tuple_reverse(n_args, args + 2*n_kw);
84 mp_map_t *kw_args = mp_map_new(MP_MAP_QSTR, n_kw);
85 for (int i = 0; i < 2*n_kw; i+=2) {
86 qstr name = mp_obj_str_get(args[i+1]);
87 mp_qstr_map_lookup(kw_args, name, true)->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 return res;
92}
93
Damiend99b0522013-12-21 18:17:45 +000094const mp_obj_type_t fun_native_type = {
John R. Lentonc06763a2014-01-07 17:29:16 +000095 .base = { &mp_const_type },
96 .name = "function",
97 .call_n = fun_native_call_n,
98 .call_n_kw = fun_native_call_n_kw,
Paul Sokolovsky860ffb02014-01-05 22:34:09 +020099 .methods = {
Damiend99b0522013-12-21 18:17:45 +0000100 {NULL, NULL}, // end-of-list sentinel
101 },
102};
103
104mp_obj_t rt_make_function_0(mp_fun_0_t fun) {
105 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
106 o->base.type = &fun_native_type;
107 o->n_args_min = 0;
108 o->n_args_max = 0;
109 o->fun = fun;
110 return o;
111}
112
113mp_obj_t rt_make_function_1(mp_fun_1_t fun) {
114 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
115 o->base.type = &fun_native_type;
116 o->n_args_min = 1;
117 o->n_args_max = 1;
118 o->fun = fun;
119 return o;
120}
121
122mp_obj_t rt_make_function_2(mp_fun_2_t fun) {
123 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
124 o->base.type = &fun_native_type;
125 o->n_args_min = 2;
126 o->n_args_max = 2;
127 o->fun = fun;
128 return o;
129}
130
John R. Lenton45a87442014-01-04 01:15:01 +0000131mp_obj_t rt_make_function_3(mp_fun_3_t fun) {
132 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
133 o->base.type = &fun_native_type;
134 o->n_args_min = 3;
135 o->n_args_max = 3;
136 o->fun = fun;
137 return o;
138}
139
Damiend99b0522013-12-21 18:17:45 +0000140mp_obj_t rt_make_function_var(int n_args_min, mp_fun_var_t fun) {
141 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
142 o->base.type = &fun_native_type;
143 o->n_args_min = n_args_min;
144 o->n_args_max = ~((machine_uint_t)0);
145 o->fun = fun;
146 return o;
147}
148
149// min and max are inclusive
150mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
151 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
152 o->base.type = &fun_native_type;
153 o->n_args_min = n_args_min;
154 o->n_args_max = n_args_max;
155 o->fun = fun;
156 return o;
157}
158
159/******************************************************************************/
160/* byte code functions */
161
162typedef struct _mp_obj_fun_bc_t {
163 mp_obj_base_t base;
Damien George66028ab2014-01-03 14:03:48 +0000164 mp_map_t *globals; // the context within which this function was defined
165 int n_args; // number of arguments this function takes
166 uint n_state; // total state size for the executing function (incl args, locals, stack)
167 const byte *bytecode; // bytecode for the function
Damiend99b0522013-12-21 18:17:45 +0000168} mp_obj_fun_bc_t;
169
170// args are in reverse order in the array
171mp_obj_t fun_bc_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
172 mp_obj_fun_bc_t *self = self_in;
173
174 if (n_args != self->n_args) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +0000175 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));
Damiend99b0522013-12-21 18:17:45 +0000176 }
177
Damien George66028ab2014-01-03 14:03:48 +0000178 // optimisation: allow the compiler to optimise this tail call for
179 // the common case when the globals don't need to be changed
180 mp_map_t *old_globals = rt_globals_get();
181 if (self->globals == old_globals) {
182 return mp_execute_byte_code(self->bytecode, args, n_args, self->n_state);
183 } else {
184 rt_globals_set(self->globals);
185 mp_obj_t result = mp_execute_byte_code(self->bytecode, args, n_args, self->n_state);
186 rt_globals_set(old_globals);
187 return result;
188 }
Damiend99b0522013-12-21 18:17:45 +0000189}
190
191const mp_obj_type_t fun_bc_type = {
John R. Lentonc06763a2014-01-07 17:29:16 +0000192 .base = { &mp_const_type },
193 .name = "function",
194 .call_n = fun_bc_call_n,
Paul Sokolovsky860ffb02014-01-05 22:34:09 +0200195 .methods = {
Damiend99b0522013-12-21 18:17:45 +0000196 {NULL, NULL}, // end-of-list sentinel
197 },
198};
199
200mp_obj_t mp_obj_new_fun_bc(int n_args, uint n_state, const byte *code) {
201 mp_obj_fun_bc_t *o = m_new_obj(mp_obj_fun_bc_t);
202 o->base.type = &fun_bc_type;
Damien George66028ab2014-01-03 14:03:48 +0000203 o->globals = rt_globals_get();
Damiend99b0522013-12-21 18:17:45 +0000204 o->n_args = n_args;
205 o->n_state = n_state;
Damien George66028ab2014-01-03 14:03:48 +0000206 o->bytecode = code;
Damiend99b0522013-12-21 18:17:45 +0000207 return o;
208}
209
Damien George66028ab2014-01-03 14:03:48 +0000210void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code) {
211 assert(MP_OBJ_IS_TYPE(self_in, &fun_bc_type));
212 mp_obj_fun_bc_t *self = self_in;
213 *n_args = self->n_args;
214 *n_state = self->n_state;
215 *code = self->bytecode;
216}
217
Damiend99b0522013-12-21 18:17:45 +0000218/******************************************************************************/
219/* inline assembler functions */
220
221typedef struct _mp_obj_fun_asm_t {
222 mp_obj_base_t base;
223 int n_args;
224 void *fun;
225} mp_obj_fun_asm_t;
226
227typedef machine_uint_t (*inline_asm_fun_0_t)();
228typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
229typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
230typedef machine_uint_t (*inline_asm_fun_3_t)(machine_uint_t, machine_uint_t, machine_uint_t);
231
232// convert a Micro Python object to a sensible value for inline asm
233machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
234 // TODO for byte_array, pass pointer to the array
235 if (MP_OBJ_IS_SMALL_INT(obj)) {
236 return MP_OBJ_SMALL_INT_VALUE(obj);
237 } else if (obj == mp_const_none) {
238 return 0;
239 } else if (obj == mp_const_false) {
240 return 0;
241 } else if (obj == mp_const_true) {
242 return 1;
243 } else if (MP_OBJ_IS_TYPE(obj, &str_type)) {
244 // pointer to the string (it's probably constant though!)
245 return (machine_uint_t)qstr_str(mp_obj_str_get(obj));
246#if MICROPY_ENABLE_FLOAT
247 } else if (MP_OBJ_IS_TYPE(obj, &float_type)) {
248 // convert float to int (could also pass in float registers)
249 return (machine_int_t)mp_obj_float_get(obj);
250#endif
251 } else if (MP_OBJ_IS_TYPE(obj, &tuple_type)) {
252 // pointer to start of tuple (could pass length, but then could use len(x) for that)
253 uint len;
254 mp_obj_t *items;
255 mp_obj_tuple_get(obj, &len, &items);
256 return (machine_uint_t)items;
257 } else if (MP_OBJ_IS_TYPE(obj, &list_type)) {
258 // pointer to start of list (could pass length, but then could use len(x) for that)
259 uint len;
260 mp_obj_t *items;
261 mp_obj_list_get(obj, &len, &items);
262 return (machine_uint_t)items;
263 } else {
264 // just pass along a pointer to the object
265 return (machine_uint_t)obj;
266 }
267}
268
269// convert a return value from inline asm to a sensible Micro Python object
270mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
271 return MP_OBJ_NEW_SMALL_INT(val);
272}
273
274// args are in reverse order in the array
275mp_obj_t fun_asm_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
276 mp_obj_fun_asm_t *self = self_in;
277
278 if (n_args != self->n_args) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +0000279 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));
Damiend99b0522013-12-21 18:17:45 +0000280 }
281
282 machine_uint_t ret;
283 if (n_args == 0) {
284 ret = ((inline_asm_fun_0_t)self->fun)();
285 } else if (n_args == 1) {
286 ret = ((inline_asm_fun_1_t)self->fun)(convert_obj_for_inline_asm(args[0]));
287 } else if (n_args == 2) {
288 ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[1]), convert_obj_for_inline_asm(args[0]));
289 } else if (n_args == 3) {
290 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]));
291 } else {
292 assert(0);
293 ret = 0;
294 }
295
296 return convert_val_from_inline_asm(ret);
297}
298
299static const mp_obj_type_t fun_asm_type = {
John R. Lentonc06763a2014-01-07 17:29:16 +0000300 .base = { &mp_const_type },
301 .name = "function",
302 .call_n = fun_asm_call_n,
Paul Sokolovsky860ffb02014-01-05 22:34:09 +0200303 .methods = {
Damiend99b0522013-12-21 18:17:45 +0000304 {NULL, NULL}, // end-of-list sentinel
305 },
306};
307
308mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) {
309 mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
310 o->base.type = &fun_asm_type;
311 o->n_args = n_args;
312 o->fun = fun;
313 return o;
314}