blob: 3e9125d5cde1eaaf0ede948fe4002a42cd28e735 [file] [log] [blame]
Damien Georgeb534e1b2014-09-04 14:44:01 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2014 Damien P. George
7 * Copyright (c) 2014 Paul Sokolovsky
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 */
27
28#include <stdbool.h>
29#include <string.h>
30#include <assert.h>
31
Damien George51dfcb42015-01-01 20:27:54 +000032#include "py/nlr.h"
33#include "py/objfun.h"
34#include "py/bc.h"
Damien Georgeb534e1b2014-09-04 14:44:01 +010035
36#if 0 // print debugging info
37#define DEBUG_PRINT (1)
38#else // don't print debugging info
Damien George7860c2a2014-11-05 21:16:41 +000039#define DEBUG_PRINT (0)
Damien Georgeb534e1b2014-09-04 14:44:01 +010040#define DEBUG_printf(...) (void)0
41#endif
42
43mp_uint_t mp_decode_uint(const byte **ptr) {
44 mp_uint_t unum = 0;
45 byte val;
46 const byte *p = *ptr;
47 do {
48 val = *p++;
49 unum = (unum << 7) | (val & 0x7f);
50 } while ((val & 0x80) != 0);
51 *ptr = p;
52 return unum;
53}
54
55STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, mp_uint_t expected, mp_uint_t given) {
56#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
Damien George7f233842015-01-01 15:33:50 +000057 // generic message, used also for other argument issues
Damien George3a2171e2015-09-04 16:53:46 +010058 (void)f;
59 (void)expected;
60 (void)given;
Damien George7f233842015-01-01 15:33:50 +000061 mp_arg_error_terse_mismatch();
Damien Georgeb534e1b2014-09-04 14:44:01 +010062#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL
Damien George3a2171e2015-09-04 16:53:46 +010063 (void)f;
Damien Georgeb534e1b2014-09-04 14:44:01 +010064 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
65 "function takes %d positional arguments but %d were given", expected, given));
66#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
67 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
Damien George044c4732015-04-11 13:03:37 +010068 "%q() takes %d positional arguments but %d were given",
69 mp_obj_fun_get_name(f), expected, given));
Damien Georgeb534e1b2014-09-04 14:44:01 +010070#endif
71}
72
73#if DEBUG_PRINT
Damien George42f3de92014-10-03 17:44:14 +000074STATIC void dump_args(const mp_obj_t *a, mp_uint_t sz) {
Damien Georgeb534e1b2014-09-04 14:44:01 +010075 DEBUG_printf("%p: ", a);
Damien George42f3de92014-10-03 17:44:14 +000076 for (mp_uint_t i = 0; i < sz; i++) {
Damien Georgeb534e1b2014-09-04 14:44:01 +010077 DEBUG_printf("%p ", a[i]);
78 }
79 DEBUG_printf("\n");
80}
81#else
82#define dump_args(...) (void)0
83#endif
84
Damien George99886182015-04-06 22:38:53 +010085// On entry code_state should be allocated somewhere (stack/heap) and
86// contain the following valid entries:
87// - code_state->code_info should be the offset in bytes from the start of
88// the bytecode chunk to the start of the code-info within the bytecode
89// - code_state->ip should contain the offset in bytes from the start of
90// the bytecode chunk to the start of the prelude within the bytecode
91// - code_state->n_state should be set to the state size (locals plus stack)
Damien Georgeb534e1b2014-09-04 14:44:01 +010092void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
93 // This function is pretty complicated. It's main aim is to be efficient in speed and RAM
94 // usage for the common case of positional only args.
95 mp_obj_fun_bc_t *self = self_in;
96 mp_uint_t n_state = code_state->n_state;
Damien Georgeb534e1b2014-09-04 14:44:01 +010097
Paul Sokolovsky20397572015-03-28 01:14:44 +020098 #if MICROPY_STACKLESS
99 code_state->prev = NULL;
100 #endif
Damien George99886182015-04-06 22:38:53 +0100101 code_state->code_info = self->bytecode + (mp_uint_t)code_state->code_info;
Damien Georgeb534e1b2014-09-04 14:44:01 +0100102 code_state->sp = &code_state->state[0] - 1;
103 code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;
104
105 // zero out the local stack to begin with
106 memset(code_state->state, 0, n_state * sizeof(*code_state->state));
107
108 const mp_obj_t *kwargs = args + n_args;
109
110 // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed)
111 mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - self->n_pos_args - self->n_kwonly_args];
112
113 // check positional arguments
114
115 if (n_args > self->n_pos_args) {
116 // given more than enough arguments
117 if (!self->takes_var_args) {
118 fun_pos_args_mismatch(self, self->n_pos_args, n_args);
119 }
120 // put extra arguments in varargs tuple
121 *var_pos_kw_args-- = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args);
122 n_args = self->n_pos_args;
123 } else {
124 if (self->takes_var_args) {
125 DEBUG_printf("passing empty tuple as *args\n");
126 *var_pos_kw_args-- = mp_const_empty_tuple;
127 }
128 // Apply processing and check below only if we don't have kwargs,
129 // otherwise, kw handling code below has own extensive checks.
130 if (n_kw == 0 && !self->has_def_kw_args) {
Damien George963a5a32015-01-16 17:47:07 +0000131 if (n_args >= (mp_uint_t)(self->n_pos_args - self->n_def_args)) {
Damien Georgeb534e1b2014-09-04 14:44:01 +0100132 // given enough arguments, but may need to use some default arguments
133 for (mp_uint_t i = n_args; i < self->n_pos_args; i++) {
134 code_state->state[n_state - 1 - i] = self->extra_args[i - (self->n_pos_args - self->n_def_args)];
135 }
136 } else {
137 fun_pos_args_mismatch(self, self->n_pos_args - self->n_def_args, n_args);
138 }
139 }
140 }
141
142 // copy positional args into state
143 for (mp_uint_t i = 0; i < n_args; i++) {
144 code_state->state[n_state - 1 - i] = args[i];
145 }
146
147 // check keyword arguments
148
149 if (n_kw != 0 || self->has_def_kw_args) {
150 DEBUG_printf("Initial args: ");
151 dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
152
153 mp_obj_t dict = MP_OBJ_NULL;
154 if (self->takes_kw_args) {
155 dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
156 *var_pos_kw_args = dict;
157 }
158
Damien George1084b0f2014-10-25 15:07:02 +0100159 // get pointer to arg_names array at start of bytecode prelude
160 const mp_obj_t *arg_names;
161 {
162 const byte *code_info = code_state->code_info;
163 mp_uint_t code_info_size = mp_decode_uint(&code_info);
164 arg_names = (const mp_obj_t*)(code_state->code_info + code_info_size);
165 }
166
Damien Georgeb534e1b2014-09-04 14:44:01 +0100167 for (mp_uint_t i = 0; i < n_kw; i++) {
Damien George1084b0f2014-10-25 15:07:02 +0100168 mp_obj_t wanted_arg_name = kwargs[2 * i];
Damien Georgeb534e1b2014-09-04 14:44:01 +0100169 for (mp_uint_t j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) {
Damien George1084b0f2014-10-25 15:07:02 +0100170 if (wanted_arg_name == arg_names[j]) {
Damien Georgeb534e1b2014-09-04 14:44:01 +0100171 if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
172 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
Damien George044c4732015-04-11 13:03:37 +0100173 "function got multiple values for argument '%q'", MP_OBJ_QSTR_VALUE(wanted_arg_name)));
Damien Georgeb534e1b2014-09-04 14:44:01 +0100174 }
175 code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
176 goto continue2;
177 }
178 }
179 // Didn't find name match with positional args
180 if (!self->takes_kw_args) {
181 nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
182 }
183 mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
184continue2:;
185 }
186
187 DEBUG_printf("Args with kws flattened: ");
188 dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
189
190 // fill in defaults for positional args
191 mp_obj_t *d = &code_state->state[n_state - self->n_pos_args];
192 mp_obj_t *s = &self->extra_args[self->n_def_args - 1];
Damien George42f3de92014-10-03 17:44:14 +0000193 for (mp_uint_t i = self->n_def_args; i > 0; i--, d++, s--) {
Damien Georgeb534e1b2014-09-04 14:44:01 +0100194 if (*d == MP_OBJ_NULL) {
195 *d = *s;
196 }
197 }
198
199 DEBUG_printf("Args after filling default positional: ");
200 dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
201
202 // Check that all mandatory positional args are specified
203 while (d < &code_state->state[n_state]) {
204 if (*d++ == MP_OBJ_NULL) {
205 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
206 "function missing required positional argument #%d", &code_state->state[n_state] - d));
207 }
208 }
209
210 // Check that all mandatory keyword args are specified
211 // Fill in default kw args if we have them
212 for (mp_uint_t i = 0; i < self->n_kwonly_args; i++) {
213 if (code_state->state[n_state - 1 - self->n_pos_args - i] == MP_OBJ_NULL) {
214 mp_map_elem_t *elem = NULL;
215 if (self->has_def_kw_args) {
Damien George1084b0f2014-10-25 15:07:02 +0100216 elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, arg_names[self->n_pos_args + i], MP_MAP_LOOKUP);
Damien Georgeb534e1b2014-09-04 14:44:01 +0100217 }
218 if (elem != NULL) {
219 code_state->state[n_state - 1 - self->n_pos_args - i] = elem->value;
220 } else {
221 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
Damien George044c4732015-04-11 13:03:37 +0100222 "function missing required keyword argument '%q'", MP_OBJ_QSTR_VALUE(arg_names[self->n_pos_args + i])));
Damien Georgeb534e1b2014-09-04 14:44:01 +0100223 }
224 }
225 }
226
227 } else {
228 // no keyword arguments given
229 if (self->n_kwonly_args != 0) {
230 nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
231 "function missing keyword-only argument"));
232 }
233 if (self->takes_kw_args) {
234 *var_pos_kw_args = mp_obj_new_dict(0);
235 }
236 }
237
238 // bytecode prelude: initialise closed over variables
Damien George99886182015-04-06 22:38:53 +0100239 const byte *ip = self->bytecode + (mp_uint_t)code_state->ip;
Damien Georgec9aa1882015-04-07 00:08:17 +0100240 mp_uint_t local_num;
241 while ((local_num = *ip++) != 255) {
242 code_state->state[n_state - 1 - local_num] =
243 mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
Damien Georgeb534e1b2014-09-04 14:44:01 +0100244 }
245
246 // now that we skipped over the prelude, set the ip for the VM
247 code_state->ip = ip;
248
249 DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", self->n_pos_args, self->n_kwonly_args);
250 dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
251 dump_args(code_state->state, n_state);
252}