blob: 613cce795672b84c9faf49e4b2fb40e9bed24c87 [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
58 mp_arg_error_terse_mismatch();
Damien Georgeb534e1b2014-09-04 14:44:01 +010059#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL
60 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
61 "function takes %d positional arguments but %d were given", expected, given));
62#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
63 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
64 "%s() takes %d positional arguments but %d were given",
stijn3cc17c62015-02-14 18:44:31 +010065 qstr_str(mp_obj_fun_get_name(f)), expected, given));
Damien Georgeb534e1b2014-09-04 14:44:01 +010066#endif
67}
68
69#if DEBUG_PRINT
Damien George42f3de92014-10-03 17:44:14 +000070STATIC void dump_args(const mp_obj_t *a, mp_uint_t sz) {
Damien Georgeb534e1b2014-09-04 14:44:01 +010071 DEBUG_printf("%p: ", a);
Damien George42f3de92014-10-03 17:44:14 +000072 for (mp_uint_t i = 0; i < sz; i++) {
Damien Georgeb534e1b2014-09-04 14:44:01 +010073 DEBUG_printf("%p ", a[i]);
74 }
75 DEBUG_printf("\n");
76}
77#else
78#define dump_args(...) (void)0
79#endif
80
Damien George99886182015-04-06 22:38:53 +010081// On entry code_state should be allocated somewhere (stack/heap) and
82// contain the following valid entries:
83// - code_state->code_info should be the offset in bytes from the start of
84// the bytecode chunk to the start of the code-info within the bytecode
85// - code_state->ip should contain the offset in bytes from the start of
86// the bytecode chunk to the start of the prelude within the bytecode
87// - code_state->n_state should be set to the state size (locals plus stack)
Damien Georgeb534e1b2014-09-04 14:44:01 +010088void 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) {
89 // This function is pretty complicated. It's main aim is to be efficient in speed and RAM
90 // usage for the common case of positional only args.
91 mp_obj_fun_bc_t *self = self_in;
92 mp_uint_t n_state = code_state->n_state;
Damien Georgeb534e1b2014-09-04 14:44:01 +010093
Paul Sokolovsky20397572015-03-28 01:14:44 +020094 #if MICROPY_STACKLESS
95 code_state->prev = NULL;
96 #endif
Damien George99886182015-04-06 22:38:53 +010097 code_state->code_info = self->bytecode + (mp_uint_t)code_state->code_info;
Damien Georgeb534e1b2014-09-04 14:44:01 +010098 code_state->sp = &code_state->state[0] - 1;
99 code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;
100
101 // zero out the local stack to begin with
102 memset(code_state->state, 0, n_state * sizeof(*code_state->state));
103
104 const mp_obj_t *kwargs = args + n_args;
105
106 // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed)
107 mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - self->n_pos_args - self->n_kwonly_args];
108
109 // check positional arguments
110
111 if (n_args > self->n_pos_args) {
112 // given more than enough arguments
113 if (!self->takes_var_args) {
114 fun_pos_args_mismatch(self, self->n_pos_args, n_args);
115 }
116 // put extra arguments in varargs tuple
117 *var_pos_kw_args-- = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args);
118 n_args = self->n_pos_args;
119 } else {
120 if (self->takes_var_args) {
121 DEBUG_printf("passing empty tuple as *args\n");
122 *var_pos_kw_args-- = mp_const_empty_tuple;
123 }
124 // Apply processing and check below only if we don't have kwargs,
125 // otherwise, kw handling code below has own extensive checks.
126 if (n_kw == 0 && !self->has_def_kw_args) {
Damien George963a5a32015-01-16 17:47:07 +0000127 if (n_args >= (mp_uint_t)(self->n_pos_args - self->n_def_args)) {
Damien Georgeb534e1b2014-09-04 14:44:01 +0100128 // given enough arguments, but may need to use some default arguments
129 for (mp_uint_t i = n_args; i < self->n_pos_args; i++) {
130 code_state->state[n_state - 1 - i] = self->extra_args[i - (self->n_pos_args - self->n_def_args)];
131 }
132 } else {
133 fun_pos_args_mismatch(self, self->n_pos_args - self->n_def_args, n_args);
134 }
135 }
136 }
137
138 // copy positional args into state
139 for (mp_uint_t i = 0; i < n_args; i++) {
140 code_state->state[n_state - 1 - i] = args[i];
141 }
142
143 // check keyword arguments
144
145 if (n_kw != 0 || self->has_def_kw_args) {
146 DEBUG_printf("Initial args: ");
147 dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
148
149 mp_obj_t dict = MP_OBJ_NULL;
150 if (self->takes_kw_args) {
151 dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
152 *var_pos_kw_args = dict;
153 }
154
Damien George1084b0f2014-10-25 15:07:02 +0100155 // get pointer to arg_names array at start of bytecode prelude
156 const mp_obj_t *arg_names;
157 {
158 const byte *code_info = code_state->code_info;
159 mp_uint_t code_info_size = mp_decode_uint(&code_info);
160 arg_names = (const mp_obj_t*)(code_state->code_info + code_info_size);
161 }
162
Damien Georgeb534e1b2014-09-04 14:44:01 +0100163 for (mp_uint_t i = 0; i < n_kw; i++) {
Damien George1084b0f2014-10-25 15:07:02 +0100164 mp_obj_t wanted_arg_name = kwargs[2 * i];
Damien Georgeb534e1b2014-09-04 14:44:01 +0100165 for (mp_uint_t j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) {
Damien George1084b0f2014-10-25 15:07:02 +0100166 if (wanted_arg_name == arg_names[j]) {
Damien Georgeb534e1b2014-09-04 14:44:01 +0100167 if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
168 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
Damien George1084b0f2014-10-25 15:07:02 +0100169 "function got multiple values for argument '%s'", qstr_str(MP_OBJ_QSTR_VALUE(wanted_arg_name))));
Damien Georgeb534e1b2014-09-04 14:44:01 +0100170 }
171 code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
172 goto continue2;
173 }
174 }
175 // Didn't find name match with positional args
176 if (!self->takes_kw_args) {
177 nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
178 }
179 mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
180continue2:;
181 }
182
183 DEBUG_printf("Args with kws flattened: ");
184 dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
185
186 // fill in defaults for positional args
187 mp_obj_t *d = &code_state->state[n_state - self->n_pos_args];
188 mp_obj_t *s = &self->extra_args[self->n_def_args - 1];
Damien George42f3de92014-10-03 17:44:14 +0000189 for (mp_uint_t i = self->n_def_args; i > 0; i--, d++, s--) {
Damien Georgeb534e1b2014-09-04 14:44:01 +0100190 if (*d == MP_OBJ_NULL) {
191 *d = *s;
192 }
193 }
194
195 DEBUG_printf("Args after filling default positional: ");
196 dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
197
198 // Check that all mandatory positional args are specified
199 while (d < &code_state->state[n_state]) {
200 if (*d++ == MP_OBJ_NULL) {
201 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
202 "function missing required positional argument #%d", &code_state->state[n_state] - d));
203 }
204 }
205
206 // Check that all mandatory keyword args are specified
207 // Fill in default kw args if we have them
208 for (mp_uint_t i = 0; i < self->n_kwonly_args; i++) {
209 if (code_state->state[n_state - 1 - self->n_pos_args - i] == MP_OBJ_NULL) {
210 mp_map_elem_t *elem = NULL;
211 if (self->has_def_kw_args) {
Damien George1084b0f2014-10-25 15:07:02 +0100212 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 +0100213 }
214 if (elem != NULL) {
215 code_state->state[n_state - 1 - self->n_pos_args - i] = elem->value;
216 } else {
217 nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
Damien George1084b0f2014-10-25 15:07:02 +0100218 "function missing required keyword argument '%s'", qstr_str(MP_OBJ_QSTR_VALUE(arg_names[self->n_pos_args + i]))));
Damien Georgeb534e1b2014-09-04 14:44:01 +0100219 }
220 }
221 }
222
223 } else {
224 // no keyword arguments given
225 if (self->n_kwonly_args != 0) {
226 nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
227 "function missing keyword-only argument"));
228 }
229 if (self->takes_kw_args) {
230 *var_pos_kw_args = mp_obj_new_dict(0);
231 }
232 }
233
234 // bytecode prelude: initialise closed over variables
Damien George99886182015-04-06 22:38:53 +0100235 const byte *ip = self->bytecode + (mp_uint_t)code_state->ip;
Damien Georgec9aa1882015-04-07 00:08:17 +0100236 mp_uint_t local_num;
237 while ((local_num = *ip++) != 255) {
238 code_state->state[n_state - 1 - local_num] =
239 mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
Damien Georgeb534e1b2014-09-04 14:44:01 +0100240 }
241
242 // now that we skipped over the prelude, set the ip for the VM
243 code_state->ip = ip;
244
245 DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", self->n_pos_args, self->n_kwonly_args);
246 dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
247 dump_args(code_state->state, n_state);
248}