Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 392 lines (316 sloc) 11.451 kB
ca59e54 @nikic 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 @nikic Add dummy Iterator implementation
nikic authored
23 #include "zend_interfaces.h"
ca59e54 @nikic Add empty Generator class
nikic authored
24 #include "zend_generators.h"
25
26 ZEND_API zend_class_entry *zend_ce_generator;
40b7533 @nikic Add some boilerplate code for Generator class
nikic authored
27 static zend_object_handlers zend_generator_handlers;
28
64a643a @nikic Free loop variables
nikic authored
29 void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC) /* {{{ */
40b7533 @nikic Add some boilerplate code for Generator class
nikic authored
30 {
9ce9a7e @nikic 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 @nikic 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 @nikic 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 @nikic Add support for generator methods
nikic authored
46 if (execute_data->current_this) {
47 zval_ptr_dtor(&execute_data->current_this);
48 }
49
64a643a @nikic 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 @nikic Add initial code for suspending execution
nikic authored
86 efree(execute_data);
d49d397 @nikic Close generator on return
nikic authored
87 generator->execute_data = NULL;
9ce9a7e @nikic Add initial code for suspending execution
nikic authored
88 }
89
fafce58 @nikic Add YIELD opcode implementation
nikic authored
90 if (generator->value) {
91 zval_ptr_dtor(&generator->value);
d49d397 @nikic Close generator on return
nikic authored
92 generator->value = NULL;
fafce58 @nikic Add YIELD opcode implementation
nikic authored
93 }
94
bc08c2c @nikic Add support for yielding keys
nikic authored
95 if (generator->key) {
96 zval_ptr_dtor(&generator->key);
97 generator->key = NULL;
98 }
d49d397 @nikic Close generator on return
nikic authored
99 }
100 /* }}} */
101
102 static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */
103 {
64a643a @nikic Free loop variables
nikic authored
104 zend_generator_close(generator, 0 TSRMLS_CC);
d49d397 @nikic Close generator on return
nikic authored
105
106 zend_object_std_dtor(&generator->std TSRMLS_CC);
40b7533 @nikic 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 @nikic Add empty Generator class
nikic authored
139
1a99d1c @nikic 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 @nikic Add support for executing a zend_execute_data
nikic authored
141 {
d49d397 @nikic 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 @nikic 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 @nikic Set EG(current_execute_data)
nikic authored
150 zend_execute_data *original_execute_data = EG(current_execute_data);
247bb73 @nikic 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 @nikic Set EG(current_execute_data)
nikic authored
163 EG(current_execute_data) = generator->execute_data;
247bb73 @nikic 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 @nikic Set EG(current_execute_data)
nikic authored
179 EG(current_execute_data) = original_execute_data;
247bb73 @nikic 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 @nikic Add support for executing a zend_execute_data
nikic authored
187 }
188 /* }}} */
189
1a99d1c @nikic 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 @nikic Add support for executing a zend_execute_data
nikic authored
191 {
192 if (!generator->value) {
1a99d1c @nikic Add way to pass generator object to opcode handlers
nikic authored
193 zend_generator_resume(object, generator TSRMLS_CC);
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
194 }
195 }
196 /* }}} */
197
2c5ecb4 @nikic Add dummy Iterator implementation
nikic authored
198 /* {{{ proto void Generator::rewind()
199 * Rewind the generator */
200 ZEND_METHOD(Generator, rewind)
201 {
1a99d1c @nikic Add way to pass generator object to opcode handlers
nikic authored
202 zval *object;
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
203 zend_generator *generator;
204
2c5ecb4 @nikic Add dummy Iterator implementation
nikic authored
205 if (zend_parse_parameters_none() == FAILURE) {
206 return;
207 }
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
208
1a99d1c @nikic 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 @nikic Add support for executing a zend_execute_data
nikic authored
211
1a99d1c @nikic Add way to pass generator object to opcode handlers
nikic authored
212 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
5bb3a99 @nikic 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 @nikic 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 @nikic Add way to pass generator object to opcode handlers
nikic authored
223 zval *object;
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
224 zend_generator *generator;
225
2c5ecb4 @nikic Add dummy Iterator implementation
nikic authored
226 if (zend_parse_parameters_none() == FAILURE) {
227 return;
228 }
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
229
1a99d1c @nikic 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 @nikic Add support for executing a zend_execute_data
nikic authored
232
1a99d1c @nikic Add way to pass generator object to opcode handlers
nikic authored
233 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
5bb3a99 @nikic Implement return for generators
nikic authored
234
235 RETURN_BOOL(generator->value != NULL);
2c5ecb4 @nikic 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 @nikic Add way to pass generator object to opcode handlers
nikic authored
243 zval *object;
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
244 zend_generator *generator;
245
2c5ecb4 @nikic Add dummy Iterator implementation
nikic authored
246 if (zend_parse_parameters_none() == FAILURE) {
247 return;
248 }
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
249
1a99d1c @nikic 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 @nikic Add support for executing a zend_execute_data
nikic authored
252
1a99d1c @nikic Add way to pass generator object to opcode handlers
nikic authored
253 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
5bb3a99 @nikic Implement return for generators
nikic authored
254
255 if (generator->value) {
cbfa96c @nikic Remove wrong dtor call
nikic authored
256 RETURN_ZVAL(generator->value, 1, 0);
5bb3a99 @nikic Implement return for generators
nikic authored
257 }
2c5ecb4 @nikic 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 @nikic Add way to pass generator object to opcode handlers
nikic authored
265 zval *object;
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
266 zend_generator *generator;
267
2c5ecb4 @nikic Add dummy Iterator implementation
nikic authored
268 if (zend_parse_parameters_none() == FAILURE) {
269 return;
270 }
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
271
1a99d1c @nikic 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 @nikic Add support for executing a zend_execute_data
nikic authored
274
1a99d1c @nikic Add way to pass generator object to opcode handlers
nikic authored
275 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
bc08c2c @nikic Add support for yielding keys
nikic authored
276
277 if (generator->key) {
278 RETURN_ZVAL(generator->key, 1, 0);
279 }
2c5ecb4 @nikic 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 @nikic Add way to pass generator object to opcode handlers
nikic authored
287 zval *object;
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
288 zend_generator *generator;
289
2c5ecb4 @nikic Add dummy Iterator implementation
nikic authored
290 if (zend_parse_parameters_none() == FAILURE) {
291 return;
292 }
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
293
fafce58 @nikic Add YIELD opcode implementation
nikic authored
294 object = getThis();
1a99d1c @nikic Add way to pass generator object to opcode handlers
nikic authored
295 generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
f627be5 @nikic Add support for executing a zend_execute_data
nikic authored
296
1a99d1c @nikic Add way to pass generator object to opcode handlers
nikic authored
297 zend_generator_ensure_initialized(object, generator TSRMLS_CC);
5bb3a99 @nikic Implement return for generators
nikic authored
298
299 zend_generator_resume(object, generator TSRMLS_CC);
2c5ecb4 @nikic Add dummy Iterator implementation
nikic authored
300 }
301 /* }}} */
302
3600914 @nikic 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 @nikic 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 @nikic 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 @nikic 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 @nikic Add dummy Iterator implementation
nikic authored
351 ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
352 ZEND_END_ARG_INFO()
353
3600914 @nikic 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 @nikic Add empty Generator class
nikic authored
358 static const zend_function_entry generator_functions[] = {
2c5ecb4 @nikic 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 @nikic Add support for $generator->send()
nikic authored
364 ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC)
72a91d0 @nikic Add $generator->close() method
nikic authored
365 ZEND_ME(Generator, close, arginfo_generator_void, ZEND_ACC_PUBLIC)
ca59e54 @nikic 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 @nikic Add some boilerplate code for Generator class
nikic authored
376 zend_ce_generator->create_object = zend_generator_create;
377
2c5ecb4 @nikic Add dummy Iterator implementation
nikic authored
378 zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator);
379
40b7533 @nikic 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 @nikic 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.