Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 392 lines (316 sloc) 11.451 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 {
37 if (EG(symtable_cache_ptr) >= EG(symtable_cache_limit)) {
38 zend_hash_destroy(execute_data->symbol_table);
39 FREE_HASHTABLE(execute_data->symbol_table);
40 } else {
41 zend_hash_clean(execute_data->symbol_table);
42 *(++EG(symtable_cache_ptr)) = execute_data->symbol_table;
43 }
44 }
45
247bb73 Nikita Popov Add support for generator methods
nikic authored
46 if (execute_data->current_this) {
47 zval_ptr_dtor(&execute_data->current_this);
48 }
49
64a643a Nikita Popov Free loop variables
nikic authored
50 /* If the generator is closed before it can finish execution (reach
51 * a return statement) we have to free loop variables manually, as
52 * we don't know whether the SWITCH_FREE / FREE opcodes have run */
53 if (!finished_execution) {
54 zend_op_array *op_array = execute_data->op_array;
55 zend_uint op_num = execute_data->opline - op_array->opcodes;
56
57 int i;
58 for (i = 0; i < op_array->last_brk_cont; ++i) {
59 zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i;
60
61 if (brk_cont->start < 0) {
62 continue;
63 } else if (brk_cont->start > op_num) {
64 break;
65 } else if (brk_cont->brk > op_num) {
66 zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
67
68 switch (brk_opline->opcode) {
69 case ZEND_SWITCH_FREE:
70 {
71 temp_variable *var = (temp_variable *) ((char *) execute_data->Ts + brk_opline->op1.var);
72 zval_ptr_dtor(&var->var.ptr);
73 }
74 break;
75 case ZEND_FREE:
76 {
77 temp_variable *var = (temp_variable *) ((char *) execute_data->Ts + brk_opline->op1.var);
78 zval_dtor(&var->tmp_var);
79 }
80 break;
81 }
82 }
83 }
84 }
85
9ce9a7e Nikita Popov Add initial code for suspending execution
nikic authored
86 efree(execute_data);
d49d397 Nikita Popov Close generator on return
nikic authored
87 generator->execute_data = NULL;
9ce9a7e Nikita Popov Add initial code for suspending execution
nikic authored
88 }
89
fafce58 Nikita Popov Add YIELD opcode implementation
nikic authored
90 if (generator->value) {
91 zval_ptr_dtor(&generator->value);
d49d397 Nikita Popov Close generator on return
nikic authored
92 generator->value = NULL;
fafce58 Nikita Popov Add YIELD opcode implementation
nikic authored
93 }
94
bc08c2c Nikita Popov Add support for yielding keys
nikic authored
95 if (generator->key) {
96 zval_ptr_dtor(&generator->key);
97 generator->key = NULL;
98 }
d49d397 Nikita Popov Close generator on return
nikic authored
99 }
100 /* }}} */
101
102 static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */
103 {
64a643a Nikita Popov Free loop variables
nikic authored
104 zend_generator_close(generator, 0 TSRMLS_CC);
d49d397 Nikita Popov Close generator on return
nikic authored
105
106 zend_object_std_dtor(&generator->std TSRMLS_CC);
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
107 efree(generator);
108 }
109 /* }}} */
110
111 static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
112 {
113 zend_generator *generator;
114 zend_object_value object;
115
116 generator = emalloc(sizeof(zend_generator));
117 memset(generator, 0, sizeof(zend_generator));
118
119 zend_object_std_init(&generator->std, class_type TSRMLS_CC);
120
121 object.handle = zend_objects_store_put(generator, NULL,
122 (zend_objects_free_object_storage_t) zend_generator_free_storage,
123 NULL /* no clone handler for now */
124 TSRMLS_CC
125 );
126 object.handlers = &zend_generator_handlers;
127
128 return object;
129 }
130 /* }}} */
131
132 static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* {{{ */
133 {
134 zend_error(E_RECOVERABLE_ERROR, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
135
136 return NULL;
137 }
138 /* }}} */
ca59e54 Nikita Popov Add empty Generator class
nikic authored
139
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
140 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
141 {
d49d397 Nikita Popov Close generator on return
nikic authored
142 /* The generator is already closed, thus can't resume */
143 if (!generator->execute_data) {
144 return;
145 }
146
247bb73 Nikita Popov Add support for generator methods
nikic authored
147 {
148 /* Backup executor globals */
149 zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr);
bcc7d97 Nikita Popov Set EG(current_execute_data)
nikic authored
150 zend_execute_data *original_execute_data = EG(current_execute_data);
247bb73 Nikita Popov Add support for generator methods
nikic authored
151 zend_op **original_opline_ptr = EG(opline_ptr);
152 zend_op_array *original_active_op_array = EG(active_op_array);
153 HashTable *original_active_symbol_table = EG(active_symbol_table);
154 zval *original_This = EG(This);
155 zend_class_entry *original_scope = EG(scope);
156 zend_class_entry *original_called_scope = EG(called_scope);
157
158 /* We (mis)use the return_value_ptr_ptr to provide the generator object
159 * to the executor, so YIELD will be able to set the yielded value */
160 EG(return_value_ptr_ptr) = &object;
161
162 /* Set executor globals */
bcc7d97 Nikita Popov Set EG(current_execute_data)
nikic authored
163 EG(current_execute_data) = generator->execute_data;
247bb73 Nikita Popov Add support for generator methods
nikic authored
164 EG(opline_ptr) = &generator->execute_data->opline;
165 EG(active_op_array) = generator->execute_data->op_array;
166 EG(active_symbol_table) = generator->execute_data->symbol_table;
167 EG(This) = generator->execute_data->current_this;
168 EG(scope) = generator->execute_data->current_scope;
169 EG(called_scope) = generator->execute_data->current_called_scope;
170
171 /* Go to next opcode (we don't want to run the last one again) */
172 generator->execute_data->opline++;
173
174 /* Resume execution */
175 execute_ex(generator->execute_data TSRMLS_CC);
176
177 /* Restore executor globals */
178 EG(return_value_ptr_ptr) = original_return_value_ptr_ptr;
bcc7d97 Nikita Popov Set EG(current_execute_data)
nikic authored
179 EG(current_execute_data) = original_execute_data;
247bb73 Nikita Popov Add support for generator methods
nikic authored
180 EG(opline_ptr) = original_opline_ptr;
181 EG(active_op_array) = original_active_op_array;
182 EG(active_symbol_table) = original_active_symbol_table;
183 EG(This) = original_This;
184 EG(scope) = original_scope;
185 EG(called_scope) = original_called_scope;
186 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
187 }
188 /* }}} */
189
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
190 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
191 {
192 if (!generator->value) {
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
193 zend_generator_resume(object, generator TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
194 }
195 }
196 /* }}} */
197
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
198 /* {{{ proto void Generator::rewind()
199 * Rewind the generator */
200 ZEND_METHOD(Generator, rewind)
201 {
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
202 zval *object;
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
203 zend_generator *generator;
204
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
205 if (zend_parse_parameters_none() == FAILURE) {
206 return;
207 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
208
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
209 object = getThis();
210 generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
211
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
212 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
213
214 /* Generators aren't rewindable, so rewind() only has to make sure that
215 * the generator is initialized, nothing more */
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
216 }
217 /* }}} */
218
219 /* {{{ proto bool Generator::valid()
220 * Check whether the generator is valid */
221 ZEND_METHOD(Generator, valid)
222 {
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
223 zval *object;
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
224 zend_generator *generator;
225
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
226 if (zend_parse_parameters_none() == FAILURE) {
227 return;
228 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
229
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
230 object = getThis();
231 generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
232
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
233 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
234
235 RETURN_BOOL(generator->value != NULL);
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
236 }
237 /* }}} */
238
239 /* {{{ proto mixed Generator::current()
240 * Get the current value */
241 ZEND_METHOD(Generator, current)
242 {
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
243 zval *object;
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
244 zend_generator *generator;
245
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
246 if (zend_parse_parameters_none() == FAILURE) {
247 return;
248 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
249
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
250 object = getThis();
251 generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
252
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
253 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
254
255 if (generator->value) {
cbfa96c Nikita Popov Remove wrong dtor call
nikic authored
256 RETURN_ZVAL(generator->value, 1, 0);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
257 }
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
258 }
259 /* }}} */
260
261 /* {{{ proto mixed Generator::key()
262 * Get the current key */
263 ZEND_METHOD(Generator, key)
264 {
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
265 zval *object;
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
266 zend_generator *generator;
267
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
268 if (zend_parse_parameters_none() == FAILURE) {
269 return;
270 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
271
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
272 object = getThis();
273 generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
274
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
275 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
bc08c2c Nikita Popov Add support for yielding keys
nikic authored
276
277 if (generator->key) {
278 RETURN_ZVAL(generator->key, 1, 0);
279 }
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
280 }
281 /* }}} */
282
283 /* {{{ proto void Generator::next()
284 * Advances the generator */
285 ZEND_METHOD(Generator, next)
286 {
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
287 zval *object;
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
288 zend_generator *generator;
289
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
290 if (zend_parse_parameters_none() == FAILURE) {
291 return;
292 }
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
293
fafce58 Nikita Popov Add YIELD opcode implementation
nikic authored
294 object = getThis();
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
295 generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
f627be5 Nikita Popov Add support for executing a zend_execute_data
nikic authored
296
1a99d1c Nikita Popov Add way to pass generator object to opcode handlers
nikic authored
297 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
5bb3a99 Nikita Popov Implement return for generators
nikic authored
298
299 zend_generator_resume(object, generator TSRMLS_CC);
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
300 }
301 /* }}} */
302
3600914 Nikita Popov Add support for $generator->send()
nikic authored
303 /* {{{ proto void Generator::send()
304 * Sends a value to the generator */
305 ZEND_METHOD(Generator, send)
306 {
307 zval *object, *value;
308 zend_generator *generator;
309
310 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
311 return;
312 }
313
314 object = getThis();
315 generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
316
317 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
318
12e9283 Nikita Popov Fix segfault when send()ing to a closed generator
nikic authored
319 /* The generator is already closed, thus can't send anything */
320 if (!generator->execute_data) {
321 return;
322 }
323
3600914 Nikita Popov Add support for $generator->send()
nikic authored
324 /* The sent value was initialized to NULL, so dtor that */
325 zval_ptr_dtor(generator->send_target->var.ptr_ptr);
326
327 /* Set new sent value */
328 Z_ADDREF_P(value);
329 generator->send_target->var.ptr = value;
330 generator->send_target->var.ptr_ptr = &value;
331
332 zend_generator_resume(object, generator TSRMLS_CC);
333 }
334
72a91d0 Nikita Popov Add $generator->close() method
nikic authored
335 /* {{{ proto void Generator::close()
336 * Closes the generator */
337 ZEND_METHOD(Generator, close)
338 {
339 zend_generator *generator;
340
341 if (zend_parse_parameters_none() == FAILURE) {
342 return;
343 }
344
345 generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
346
347 zend_generator_close(generator, 0);
348 }
349 /* }}} */
350
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
351 ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
352 ZEND_END_ARG_INFO()
353
3600914 Nikita Popov Add support for $generator->send()
nikic authored
354 ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
355 ZEND_ARG_INFO(0, value)
356 ZEND_END_ARG_INFO()
357
ca59e54 Nikita Popov Add empty Generator class
nikic authored
358 static const zend_function_entry generator_functions[] = {
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
359 ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC)
360 ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC)
361 ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC)
362 ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC)
363 ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC)
3600914 Nikita Popov Add support for $generator->send()
nikic authored
364 ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC)
72a91d0 Nikita Popov Add $generator->close() method
nikic authored
365 ZEND_ME(Generator, close, arginfo_generator_void, ZEND_ACC_PUBLIC)
ca59e54 Nikita Popov Add empty Generator class
nikic authored
366 ZEND_FE_END
367 };
368
369 void zend_register_generator_ce(TSRMLS_D) /* {{{ */
370 {
371 zend_class_entry ce;
372
373 INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
374 zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC);
375 zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS;
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
376 zend_ce_generator->create_object = zend_generator_create;
377
2c5ecb4 Nikita Popov Add dummy Iterator implementation
nikic authored
378 zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator);
379
40b7533 Nikita Popov Add some boilerplate code for Generator class
nikic authored
380 memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
381 zend_generator_handlers.get_constructor = zend_generator_get_constructor;
ca59e54 Nikita Popov Add empty Generator class
nikic authored
382 }
383 /* }}} */
384
385 /*
386 * Local variables:
387 * tab-width: 4
388 * c-basic-offset: 4
389 * indent-tabs-mode: t
390 * End:
391 */
Something went wrong with that request. Please try again.