1 | /* |
2 | * This file is part of the MicroPython 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 | |
32 | #include "py/runtime.h" |
33 | #include "py/bc0.h" |
34 | #include "py/bc.h" |
35 | |
36 | #if MICROPY_DEBUG_VERBOSE // print debugging info |
37 | #define DEBUG_PRINT (1) |
38 | #else // don't print debugging info |
39 | #define DEBUG_PRINT (0) |
40 | #define DEBUG_printf(...) (void)0 |
41 | #endif |
42 | |
43 | #if !MICROPY_PERSISTENT_CODE |
44 | |
45 | mp_uint_t mp_decode_uint(const byte **ptr) { |
46 | mp_uint_t unum = 0; |
47 | byte val; |
48 | const byte *p = *ptr; |
49 | do { |
50 | val = *p++; |
51 | unum = (unum << 7) | (val & 0x7f); |
52 | } while ((val & 0x80) != 0); |
53 | *ptr = p; |
54 | return unum; |
55 | } |
56 | |
57 | // This function is used to help reduce stack usage at the caller, for the case when |
58 | // the caller doesn't need to increase the ptr argument. If ptr is a local variable |
59 | // and the caller uses mp_decode_uint(&ptr) instead of this function, then the compiler |
60 | // must allocate a slot on the stack for ptr, and this slot cannot be reused for |
61 | // anything else in the function because the pointer may have been stored in a global |
62 | // and reused later in the function. |
63 | mp_uint_t mp_decode_uint_value(const byte *ptr) { |
64 | return mp_decode_uint(&ptr); |
65 | } |
66 | |
67 | // This function is used to help reduce stack usage at the caller, for the case when |
68 | // the caller doesn't need the actual value and just wants to skip over it. |
69 | const byte *mp_decode_uint_skip(const byte *ptr) { |
70 | while ((*ptr++) & 0x80) { |
71 | } |
72 | return ptr; |
73 | } |
74 | |
75 | #endif |
76 | |
77 | STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { |
78 | #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE |
79 | // generic message, used also for other argument issues |
80 | (void)f; |
81 | (void)expected; |
82 | (void)given; |
83 | mp_arg_error_terse_mismatch(); |
84 | #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL |
85 | (void)f; |
86 | mp_raise_msg_varg(&mp_type_TypeError, |
87 | MP_ERROR_TEXT("function takes %d positional arguments but %d were given" ), expected, given); |
88 | #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED |
89 | mp_raise_msg_varg(&mp_type_TypeError, |
90 | MP_ERROR_TEXT("%q() takes %d positional arguments but %d were given" ), |
91 | mp_obj_fun_get_name(MP_OBJ_FROM_PTR(f)), expected, given); |
92 | #endif |
93 | } |
94 | |
95 | #if DEBUG_PRINT |
96 | STATIC void dump_args(const mp_obj_t *a, size_t sz) { |
97 | DEBUG_printf("%p: " , a); |
98 | for (size_t i = 0; i < sz; i++) { |
99 | DEBUG_printf("%p " , a[i]); |
100 | } |
101 | DEBUG_printf("\n" ); |
102 | } |
103 | #else |
104 | #define dump_args(...) (void)0 |
105 | #endif |
106 | |
107 | // On entry code_state should be allocated somewhere (stack/heap) and |
108 | // contain the following valid entries: |
109 | // - code_state->fun_bc should contain a pointer to the function object |
110 | // - code_state->ip should contain the offset in bytes from the pointer |
111 | // code_state->fun_bc->bytecode to the entry n_state (0 for bytecode, non-zero for native) |
112 | void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) { |
113 | // This function is pretty complicated. It's main aim is to be efficient in speed and RAM |
114 | // usage for the common case of positional only args. |
115 | |
116 | // get the function object that we want to set up (could be bytecode or native code) |
117 | mp_obj_fun_bc_t *self = code_state->fun_bc; |
118 | |
119 | // ip comes in as an offset into bytecode, so turn it into a true pointer |
120 | code_state->ip = self->bytecode + (size_t)code_state->ip; |
121 | |
122 | #if MICROPY_STACKLESS |
123 | code_state->prev = NULL; |
124 | #endif |
125 | |
126 | #if MICROPY_PY_SYS_SETTRACE |
127 | code_state->prev_state = NULL; |
128 | code_state->frame = NULL; |
129 | #endif |
130 | |
131 | // Get cached n_state (rather than decode it again) |
132 | size_t n_state = code_state->n_state; |
133 | |
134 | // Decode prelude |
135 | size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; |
136 | MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args); |
137 | (void)n_state_unused; |
138 | (void)n_exc_stack_unused; |
139 | |
140 | code_state->sp = &code_state->state[0] - 1; |
141 | code_state->exc_sp_idx = 0; |
142 | |
143 | // zero out the local stack to begin with |
144 | memset(code_state->state, 0, n_state * sizeof(*code_state->state)); |
145 | |
146 | const mp_obj_t *kwargs = args + n_args; |
147 | |
148 | // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed) |
149 | mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - n_pos_args - n_kwonly_args]; |
150 | |
151 | // check positional arguments |
152 | |
153 | if (n_args > n_pos_args) { |
154 | // given more than enough arguments |
155 | if ((scope_flags & MP_SCOPE_FLAG_VARARGS) == 0) { |
156 | fun_pos_args_mismatch(self, n_pos_args, n_args); |
157 | } |
158 | // put extra arguments in varargs tuple |
159 | *var_pos_kw_args-- = mp_obj_new_tuple(n_args - n_pos_args, args + n_pos_args); |
160 | n_args = n_pos_args; |
161 | } else { |
162 | if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { |
163 | DEBUG_printf("passing empty tuple as *args\n" ); |
164 | *var_pos_kw_args-- = mp_const_empty_tuple; |
165 | } |
166 | // Apply processing and check below only if we don't have kwargs, |
167 | // otherwise, kw handling code below has own extensive checks. |
168 | if (n_kw == 0 && (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) == 0) { |
169 | if (n_args >= (size_t)(n_pos_args - n_def_pos_args)) { |
170 | // given enough arguments, but may need to use some default arguments |
171 | for (size_t i = n_args; i < n_pos_args; i++) { |
172 | code_state->state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)]; |
173 | } |
174 | } else { |
175 | fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args); |
176 | } |
177 | } |
178 | } |
179 | |
180 | // copy positional args into state |
181 | for (size_t i = 0; i < n_args; i++) { |
182 | code_state->state[n_state - 1 - i] = args[i]; |
183 | } |
184 | |
185 | // check keyword arguments |
186 | |
187 | if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { |
188 | DEBUG_printf("Initial args: " ); |
189 | dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); |
190 | |
191 | mp_obj_t dict = MP_OBJ_NULL; |
192 | if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { |
193 | dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0? |
194 | *var_pos_kw_args = dict; |
195 | } |
196 | |
197 | // get pointer to arg_names array |
198 | const mp_obj_t *arg_names = (const mp_obj_t *)self->const_table; |
199 | |
200 | for (size_t i = 0; i < n_kw; i++) { |
201 | // the keys in kwargs are expected to be qstr objects |
202 | mp_obj_t wanted_arg_name = kwargs[2 * i]; |
203 | for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) { |
204 | if (wanted_arg_name == arg_names[j]) { |
205 | if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) { |
206 | mp_raise_msg_varg(&mp_type_TypeError, |
207 | MP_ERROR_TEXT("function got multiple values for argument '%q'" ), MP_OBJ_QSTR_VALUE(wanted_arg_name)); |
208 | } |
209 | code_state->state[n_state - 1 - j] = kwargs[2 * i + 1]; |
210 | goto continue2; |
211 | } |
212 | } |
213 | // Didn't find name match with positional args |
214 | if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) == 0) { |
215 | #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE |
216 | mp_raise_TypeError(MP_ERROR_TEXT("unexpected keyword argument" )); |
217 | #else |
218 | mp_raise_msg_varg(&mp_type_TypeError, |
219 | MP_ERROR_TEXT("unexpected keyword argument '%q'" ), MP_OBJ_QSTR_VALUE(wanted_arg_name)); |
220 | #endif |
221 | } |
222 | mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]); |
223 | continue2:; |
224 | } |
225 | |
226 | DEBUG_printf("Args with kws flattened: " ); |
227 | dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); |
228 | |
229 | // fill in defaults for positional args |
230 | mp_obj_t *d = &code_state->state[n_state - n_pos_args]; |
231 | mp_obj_t *s = &self->extra_args[n_def_pos_args - 1]; |
232 | for (size_t i = n_def_pos_args; i > 0; i--, d++, s--) { |
233 | if (*d == MP_OBJ_NULL) { |
234 | *d = *s; |
235 | } |
236 | } |
237 | |
238 | DEBUG_printf("Args after filling default positional: " ); |
239 | dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); |
240 | |
241 | // Check that all mandatory positional args are specified |
242 | while (d < &code_state->state[n_state]) { |
243 | if (*d++ == MP_OBJ_NULL) { |
244 | mp_raise_msg_varg(&mp_type_TypeError, |
245 | MP_ERROR_TEXT("function missing required positional argument #%d" ), &code_state->state[n_state] - d); |
246 | } |
247 | } |
248 | |
249 | // Check that all mandatory keyword args are specified |
250 | // Fill in default kw args if we have them |
251 | for (size_t i = 0; i < n_kwonly_args; i++) { |
252 | if (code_state->state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) { |
253 | mp_map_elem_t *elem = NULL; |
254 | if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { |
255 | elem = mp_map_lookup(&((mp_obj_dict_t *)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, arg_names[n_pos_args + i], MP_MAP_LOOKUP); |
256 | } |
257 | if (elem != NULL) { |
258 | code_state->state[n_state - 1 - n_pos_args - i] = elem->value; |
259 | } else { |
260 | mp_raise_msg_varg(&mp_type_TypeError, |
261 | MP_ERROR_TEXT("function missing required keyword argument '%q'" ), MP_OBJ_QSTR_VALUE(arg_names[n_pos_args + i])); |
262 | } |
263 | } |
264 | } |
265 | |
266 | } else { |
267 | // no keyword arguments given |
268 | if (n_kwonly_args != 0) { |
269 | mp_raise_TypeError(MP_ERROR_TEXT("function missing keyword-only argument" )); |
270 | } |
271 | if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { |
272 | *var_pos_kw_args = mp_obj_new_dict(0); |
273 | } |
274 | } |
275 | |
276 | // read the size part of the prelude |
277 | const byte *ip = code_state->ip; |
278 | MP_BC_PRELUDE_SIZE_DECODE(ip); |
279 | |
280 | // jump over code info (source file and line-number mapping) |
281 | ip += n_info; |
282 | |
283 | // bytecode prelude: initialise closed over variables |
284 | for (; n_cell; --n_cell) { |
285 | size_t local_num = *ip++; |
286 | code_state->state[n_state - 1 - local_num] = |
287 | mp_obj_new_cell(code_state->state[n_state - 1 - local_num]); |
288 | } |
289 | |
290 | #if !MICROPY_PERSISTENT_CODE |
291 | // so bytecode is aligned |
292 | ip = MP_ALIGN(ip, sizeof(mp_uint_t)); |
293 | #endif |
294 | |
295 | // now that we skipped over the prelude, set the ip for the VM |
296 | code_state->ip = ip; |
297 | |
298 | DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n" , n_pos_args, n_kwonly_args); |
299 | dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); |
300 | dump_args(code_state->state, n_state); |
301 | } |
302 | |
303 | #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE |
304 | |
305 | // The following table encodes the number of bytes that a specific opcode |
306 | // takes up. Some opcodes have an extra byte, defined by MP_BC_MASK_EXTRA_BYTE. |
307 | // There are 4 special opcodes that have an extra byte only when |
308 | // MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE is enabled (and they take a qstr): |
309 | // MP_BC_LOAD_NAME |
310 | // MP_BC_LOAD_GLOBAL |
311 | // MP_BC_LOAD_ATTR |
312 | // MP_BC_STORE_ATTR |
313 | uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint) { |
314 | uint f = MP_BC_FORMAT(*ip); |
315 | const byte *ip_start = ip; |
316 | if (f == MP_BC_FORMAT_QSTR) { |
317 | if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { |
318 | if (*ip == MP_BC_LOAD_NAME |
319 | || *ip == MP_BC_LOAD_GLOBAL |
320 | || *ip == MP_BC_LOAD_ATTR |
321 | || *ip == MP_BC_STORE_ATTR) { |
322 | ip += 1; |
323 | } |
324 | } |
325 | ip += 3; |
326 | } else { |
327 | int = (*ip & MP_BC_MASK_EXTRA_BYTE) == 0; |
328 | ip += 1; |
329 | if (f == MP_BC_FORMAT_VAR_UINT) { |
330 | if (count_var_uint) { |
331 | while ((*ip++ & 0x80) != 0) { |
332 | } |
333 | } |
334 | } else if (f == MP_BC_FORMAT_OFFSET) { |
335 | ip += 2; |
336 | } |
337 | ip += extra_byte; |
338 | } |
339 | *opcode_size = ip - ip_start; |
340 | return f; |
341 | } |
342 | |
343 | #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE |
344 | |