blob: 1f6ad68ea43a35b2bd39b40ac2cab30936da539d [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"
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. Lenton88cb1e62014-01-13 19:55:18 +000020void check_nargs(mp_obj_fun_native_t *self, int n_args, int n_kw) {
21 if (n_kw && !self->is_kw) {
22 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError,
23 "function does not take keyword arguments"));
24 }
25
26 if (self->n_args_min == self->n_args_max) {
27 if (n_args != self->n_args_min) {
28 nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_TypeError,
29 "function takes %d positional arguments but %d were given",
30 (const char*)(machine_int_t)self->n_args_min,
31 (const char*)(machine_int_t)n_args));
32 }
33 } else {
34 if (n_args < self->n_args_min) {
35 nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError,
36 "<fun name>() missing %d required positional arguments: <list of names of params>",
37 (const char*)(machine_int_t)(self->n_args_min - n_args)));
38 } else if (n_args > self->n_args_max) {
39 nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_TypeError,
40 "<fun name> expected at most %d arguments, got %d",
41 (void*)(machine_int_t)self->n_args_max, (void*)(machine_int_t)n_args));
42 }
43 }
44}
45
Damien George20006db2014-01-18 14:10:48 +000046mp_obj_t fun_native_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
47 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
56 // TODO if n_kw==0 then don't allocate any memory for map (either pass NULL or allocate it on the heap)
57 mp_map_t *kw_args = mp_map_new(n_kw);
58 for (int i = 0; i < 2 * n_kw; i += 2) {
Damien George5fa93b62014-01-22 14:35:10 +000059 mp_map_lookup(kw_args, args[n_args + i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = args[n_args + i + 1];
Damien George20006db2014-01-18 14:10:48 +000060 }
61 mp_obj_t res = ((mp_fun_kw_t)self->fun)(n_args, args, kw_args);
62 // TODO clean up kw_args
63
64 return res;
65
66 } else if (self->n_args_min == self->n_args_max) {
Damiend99b0522013-12-21 18:17:45 +000067 // function requires a fixed number of arguments
68
Damiend99b0522013-12-21 18:17:45 +000069 // dispatch function call
70 switch (self->n_args_min) {
71 case 0:
72 return ((mp_fun_0_t)self->fun)();
73
74 case 1:
75 return ((mp_fun_1_t)self->fun)(args[0]);
76
77 case 2:
Damien George20006db2014-01-18 14:10:48 +000078 return ((mp_fun_2_t)self->fun)(args[0], args[1]);
Damiend99b0522013-12-21 18:17:45 +000079
John R. Lenton45a87442014-01-04 01:15:01 +000080 case 3:
Damien George20006db2014-01-18 14:10:48 +000081 return ((mp_fun_3_t)self->fun)(args[0], args[1], args[2]);
John R. Lenton45a87442014-01-04 01:15:01 +000082
Damiend99b0522013-12-21 18:17:45 +000083 default:
84 assert(0);
85 return mp_const_none;
86 }
87
88 } else {
Damien George20006db2014-01-18 14:10:48 +000089 // function takes a variable number of arguments, but no keywords
Damiend99b0522013-12-21 18:17:45 +000090
Damien George20006db2014-01-18 14:10:48 +000091 return ((mp_fun_var_t)self->fun)(n_args, args);
Damiend99b0522013-12-21 18:17:45 +000092 }
93}
94
95const mp_obj_type_t fun_native_type = {
96 { &mp_const_type },
97 "function",
Damien George20006db2014-01-18 14:10:48 +000098 .call = fun_native_call,
Damiend99b0522013-12-21 18:17:45 +000099};
100
Damien Georgef62d33a2014-01-13 19:50:05 +0000101// fun must have the correct signature for n_args fixed arguments
102mp_obj_t rt_make_function_n(int n_args, void *fun) {
Damiend99b0522013-12-21 18:17:45 +0000103 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
104 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800105 o->is_kw = false;
Damien Georgef62d33a2014-01-13 19:50:05 +0000106 o->n_args_min = n_args;
107 o->n_args_max = n_args;
John R. Lenton45a87442014-01-04 01:15:01 +0000108 o->fun = fun;
109 return o;
110}
111
Damiend99b0522013-12-21 18:17:45 +0000112mp_obj_t rt_make_function_var(int n_args_min, mp_fun_var_t fun) {
113 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
114 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800115 o->is_kw = false;
Damiend99b0522013-12-21 18:17:45 +0000116 o->n_args_min = n_args_min;
117 o->n_args_max = ~((machine_uint_t)0);
118 o->fun = fun;
119 return o;
120}
121
122// min and max are inclusive
123mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
124 mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
125 o->base.type = &fun_native_type;
Dave Hylands44332ec2014-01-13 08:42:43 -0800126 o->is_kw = false;
Damiend99b0522013-12-21 18:17:45 +0000127 o->n_args_min = n_args_min;
128 o->n_args_max = n_args_max;
129 o->fun = fun;
130 return o;
131}
132
133/******************************************************************************/
134/* byte code functions */
135
136typedef struct _mp_obj_fun_bc_t {
137 mp_obj_base_t base;
Damien George66028ab2014-01-03 14:03:48 +0000138 mp_map_t *globals; // the context within which this function was defined
139 int n_args; // number of arguments this function takes
140 uint n_state; // total state size for the executing function (incl args, locals, stack)
141 const byte *bytecode; // bytecode for the function
Damiend99b0522013-12-21 18:17:45 +0000142} mp_obj_fun_bc_t;
143
Damien George20006db2014-01-18 14:10:48 +0000144mp_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 +0000145 mp_obj_fun_bc_t *self = self_in;
146
147 if (n_args != self->n_args) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +0000148 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 +0000149 }
Damien George20006db2014-01-18 14:10:48 +0000150 if (n_kw != 0) {
151 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "function does not take keyword arguments"));
152 }
Damiend99b0522013-12-21 18:17:45 +0000153
Damien George66028ab2014-01-03 14:03:48 +0000154 // optimisation: allow the compiler to optimise this tail call for
155 // the common case when the globals don't need to be changed
156 mp_map_t *old_globals = rt_globals_get();
157 if (self->globals == old_globals) {
158 return mp_execute_byte_code(self->bytecode, args, n_args, self->n_state);
159 } else {
160 rt_globals_set(self->globals);
161 mp_obj_t result = mp_execute_byte_code(self->bytecode, args, n_args, self->n_state);
162 rt_globals_set(old_globals);
163 return result;
164 }
Damiend99b0522013-12-21 18:17:45 +0000165}
166
167const mp_obj_type_t fun_bc_type = {
168 { &mp_const_type },
169 "function",
Damien George20006db2014-01-18 14:10:48 +0000170 .call = fun_bc_call,
Damiend99b0522013-12-21 18:17:45 +0000171};
172
173mp_obj_t mp_obj_new_fun_bc(int n_args, uint n_state, const byte *code) {
174 mp_obj_fun_bc_t *o = m_new_obj(mp_obj_fun_bc_t);
175 o->base.type = &fun_bc_type;
Damien George66028ab2014-01-03 14:03:48 +0000176 o->globals = rt_globals_get();
Damiend99b0522013-12-21 18:17:45 +0000177 o->n_args = n_args;
178 o->n_state = n_state;
Damien George66028ab2014-01-03 14:03:48 +0000179 o->bytecode = code;
Damiend99b0522013-12-21 18:17:45 +0000180 return o;
181}
182
Damien George66028ab2014-01-03 14:03:48 +0000183void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code) {
184 assert(MP_OBJ_IS_TYPE(self_in, &fun_bc_type));
185 mp_obj_fun_bc_t *self = self_in;
186 *n_args = self->n_args;
187 *n_state = self->n_state;
188 *code = self->bytecode;
189}
190
Damiend99b0522013-12-21 18:17:45 +0000191/******************************************************************************/
192/* inline assembler functions */
193
194typedef struct _mp_obj_fun_asm_t {
195 mp_obj_base_t base;
196 int n_args;
197 void *fun;
198} mp_obj_fun_asm_t;
199
200typedef machine_uint_t (*inline_asm_fun_0_t)();
201typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
202typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
203typedef machine_uint_t (*inline_asm_fun_3_t)(machine_uint_t, machine_uint_t, machine_uint_t);
204
205// convert a Micro Python object to a sensible value for inline asm
206machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
207 // TODO for byte_array, pass pointer to the array
208 if (MP_OBJ_IS_SMALL_INT(obj)) {
209 return MP_OBJ_SMALL_INT_VALUE(obj);
210 } else if (obj == mp_const_none) {
211 return 0;
212 } else if (obj == mp_const_false) {
213 return 0;
214 } else if (obj == mp_const_true) {
215 return 1;
Damien George5fa93b62014-01-22 14:35:10 +0000216 } else if (MP_OBJ_IS_STR(obj)) {
Damiend99b0522013-12-21 18:17:45 +0000217 // pointer to the string (it's probably constant though!)
Damien George5fa93b62014-01-22 14:35:10 +0000218 uint l;
219 return (machine_uint_t)mp_obj_str_get_data(obj, &l);
Damiend99b0522013-12-21 18:17:45 +0000220#if MICROPY_ENABLE_FLOAT
221 } else if (MP_OBJ_IS_TYPE(obj, &float_type)) {
222 // convert float to int (could also pass in float registers)
223 return (machine_int_t)mp_obj_float_get(obj);
224#endif
225 } else if (MP_OBJ_IS_TYPE(obj, &tuple_type)) {
226 // pointer to start of tuple (could pass length, but then could use len(x) for that)
227 uint len;
228 mp_obj_t *items;
229 mp_obj_tuple_get(obj, &len, &items);
230 return (machine_uint_t)items;
231 } else if (MP_OBJ_IS_TYPE(obj, &list_type)) {
232 // pointer to start of list (could pass length, but then could use len(x) for that)
233 uint len;
234 mp_obj_t *items;
235 mp_obj_list_get(obj, &len, &items);
236 return (machine_uint_t)items;
237 } else {
238 // just pass along a pointer to the object
239 return (machine_uint_t)obj;
240 }
241}
242
243// convert a return value from inline asm to a sensible Micro Python object
244mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
245 return MP_OBJ_NEW_SMALL_INT(val);
246}
247
Damien George20006db2014-01-18 14:10:48 +0000248mp_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 +0000249 mp_obj_fun_asm_t *self = self_in;
250
251 if (n_args != self->n_args) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +0000252 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 +0000253 }
Damien George20006db2014-01-18 14:10:48 +0000254 if (n_kw != 0) {
255 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "function does not take keyword arguments"));
256 }
Damiend99b0522013-12-21 18:17:45 +0000257
258 machine_uint_t ret;
259 if (n_args == 0) {
260 ret = ((inline_asm_fun_0_t)self->fun)();
261 } else if (n_args == 1) {
262 ret = ((inline_asm_fun_1_t)self->fun)(convert_obj_for_inline_asm(args[0]));
263 } else if (n_args == 2) {
Damien George20006db2014-01-18 14:10:48 +0000264 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 +0000265 } else if (n_args == 3) {
Damien George20006db2014-01-18 14:10:48 +0000266 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 +0000267 } else {
268 assert(0);
269 ret = 0;
270 }
271
272 return convert_val_from_inline_asm(ret);
273}
274
275static const mp_obj_type_t fun_asm_type = {
276 { &mp_const_type },
277 "function",
Damien George20006db2014-01-18 14:10:48 +0000278 .call = fun_asm_call,
Damiend99b0522013-12-21 18:17:45 +0000279};
280
281mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) {
282 mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
283 o->base.type = &fun_asm_type;
284 o->n_args = n_args;
285 o->fun = fun;
286 return o;
287}