1 | /* |
2 | * This file is part of the MicroPython project, http://micropython.org/ |
3 | * |
4 | * The MIT License (MIT) |
5 | * |
6 | * Copyright (c) 2013-2019 Damien P. George |
7 | * Copyright (c) 2014-2017 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 <stdlib.h> |
29 | #include <assert.h> |
30 | |
31 | #include "py/runtime.h" |
32 | #include "py/bc.h" |
33 | #include "py/objstr.h" |
34 | #include "py/objgenerator.h" |
35 | #include "py/objfun.h" |
36 | #include "py/stackctrl.h" |
37 | |
38 | // Instance of GeneratorExit exception - needed by generator.close() |
39 | const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t *)&mp_const_empty_tuple_obj}; |
40 | |
41 | /******************************************************************************/ |
42 | /* generator wrapper */ |
43 | |
44 | typedef struct _mp_obj_gen_instance_t { |
45 | mp_obj_base_t base; |
46 | // mp_const_none: Not-running, no exception. |
47 | // MP_OBJ_NULL: Running, no exception. |
48 | // other: Not running, pending exception. |
49 | mp_obj_t pend_exc; |
50 | mp_code_state_t code_state; |
51 | } mp_obj_gen_instance_t; |
52 | |
53 | STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { |
54 | // A generating function is just a bytecode function with type mp_type_gen_wrap |
55 | mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in); |
56 | |
57 | // bytecode prelude: get state size and exception stack size |
58 | const uint8_t *ip = self_fun->bytecode; |
59 | MP_BC_PRELUDE_SIG_DECODE(ip); |
60 | |
61 | // allocate the generator object, with room for local stack and exception stack |
62 | mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte, |
63 | n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t)); |
64 | o->base.type = &mp_type_gen_instance; |
65 | |
66 | o->pend_exc = mp_const_none; |
67 | o->code_state.fun_bc = self_fun; |
68 | o->code_state.ip = 0; |
69 | o->code_state.n_state = n_state; |
70 | mp_setup_code_state(&o->code_state, n_args, n_kw, args); |
71 | return MP_OBJ_FROM_PTR(o); |
72 | } |
73 | |
74 | const mp_obj_type_t mp_type_gen_wrap = { |
75 | { &mp_type_type }, |
76 | .flags = MP_TYPE_FLAG_BINDS_SELF, |
77 | .name = MP_QSTR_generator, |
78 | .call = gen_wrap_call, |
79 | .unary_op = mp_generic_unary_op, |
80 | #if MICROPY_PY_FUNCTION_ATTRS |
81 | .attr = mp_obj_fun_bc_attr, |
82 | #endif |
83 | }; |
84 | |
85 | /******************************************************************************/ |
86 | // native generator wrapper |
87 | |
88 | #if MICROPY_EMIT_NATIVE |
89 | |
90 | STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { |
91 | // The state for a native generating function is held in the same struct as a bytecode function |
92 | mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in); |
93 | |
94 | // Determine start of prelude, and extract n_state from it |
95 | uintptr_t prelude_offset = ((uintptr_t *)self_fun->bytecode)[0]; |
96 | #if MICROPY_EMIT_NATIVE_PRELUDE_AS_BYTES_OBJ |
97 | // Prelude is in bytes object in const_table, at index prelude_offset |
98 | mp_obj_str_t *prelude_bytes = MP_OBJ_TO_PTR(self_fun->const_table[prelude_offset]); |
99 | prelude_offset = (const byte *)prelude_bytes->data - self_fun->bytecode; |
100 | #endif |
101 | const uint8_t *ip = self_fun->bytecode + prelude_offset; |
102 | size_t n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args; |
103 | MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args); |
104 | size_t n_exc_stack = 0; |
105 | |
106 | // Allocate the generator object, with room for local stack and exception stack |
107 | mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte, |
108 | n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t)); |
109 | o->base.type = &mp_type_gen_instance; |
110 | |
111 | // Parse the input arguments and set up the code state |
112 | o->pend_exc = mp_const_none; |
113 | o->code_state.fun_bc = self_fun; |
114 | o->code_state.ip = (const byte *)prelude_offset; |
115 | o->code_state.n_state = n_state; |
116 | mp_setup_code_state(&o->code_state, n_args, n_kw, args); |
117 | |
118 | // Indicate we are a native function, which doesn't use this variable |
119 | o->code_state.exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_SENTINEL; |
120 | |
121 | // Prepare the generator instance for execution |
122 | uintptr_t start_offset = ((uintptr_t *)self_fun->bytecode)[1]; |
123 | o->code_state.ip = MICROPY_MAKE_POINTER_CALLABLE((void *)(self_fun->bytecode + start_offset)); |
124 | |
125 | return MP_OBJ_FROM_PTR(o); |
126 | } |
127 | |
128 | const mp_obj_type_t mp_type_native_gen_wrap = { |
129 | { &mp_type_type }, |
130 | .flags = MP_TYPE_FLAG_BINDS_SELF, |
131 | .name = MP_QSTR_generator, |
132 | .call = native_gen_wrap_call, |
133 | .unary_op = mp_generic_unary_op, |
134 | #if MICROPY_PY_FUNCTION_ATTRS |
135 | .attr = mp_obj_fun_bc_attr, |
136 | #endif |
137 | }; |
138 | |
139 | #endif // MICROPY_EMIT_NATIVE |
140 | |
141 | /******************************************************************************/ |
142 | /* generator instance */ |
143 | |
144 | STATIC void gen_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { |
145 | (void)kind; |
146 | mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); |
147 | mp_printf(print, "<generator object '%q' at %p>" , mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self); |
148 | } |
149 | |
150 | mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) { |
151 | MP_STACK_CHECK(); |
152 | mp_check_self(mp_obj_is_type(self_in, &mp_type_gen_instance)); |
153 | mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); |
154 | if (self->code_state.ip == 0) { |
155 | // Trying to resume already stopped generator |
156 | *ret_val = MP_OBJ_STOP_ITERATION; |
157 | return MP_VM_RETURN_NORMAL; |
158 | } |
159 | |
160 | // Ensure the generator cannot be reentered during execution |
161 | if (self->pend_exc == MP_OBJ_NULL) { |
162 | mp_raise_ValueError(MP_ERROR_TEXT("generator already executing" )); |
163 | } |
164 | |
165 | #if MICROPY_PY_GENERATOR_PEND_THROW |
166 | // If exception is pending (set using .pend_throw()), process it now. |
167 | if (self->pend_exc != mp_const_none) { |
168 | throw_value = self->pend_exc; |
169 | } |
170 | #endif |
171 | |
172 | // If the generator is started, allow sending a value. |
173 | if (self->code_state.sp == self->code_state.state - 1) { |
174 | if (send_value != mp_const_none) { |
175 | mp_raise_TypeError(MP_ERROR_TEXT("can't send non-None value to a just-started generator" )); |
176 | } |
177 | } else { |
178 | *self->code_state.sp = send_value; |
179 | } |
180 | |
181 | // Mark as running |
182 | self->pend_exc = MP_OBJ_NULL; |
183 | |
184 | // Set up the correct globals context for the generator and execute it |
185 | self->code_state.old_globals = mp_globals_get(); |
186 | mp_globals_set(self->code_state.fun_bc->globals); |
187 | |
188 | mp_vm_return_kind_t ret_kind; |
189 | |
190 | #if MICROPY_EMIT_NATIVE |
191 | if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { |
192 | // A native generator, with entry point 2 words into the "bytecode" pointer |
193 | typedef uintptr_t (*mp_fun_native_gen_t)(void *, mp_obj_t); |
194 | mp_fun_native_gen_t fun = MICROPY_MAKE_POINTER_CALLABLE((const void *)(self->code_state.fun_bc->bytecode + 2 * sizeof(uintptr_t))); |
195 | ret_kind = fun((void *)&self->code_state, throw_value); |
196 | } else |
197 | #endif |
198 | { |
199 | // A bytecode generator |
200 | ret_kind = mp_execute_bytecode(&self->code_state, throw_value); |
201 | } |
202 | |
203 | mp_globals_set(self->code_state.old_globals); |
204 | |
205 | // Mark as not running |
206 | self->pend_exc = mp_const_none; |
207 | |
208 | switch (ret_kind) { |
209 | case MP_VM_RETURN_NORMAL: |
210 | default: |
211 | // Explicitly mark generator as completed. If we don't do this, |
212 | // subsequent next() may re-execute statements after last yield |
213 | // again and again, leading to side effects. |
214 | self->code_state.ip = 0; |
215 | *ret_val = *self->code_state.sp; |
216 | break; |
217 | |
218 | case MP_VM_RETURN_YIELD: |
219 | *ret_val = *self->code_state.sp; |
220 | #if MICROPY_PY_GENERATOR_PEND_THROW |
221 | *self->code_state.sp = mp_const_none; |
222 | #endif |
223 | break; |
224 | |
225 | case MP_VM_RETURN_EXCEPTION: { |
226 | self->code_state.ip = 0; |
227 | *ret_val = self->code_state.state[0]; |
228 | // PEP479: if StopIteration is raised inside a generator it is replaced with RuntimeError |
229 | if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(*ret_val)), MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { |
230 | *ret_val = mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("generator raised StopIteration" )); |
231 | } |
232 | break; |
233 | } |
234 | } |
235 | |
236 | return ret_kind; |
237 | } |
238 | |
239 | STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value) { |
240 | mp_obj_t ret; |
241 | switch (mp_obj_gen_resume(self_in, send_value, throw_value, &ret)) { |
242 | case MP_VM_RETURN_NORMAL: |
243 | default: |
244 | // Optimize return w/o value in case generator is used in for loop |
245 | if (ret == mp_const_none || ret == MP_OBJ_STOP_ITERATION) { |
246 | return MP_OBJ_STOP_ITERATION; |
247 | } else { |
248 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_StopIteration, ret)); |
249 | } |
250 | |
251 | case MP_VM_RETURN_YIELD: |
252 | return ret; |
253 | |
254 | case MP_VM_RETURN_EXCEPTION: |
255 | nlr_raise(ret); |
256 | } |
257 | } |
258 | |
259 | STATIC mp_obj_t gen_instance_iternext(mp_obj_t self_in) { |
260 | return gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL); |
261 | } |
262 | |
263 | STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) { |
264 | mp_obj_t ret = gen_resume_and_raise(self_in, send_value, MP_OBJ_NULL); |
265 | if (ret == MP_OBJ_STOP_ITERATION) { |
266 | mp_raise_type(&mp_type_StopIteration); |
267 | } else { |
268 | return ret; |
269 | } |
270 | } |
271 | STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send); |
272 | |
273 | STATIC mp_obj_t gen_instance_throw(size_t n_args, const mp_obj_t *args) { |
274 | // The signature of this function is: throw(type[, value[, traceback]]) |
275 | // CPython will pass all given arguments through the call chain and process them |
276 | // at the point they are used (native generators will handle them differently to |
277 | // user-defined generators with a throw() method). To save passing multiple |
278 | // values, MicroPython instead does partial processing here to reduce it down to |
279 | // one argument and passes that through: |
280 | // - if only args[1] is given, or args[2] is given but is None, args[1] is |
281 | // passed through (in the standard case it is an exception class or instance) |
282 | // - if args[2] is given and not None it is passed through (in the standard |
283 | // case it would be an exception instance and args[1] its corresponding class) |
284 | // - args[3] is always ignored |
285 | |
286 | mp_obj_t exc = args[1]; |
287 | if (n_args > 2 && args[2] != mp_const_none) { |
288 | exc = args[2]; |
289 | } |
290 | |
291 | mp_obj_t ret = gen_resume_and_raise(args[0], mp_const_none, exc); |
292 | if (ret == MP_OBJ_STOP_ITERATION) { |
293 | mp_raise_type(&mp_type_StopIteration); |
294 | } else { |
295 | return ret; |
296 | } |
297 | } |
298 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gen_instance_throw_obj, 2, 4, gen_instance_throw); |
299 | |
300 | STATIC mp_obj_t gen_instance_close(mp_obj_t self_in) { |
301 | mp_obj_t ret; |
302 | switch (mp_obj_gen_resume(self_in, mp_const_none, MP_OBJ_FROM_PTR(&mp_const_GeneratorExit_obj), &ret)) { |
303 | case MP_VM_RETURN_YIELD: |
304 | mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("generator ignored GeneratorExit" )); |
305 | |
306 | // Swallow GeneratorExit (== successful close), and re-raise any other |
307 | case MP_VM_RETURN_EXCEPTION: |
308 | // ret should always be an instance of an exception class |
309 | if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(ret)), MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) { |
310 | return mp_const_none; |
311 | } |
312 | nlr_raise(ret); |
313 | |
314 | default: |
315 | // The only choice left is MP_VM_RETURN_NORMAL which is successful close |
316 | return mp_const_none; |
317 | } |
318 | } |
319 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(gen_instance_close_obj, gen_instance_close); |
320 | |
321 | #if MICROPY_PY_GENERATOR_PEND_THROW |
322 | STATIC mp_obj_t gen_instance_pend_throw(mp_obj_t self_in, mp_obj_t exc_in) { |
323 | mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); |
324 | if (self->pend_exc == MP_OBJ_NULL) { |
325 | mp_raise_ValueError(MP_ERROR_TEXT("generator already executing" )); |
326 | } |
327 | mp_obj_t prev = self->pend_exc; |
328 | self->pend_exc = exc_in; |
329 | return prev; |
330 | } |
331 | STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_pend_throw_obj, gen_instance_pend_throw); |
332 | #endif |
333 | |
334 | STATIC const mp_rom_map_elem_t gen_instance_locals_dict_table[] = { |
335 | { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&gen_instance_close_obj) }, |
336 | { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&gen_instance_send_obj) }, |
337 | { MP_ROM_QSTR(MP_QSTR_throw), MP_ROM_PTR(&gen_instance_throw_obj) }, |
338 | #if MICROPY_PY_GENERATOR_PEND_THROW |
339 | { MP_ROM_QSTR(MP_QSTR_pend_throw), MP_ROM_PTR(&gen_instance_pend_throw_obj) }, |
340 | #endif |
341 | }; |
342 | |
343 | STATIC MP_DEFINE_CONST_DICT(gen_instance_locals_dict, gen_instance_locals_dict_table); |
344 | |
345 | const mp_obj_type_t mp_type_gen_instance = { |
346 | { &mp_type_type }, |
347 | .name = MP_QSTR_generator, |
348 | .print = gen_instance_print, |
349 | .unary_op = mp_generic_unary_op, |
350 | .getiter = mp_identity_getiter, |
351 | .iternext = gen_instance_iternext, |
352 | .locals_dict = (mp_obj_dict_t *)&gen_instance_locals_dict, |
353 | }; |
354 | |