Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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