Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 781 lines (622 sloc) 25.112 kb
ca59e54 Nikita Popov Add empty Generator class
nikic authored
1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Nikita Popov <nikic@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 #include "zend.h"
22 #include "zend_API.h"
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
23 #include "zend_interfaces.h"
94b2cca Nikita Popov Fix throwing of exceptions within a generator
nikic authored
24 #include "zend_exceptions.h"
ca59e54 Nikita Popov Add empty Generator class
nikic authored
25 #include "zend_generators.h"
26
27 ZEND_API zend_class_entry *zend_ce_generator;
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
28 static zend_object_handlers zend_generator_handlers;
29
64a643a Nikita Popov Free loop variables
nikic authored
30 void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC) /* {{{ */
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
31 {
9ce9a7e Nikita Popov Add initial code for suspending execution
nikic authored
32 if (generator->execute_data) {
33 zend_execute_data *execute_data = generator->execute_data;
34
35 if (!execute_data->symbol_table) {
4aab08b Nikita Popov Properly free resources when generator return value not used
nikic authored
36 zend_free_compiled_variables(execute_data->CVs, execute_data->op_array->last_var);
9ce9a7e Nikita Popov Add initial code for suspending execution
nikic authored
37 } else {
6233408 Nikita Popov Fix thread safe build
nikic authored
38 zend_clean_and_cache_symbol_table(execute_data->symbol_table TSRMLS_CC);
9ce9a7e Nikita Popov Add initial code for suspending execution
nikic authored
39 }
40
247bb73 Nikita Popov Add support for generator methods
nikic authored
41 if (execute_data->current_this) {
42 zval_ptr_dtor(&execute_data->current_this);
43 }
44
bf82f46 Nikita Popov Properly handle yield during method calls
nikic authored
45 if (execute_data->object) {
46 zval_ptr_dtor(&execute_data->object);
47 }
48
64a643a Nikita Popov Free loop variables
nikic authored
49 /* If the generator is closed before it can finish execution (reach
50 * a return statement) we have to free loop variables manually, as
51 * we don't know whether the SWITCH_FREE / FREE opcodes have run */
52 if (!finished_execution) {
53 zend_op_array *op_array = execute_data->op_array;
54 zend_uint op_num = execute_data->opline - op_array->opcodes;
55
56 int i;
57 for (i = 0; i < op_array->last_brk_cont; ++i) {
58 zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i;
59
60 if (brk_cont->start < 0) {
61 continue;
62 } else if (brk_cont->start > op_num) {
63 break;
64 } else if (brk_cont->brk > op_num) {
65 zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
66
67 switch (brk_opline->opcode) {
68 case ZEND_SWITCH_FREE:
69 {
70 temp_variable *var = (temp_variable *) ((char *) execute_data->Ts + brk_opline->op1.var);
71 zval_ptr_dtor(&var->var.ptr);
72 }
73 break;
74 case ZEND_FREE:
75 {
76 temp_variable *var = (temp_variable *) ((char *) execute_data->Ts + brk_opline->op1.var);
77 zval_dtor(&var->tmp_var);
78 }
79 break;
80 }
81 }
82 }
83 }
84
ee89e22 Nikita Popov Allow yielding during function calls
nikic authored
85 /* Clear any backed up stack arguments */
86 if (generator->backed_up_stack) {
87 zval **zvals = (zval **) generator->backed_up_stack;
88 size_t zval_num = generator->backed_up_stack_size / sizeof(zval *);
89 int i;
90
91 for (i = 0; i < zval_num; i++) {
92 zval_ptr_dtor(&zvals[i]);
93 }
94
95 efree(generator->backed_up_stack);
96 }
97
f169b26 Nikita Popov Fix backtraces and func_get_args()
nikic authored
98 /* We have added an additional stack frame in prev_execute_data, so we
99 * have to free it. It also contains the arguments passed to the
100 * generator (for func_get_args) so those have to be freed too. */
101 {
102 zend_execute_data *prev_execute_data = execute_data->prev_execute_data;
103 void **arguments = prev_execute_data->function_state.arguments;
104
105 if (arguments) {
106 int arguments_count = (int) (zend_uintptr_t) *arguments;
107 zval **arguments_start = (zval **) (arguments - arguments_count);
108 int i;
109
110 for (i = 0; i < arguments_count; ++i) {
111 zval_ptr_dtor(arguments_start + i);
112 }
113
114 efree(arguments_start);
115 }
116
117 efree(prev_execute_data);
118 }
119
9ce9a7e Nikita Popov Add initial code for suspending execution
nikic authored
120 efree(execute_data);
d49d397 Nikita Popov Close generator on return
nikic authored
121 generator->execute_data = NULL;
9ce9a7e Nikita Popov Add initial code for suspending execution
nikic authored
122 }
123
fafce58 Nikita Popov Add YIELD opcode implementation
nikic authored
124 if (generator->value) {
125 zval_ptr_dtor(&generator->value);
d49d397 Nikita Popov Close generator on return
nikic authored
126 generator->value = NULL;
fafce58 Nikita Popov Add YIELD opcode implementation
nikic authored
127 }
128
bc08c2c Nikita Popov Add support for yielding keys
nikic authored
129 if (generator->key) {
130 zval_ptr_dtor(&generator->key);
131 generator->key = NULL;
132 }
d49d397 Nikita Popov Close generator on return
nikic authored
133 }
134 /* }}} */
135
136 static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */
137 {
64a643a Nikita Popov Free loop variables
nikic authored
138 zend_generator_close(generator, 0 TSRMLS_CC);
d49d397 Nikita Popov Close generator on return
nikic authored
139
140 zend_object_std_dtor(&generator->std TSRMLS_CC);
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
141 efree(generator);
142 }
143 /* }}} */
144
6117f4c Nikita Popov Add cloning support for generators
nikic authored
145 static void zend_generator_clone_storage(zend_generator *orig, zend_generator **clone_ptr) /* {{{ */
146 {
147 zend_generator *clone = emalloc(sizeof(zend_generator));
148 memcpy(clone, orig, sizeof(zend_generator));
149
150 if (orig->execute_data) {
151 /* Create a few shorter aliases to the old execution data */
152 zend_execute_data *execute_data = orig->execute_data;
153 zend_op_array *op_array = execute_data->op_array;
154 HashTable *symbol_table = execute_data->symbol_table;
155
156 /* Alloc separate execution context, as well as separate sections for
157 * compiled variables and temporary variables */
158 size_t execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data));
159 size_t CVs_size = ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (symbol_table ? 1 : 2));
160 size_t Ts_size = ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T;
161 size_t total_size = execute_data_size + CVs_size + Ts_size;
162
163 clone->execute_data = emalloc(total_size);
164
165 /* Copy the zend_execute_data struct */
166 memcpy(clone->execute_data, execute_data, execute_data_size);
167
168 /* Set the pointers to the memory segments for the compiled and
169 * temporary variables (which are located after the execute_data) */
170 clone->execute_data->CVs = (zval ***) ((char *) clone->execute_data + execute_data_size);
171 clone->execute_data->Ts = (temp_variable *) ((char *) clone->execute_data->CVs + CVs_size);
172
173 /* Zero out the compiled variables section */
174 memset(clone->execute_data->CVs, 0, sizeof(zval **) * op_array->last_var);
175
176 if (!symbol_table) {
177 int i;
178
179 /* Copy compiled variables */
180 for (i = 0; i < op_array->last_var; i++) {
181 if (execute_data->CVs[i]) {
182 clone->execute_data->CVs[i] = (zval **) clone->execute_data->CVs + op_array->last_var + i;
183 *clone->execute_data->CVs[i] = (zval *) orig->execute_data->CVs[op_array->last_var + i];
184 Z_ADDREF_PP(clone->execute_data->CVs[i]);
185 }
186 }
187 } else {
188 /* Copy symbol table */
189 ALLOC_HASHTABLE(clone->execute_data->symbol_table);
190 zend_hash_init(clone->execute_data->symbol_table, zend_hash_num_elements(symbol_table), NULL, ZVAL_PTR_DTOR, 0);
191 zend_hash_copy(clone->execute_data->symbol_table, symbol_table, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
192
193 /* Update zval** pointers for compiled variables */
194 {
195 int i;
196 for (i = 0; i < op_array->last_var; i++) {
197 if (zend_hash_quick_find(clone->execute_data->symbol_table, op_array->vars[i].name, op_array->vars[i].name_len + 1, op_array->vars[i].hash_value, (void **) &clone->execute_data->CVs[i]) == FAILURE) {
198 clone->execute_data->CVs[i] = NULL;
199 }
200 }
201 }
202 }
203
204 /* Copy the temporary variables */
205 memcpy(clone->execute_data->Ts, orig->execute_data->Ts, Ts_size);
206
207 /* Add references to loop variables */
208 {
209 zend_uint op_num = execute_data->opline - op_array->opcodes;
210
211 int i;
212 for (i = 0; i < op_array->last_brk_cont; ++i) {
213 zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i;
214
215 if (brk_cont->start < 0) {
216 continue;
217 } else if (brk_cont->start > op_num) {
218 break;
219 } else if (brk_cont->brk > op_num) {
220 zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
221
222 if (brk_opline->opcode == ZEND_SWITCH_FREE) {
223 temp_variable *var = (temp_variable *) ((char *) execute_data->Ts + brk_opline->op1.var);
224
225 Z_ADDREF_P(var->var.ptr);
226 }
227 }
228 }
229 }
230
231 if (orig->backed_up_stack) {
232 /* Copy backed up stack */
233 clone->backed_up_stack = emalloc(orig->backed_up_stack_size);
234 memcpy(clone->backed_up_stack, orig->backed_up_stack, orig->backed_up_stack_size);
235
236 /* Add refs to stack variables */
237 {
238 zval **zvals = (zval **) orig->backed_up_stack;
239 size_t zval_num = orig->backed_up_stack_size / sizeof(zval *);
240 int i;
241
242 for (i = 0; i < zval_num; i++) {
243 Z_ADDREF_P(zvals[i]);
244 }
245 }
246 }
247
248 /* Update the send_target to use the temporary variable with the same
d939d2d Nikita Popov Add sceleton for yield* expression
nikic authored
249 * offset as the original generator, but in our temporary variable
6117f4c Nikita Popov Add cloning support for generators
nikic authored
250 * memory segment. */
251 if (orig->send_target) {
252 size_t offset = (char *) orig->send_target - (char *) execute_data->Ts;
253 clone->send_target = (temp_variable *) (
254 (char *) clone->execute_data->Ts + offset
255 );
256 Z_ADDREF_P(clone->send_target->var.ptr);
257 }
bf82f46 Nikita Popov Properly handle yield during method calls
nikic authored
258
40760ec Nikita Popov Fix cloning of generator methods
nikic authored
259 if (execute_data->current_this) {
260 Z_ADDREF_P(execute_data->current_this);
261 }
262
bf82f46 Nikita Popov Properly handle yield during method calls
nikic authored
263 if (execute_data->object) {
264 Z_ADDREF_P(execute_data->object);
265 }
f169b26 Nikita Popov Fix backtraces and func_get_args()
nikic authored
266
267 /* Prev execute data contains an additional stack frame (for proper)
268 * backtraces) which has to be copied. */
269 clone->execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data));
270 memcpy(clone->execute_data->prev_execute_data, execute_data->prev_execute_data, sizeof(zend_execute_data));
271
272 /* It also contains the arguments passed to the generator, which also
273 * have to be copied */
274 if (execute_data->prev_execute_data->function_state.arguments) {
275 clone->execute_data->prev_execute_data->function_state.arguments
276 = zend_copy_arguments(execute_data->prev_execute_data->function_state.arguments);
277 }
6117f4c Nikita Popov Add cloning support for generators
nikic authored
278 }
279
280 /* The value and key are known not to be references, so simply add refs */
281 if (orig->value) {
282 Z_ADDREF_P(orig->value);
283 }
284
285 if (orig->key) {
286 Z_ADDREF_P(orig->key);
287 }
288
289 *clone_ptr = clone;
290 }
291 /* }}} */
292
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
293 static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
294 {
295 zend_generator *generator;
296 zend_object_value object;
297
298 generator = emalloc(sizeof(zend_generator));
299 memset(generator, 0, sizeof(zend_generator));
300
8790160 Nikita Popov Add auto-increment keys
nikic authored
301 /* The key will be incremented on first use, so it'll start at 0 */
302 generator->largest_used_integer_key = -1;
303
ab75ed6 Nikita Popov Disallow closing a generator during its execution
nikic authored
304 generator->is_currently_running = 0;
305
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
306 zend_object_std_init(&generator->std, class_type TSRMLS_CC);
307
308 object.handle = zend_objects_store_put(generator, NULL,
309 (zend_objects_free_object_storage_t) zend_generator_free_storage,
6117f4c Nikita Popov Add cloning support for generators
nikic authored
310 (zend_objects_store_clone_t) zend_generator_clone_storage
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
311 TSRMLS_CC
312 );
313 object.handlers = &zend_generator_handlers;
314
315 return object;
316 }
317 /* }}} */
318
c9709bf Nikita Popov Remove asterix modifier (*) for generators
nikic authored
319 /* Requires globals EG(scope), EG(current_scope), EG(This),
320 * EG(active_symbol_table) and EG(current_execute_data). */
321 zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC) /* {{{ */
322 {
323 zval *return_value;
324 zend_generator *generator;
325
326 /* Create new execution context. We have to back up and restore
327 * EG(current_execute_data) and EG(opline_ptr) here because the function
328 * modifies it. */
329 zend_execute_data *current_execute_data = EG(current_execute_data);
330 zend_op **opline_ptr = EG(opline_ptr);
331 zend_execute_data *execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC);
332 EG(current_execute_data) = current_execute_data;
333 EG(opline_ptr) = opline_ptr;
334
335 ALLOC_INIT_ZVAL(return_value);
336 object_init_ex(return_value, zend_ce_generator);
337
338 if (EG(This)) {
339 Z_ADDREF_P(EG(This));
340 }
341
342 /* Back up executor globals. */
343 execute_data->current_scope = EG(scope);
344 execute_data->current_called_scope = EG(called_scope);
345 execute_data->symbol_table = EG(active_symbol_table);
346 execute_data->current_this = EG(This);
347
348 /* Save execution context in generator object. */
349 generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC);
350 generator->execute_data = execute_data;
351
352 /* We have to add another stack frame so the generator function shows
353 * up in backtraces and func_get_all() can access the function
354 * arguments. */
355 execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data));
94b2cca Nikita Popov Fix throwing of exceptions within a generator
nikic authored
356 memset(execute_data->prev_execute_data, 0, sizeof(zend_execute_data));
357 execute_data->prev_execute_data->function_state.function = (zend_function *) op_array;
c9709bf Nikita Popov Remove asterix modifier (*) for generators
nikic authored
358 if (EG(current_execute_data)) {
359 execute_data->prev_execute_data->function_state.arguments = zend_copy_arguments(EG(current_execute_data)->function_state.arguments);
360 } else {
361 execute_data->prev_execute_data->function_state.arguments = NULL;
362 }
363
364 return return_value;
365 }
366 /* }}} */
367
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
368 static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* {{{ */
369 {
370 zend_error(E_RECOVERABLE_ERROR, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
371
372 return NULL;
373 }
374 /* }}} */
ca59e54 Nikita Popov Add empty Generator class
nikic authored
375
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
376 static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
377 {
d49d397 Nikita Popov Close generator on return
nikic authored
378 /* The generator is already closed, thus can't resume */
379 if (!generator->execute_data) {
380 return;
381 }
382
247bb73 Nikita Popov Add support for generator methods
nikic authored
383 {
384 /* Backup executor globals */
385 zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr);
bcc7d97 Nikita Popov Set EG(current_execute_data)
nikic authored
386 zend_execute_data *original_execute_data = EG(current_execute_data);
247bb73 Nikita Popov Add support for generator methods
nikic authored
387 zend_op **original_opline_ptr = EG(opline_ptr);
388 zend_op_array *original_active_op_array = EG(active_op_array);
389 HashTable *original_active_symbol_table = EG(active_symbol_table);
390 zval *original_This = EG(This);
391 zend_class_entry *original_scope = EG(scope);
392 zend_class_entry *original_called_scope = EG(called_scope);
393
ab75ed6 Nikita Popov Disallow closing a generator during its execution
nikic authored
394 zend_bool original_is_currently_running = generator->is_currently_running;
395
ee89e22 Nikita Popov Allow yielding during function calls
nikic authored
396 /* Remember the current stack position so we can back up pushed args */
397 generator->original_stack_top = zend_vm_stack_top(TSRMLS_C);
398
399 /* If there is a backed up stack copy it to the VM stack */
400 if (generator->backed_up_stack) {
401 void *stack = zend_vm_stack_alloc(generator->backed_up_stack_size TSRMLS_CC);
402 memcpy(stack, generator->backed_up_stack, generator->backed_up_stack_size);
403 efree(generator->backed_up_stack);
404 generator->backed_up_stack = NULL;
405 }
406
247bb73 Nikita Popov Add support for generator methods
nikic authored
407 /* We (mis)use the return_value_ptr_ptr to provide the generator object
408 * to the executor, so YIELD will be able to set the yielded value */
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
409 EG(return_value_ptr_ptr) = (zval **) generator;
247bb73 Nikita Popov Add support for generator methods
nikic authored
410
411 /* Set executor globals */
bcc7d97 Nikita Popov Set EG(current_execute_data)
nikic authored
412 EG(current_execute_data) = generator->execute_data;
247bb73 Nikita Popov Add support for generator methods
nikic authored
413 EG(opline_ptr) = &generator->execute_data->opline;
414 EG(active_op_array) = generator->execute_data->op_array;
415 EG(active_symbol_table) = generator->execute_data->symbol_table;
416 EG(This) = generator->execute_data->current_this;
417 EG(scope) = generator->execute_data->current_scope;
418 EG(called_scope) = generator->execute_data->current_called_scope;
419
ab75ed6 Nikita Popov Disallow closing a generator during its execution
nikic authored
420 generator->is_currently_running = 1;
421
f169b26 Nikita Popov Fix backtraces and func_get_args()
nikic authored
422 /* We want the backtrace to look as if the generator function was
423 * called from whatever method we are current running (e.g. next()).
424 * The first prev_execute_data contains an additional stack frame,
425 * which makes the generator function show up in the backtrace and
426 * makes the arguments available to func_get_args(). So we have to
427 * set the prev_execute_data of that prev_execute_data :) */
428 generator->execute_data->prev_execute_data->prev_execute_data = original_execute_data;
7b3bfa5 Nikita Popov Improve backtraces from generators
nikic authored
429
247bb73 Nikita Popov Add support for generator methods
nikic authored
430 /* Resume execution */
431 execute_ex(generator->execute_data TSRMLS_CC);
432
433 /* Restore executor globals */
434 EG(return_value_ptr_ptr) = original_return_value_ptr_ptr;
bcc7d97 Nikita Popov Set EG(current_execute_data)
nikic authored
435 EG(current_execute_data) = original_execute_data;
247bb73 Nikita Popov Add support for generator methods
nikic authored
436 EG(opline_ptr) = original_opline_ptr;
437 EG(active_op_array) = original_active_op_array;
438 EG(active_symbol_table) = original_active_symbol_table;
439 EG(This) = original_This;
440 EG(scope) = original_scope;
441 EG(called_scope) = original_called_scope;
ee89e22 Nikita Popov Allow yielding during function calls
nikic authored
442
ab75ed6 Nikita Popov Disallow closing a generator during its execution
nikic authored
443 generator->is_currently_running = original_is_currently_running;
444
ee89e22 Nikita Popov Allow yielding during function calls
nikic authored
445 /* The stack top before and after the execution differ, i.e. there are
446 * arguments pushed to the stack. */
447 if (generator->original_stack_top != zend_vm_stack_top(TSRMLS_C)) {
448 generator->backed_up_stack_size = (zend_vm_stack_top(TSRMLS_C) - generator->original_stack_top) * sizeof(void *);
449 generator->backed_up_stack = emalloc(generator->backed_up_stack_size);
450 memcpy(generator->backed_up_stack, generator->original_stack_top, generator->backed_up_stack_size);
451 zend_vm_stack_free(generator->original_stack_top TSRMLS_CC);
452 }
94b2cca Nikita Popov Fix throwing of exceptions within a generator
nikic authored
453
454 /* If an exception was thrown in the generator we have to internally
455 * rethrow it in the parent scope. */
456 if (UNEXPECTED(EG(exception) != NULL)) {
457 zend_throw_exception_internal(NULL TSRMLS_CC);
458 }
247bb73 Nikita Popov Add support for generator methods
nikic authored
459 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
460 }
461 /* }}} */
462
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
463 static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_DC) /* {{{ */
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
464 {
465 if (!generator->value) {
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
466 zend_generator_resume(generator TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
467 }
468 }
469 /* }}} */
470
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
471 /* {{{ proto void Generator::rewind()
472 * Rewind the generator */
473 ZEND_METHOD(Generator, rewind)
474 {
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
475 zend_generator *generator;
476
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
477 if (zend_parse_parameters_none() == FAILURE) {
478 return;
479 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
480
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
481 generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
482
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
483 zend_generator_ensure_initialized(generator TSRMLS_CC);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
484
485 /* Generators aren't rewindable, so rewind() only has to make sure that
486 * the generator is initialized, nothing more */
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
487 }
488 /* }}} */
489
490 /* {{{ proto bool Generator::valid()
491 * Check whether the generator is valid */
492 ZEND_METHOD(Generator, valid)
493 {
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
494 zend_generator *generator;
495
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
496 if (zend_parse_parameters_none() == FAILURE) {
497 return;
498 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
499
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
500 generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
501
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
502 zend_generator_ensure_initialized(generator TSRMLS_CC);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
503
504 RETURN_BOOL(generator->value != NULL);
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
505 }
506 /* }}} */
507
508 /* {{{ proto mixed Generator::current()
509 * Get the current value */
510 ZEND_METHOD(Generator, current)
511 {
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
512 zend_generator *generator;
513
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
514 if (zend_parse_parameters_none() == FAILURE) {
515 return;
516 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
517
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
518 generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
519
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
520 zend_generator_ensure_initialized(generator TSRMLS_CC);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
521
522 if (generator->value) {
cbfa96c Nikita Popov Remove wrong dtor call
nikic authored
523 RETURN_ZVAL(generator->value, 1, 0);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
524 }
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
525 }
526 /* }}} */
527
528 /* {{{ proto mixed Generator::key()
529 * Get the current key */
530 ZEND_METHOD(Generator, key)
531 {
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
532 zend_generator *generator;
533
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
534 if (zend_parse_parameters_none() == FAILURE) {
535 return;
536 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
537
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
538 generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
539
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
540 zend_generator_ensure_initialized(generator TSRMLS_CC);
bc08c2c Nikita Popov Add support for yielding keys
nikic authored
541
542 if (generator->key) {
543 RETURN_ZVAL(generator->key, 1, 0);
544 }
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
545 }
546 /* }}} */
547
548 /* {{{ proto void Generator::next()
549 * Advances the generator */
550 ZEND_METHOD(Generator, next)
551 {
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
552 zend_generator *generator;
553
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
554 if (zend_parse_parameters_none() == FAILURE) {
555 return;
556 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
557
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
558 generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
559
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
560 zend_generator_ensure_initialized(generator TSRMLS_CC);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
561
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
562 zend_generator_resume(generator TSRMLS_CC);
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
563 }
564 /* }}} */
565
1477be9 Nikita Popov Make $generator->send() return the current value
nikic authored
566 /* {{{ proto mixed Generator::send()
3600914 Nikita Popov Add support for $generator->send()
nikic authored
567 * Sends a value to the generator */
568 ZEND_METHOD(Generator, send)
569 {
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
570 zval *value;
3600914 Nikita Popov Add support for $generator->send()
nikic authored
571 zend_generator *generator;
572
573 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
574 return;
575 }
576
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
577 generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
3600914 Nikita Popov Add support for $generator->send()
nikic authored
578
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
579 zend_generator_ensure_initialized(generator TSRMLS_CC);
3600914 Nikita Popov Add support for $generator->send()
nikic authored
580
12e9283 Nikita Popov Fix segfault when send()ing to a closed generator
nikic authored
581 /* The generator is already closed, thus can't send anything */
582 if (!generator->execute_data) {
583 return;
584 }
585
3600914 Nikita Popov Add support for $generator->send()
nikic authored
586 /* The sent value was initialized to NULL, so dtor that */
6117f4c Nikita Popov Add cloning support for generators
nikic authored
587 zval_ptr_dtor(&generator->send_target->var.ptr);
588
3600914 Nikita Popov Add support for $generator->send()
nikic authored
589 /* Set new sent value */
590 Z_ADDREF_P(value);
591 generator->send_target->var.ptr = value;
592 generator->send_target->var.ptr_ptr = &value;
593
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
594 zend_generator_resume(generator TSRMLS_CC);
1477be9 Nikita Popov Make $generator->send() return the current value
nikic authored
595
596 if (generator->value) {
597 RETURN_ZVAL(generator->value, 1, 0);
598 }
3600914 Nikita Popov Add support for $generator->send()
nikic authored
599 }
600
72a91d0 Nikita Popov Add $generator->close() method
nikic authored
601 /* {{{ proto void Generator::close()
602 * Closes the generator */
603 ZEND_METHOD(Generator, close)
604 {
605 zend_generator *generator;
606
607 if (zend_parse_parameters_none() == FAILURE) {
608 return;
609 }
610
611 generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
612
ab75ed6 Nikita Popov Disallow closing a generator during its execution
nikic authored
613 if (generator->is_currently_running) {
614 zend_error(E_WARNING, "A generator cannot be closed while it is running");
615 return;
616 }
617
6233408 Nikita Popov Fix thread safe build
nikic authored
618 zend_generator_close(generator, 0 TSRMLS_CC);
72a91d0 Nikita Popov Add $generator->close() method
nikic authored
619 }
620 /* }}} */
621
04e781f Nikita Popov Implement get_iterator
nikic authored
622 /* get_iterator implementation */
623
624 typedef struct _zend_generator_iterator {
625 zend_object_iterator intern;
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
626
627 /* The generator object zval has to be stored, because the iterator is
628 * holding a ref to it, which has to be dtored. */
629 zval *object;
04e781f Nikita Popov Implement get_iterator
nikic authored
630 } zend_generator_iterator;
631
632 static void zend_generator_iterator_dtor(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
633 {
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
634 zval *object = ((zend_generator_iterator *) iterator)->object;
635
636 zval_ptr_dtor(&object);
637
04e781f Nikita Popov Implement get_iterator
nikic authored
638 efree(iterator);
639 }
640 /* }}} */
641
642 static int zend_generator_iterator_valid(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
643 {
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
644 zend_generator *generator = (zend_generator *) iterator->data;
04e781f Nikita Popov Implement get_iterator
nikic authored
645
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
646 zend_generator_ensure_initialized(generator TSRMLS_CC);
04e781f Nikita Popov Implement get_iterator
nikic authored
647
648 return generator->value != NULL ? SUCCESS : FAILURE;
649 }
650 /* }}} */
651
652 static void zend_generator_iterator_get_data(zend_object_iterator *iterator, zval ***data TSRMLS_DC) /* {{{ */
653 {
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
654 zend_generator *generator = (zend_generator *) iterator->data;
04e781f Nikita Popov Implement get_iterator
nikic authored
655
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
656 zend_generator_ensure_initialized(generator TSRMLS_CC);
04e781f Nikita Popov Implement get_iterator
nikic authored
657
658 if (generator->value) {
659 *data = &generator->value;
660 } else {
661 *data = NULL;
662 }
663 }
664 /* }}} */
665
666 static int zend_generator_iterator_get_key(zend_object_iterator *iterator, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */
667 {
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
668 zend_generator *generator = (zend_generator *) iterator->data;
04e781f Nikita Popov Implement get_iterator
nikic authored
669
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
670 zend_generator_ensure_initialized(generator TSRMLS_CC);
04e781f Nikita Popov Implement get_iterator
nikic authored
671
672 if (!generator->key) {
673 return HASH_KEY_NON_EXISTANT;
674 }
675
676 if (Z_TYPE_P(generator->key) == IS_LONG) {
677 *int_key = Z_LVAL_P(generator->key);
678 return HASH_KEY_IS_LONG;
679 }
680
681 if (Z_TYPE_P(generator->key) == IS_STRING) {
682 *str_key = estrndup(Z_STRVAL_P(generator->key), Z_STRLEN_P(generator->key));
683 *str_key_len = Z_STRLEN_P(generator->key) + 1;
684 return HASH_KEY_IS_STRING;
685 }
686
687 /* Waiting for Etienne's patch to allow arbitrary zval keys. Until then
688 * error out on non-int and non-string keys. */
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
689 zend_error_noreturn(E_ERROR, "Currently only int and string keys can be yielded");
04e781f Nikita Popov Implement get_iterator
nikic authored
690 }
691 /* }}} */
692
693 static void zend_generator_iterator_move_forward(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
694 {
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
695 zend_generator *generator = (zend_generator *) iterator->data;
04e781f Nikita Popov Implement get_iterator
nikic authored
696
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
697 zend_generator_ensure_initialized(generator TSRMLS_CC);
04e781f Nikita Popov Implement get_iterator
nikic authored
698
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
699 zend_generator_resume(generator TSRMLS_CC);
04e781f Nikita Popov Implement get_iterator
nikic authored
700 }
701 /* }}} */
702
703 static zend_object_iterator_funcs zend_generator_iterator_functions = {
704 zend_generator_iterator_dtor,
705 zend_generator_iterator_valid,
706 zend_generator_iterator_get_data,
707 zend_generator_iterator_get_key,
708 zend_generator_iterator_move_forward,
709 NULL
710 };
711
712 zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
713 {
714 zend_generator_iterator *iterator;
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
715 zend_generator *generator;
04e781f Nikita Popov Implement get_iterator
nikic authored
716
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
717 generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
718
85f077c Nikita Popov Add support by yielding by-reference
nikic authored
719 if (by_ref && !(generator->execute_data->op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
720 zend_error(E_ERROR, "You can only iterate a generator by-reference if it declared that it yields by-reference");
721 }
722
04e781f Nikita Popov Implement get_iterator
nikic authored
723 iterator = emalloc(sizeof(zend_generator_iterator));
724 iterator->intern.funcs = &zend_generator_iterator_functions;
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
725 iterator->intern.data = (void *) generator;
04e781f Nikita Popov Implement get_iterator
nikic authored
726
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
727 /* We have to keep a reference to the generator object zval around,
728 * otherwise the generator may be destroyed during iteration. */
04e781f Nikita Popov Implement get_iterator
nikic authored
729 Z_ADDREF_P(object);
14766e1 Nikita Popov Pass zend_generator directly to Zend VM
nikic authored
730 iterator->object = object;
04e781f Nikita Popov Implement get_iterator
nikic authored
731
732 return (zend_object_iterator *) iterator;
733 }
734 /* }}} */
735
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
736 ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
737 ZEND_END_ARG_INFO()
738
3600914 Nikita Popov Add support for $generator->send()
nikic authored
739 ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
740 ZEND_ARG_INFO(0, value)
741 ZEND_END_ARG_INFO()
742
ca59e54 Nikita Popov Add empty Generator class
nikic authored
743 static const zend_function_entry generator_functions[] = {
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
744 ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC)
745 ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC)
746 ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC)
747 ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC)
748 ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC)
3600914 Nikita Popov Add support for $generator->send()
nikic authored
749 ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC)
72a91d0 Nikita Popov Add $generator->close() method
nikic authored
750 ZEND_ME(Generator, close, arginfo_generator_void, ZEND_ACC_PUBLIC)
ca59e54 Nikita Popov Add empty Generator class
nikic authored
751 ZEND_FE_END
752 };
753
754 void zend_register_generator_ce(TSRMLS_D) /* {{{ */
755 {
756 zend_class_entry ce;
757
758 INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
759 zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC);
760 zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS;
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
761 zend_ce_generator->create_object = zend_generator_create;
762
268740d Nikita Popov Fix implementation of Iterator interface
nikic authored
763 /* get_iterator has to be assigned *after* implementing the inferface */
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
764 zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator);
268740d Nikita Popov Fix implementation of Iterator interface
nikic authored
765 zend_ce_generator->get_iterator = zend_generator_get_iterator;
766 zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions;
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
767
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
768 memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
769 zend_generator_handlers.get_constructor = zend_generator_get_constructor;
6117f4c Nikita Popov Add cloning support for generators
nikic authored
770 zend_generator_handlers.clone_obj = zend_objects_store_clone_obj;
ca59e54 Nikita Popov Add empty Generator class
nikic authored
771 }
772 /* }}} */
773
774 /*
775 * Local variables:
776 * tab-width: 4
777 * c-basic-offset: 4
778 * indent-tabs-mode: t
779 * End:
780 */
Something went wrong with that request. Please try again.