From 25aa8b715e2f7eb08a9e1287c1a479951759dbfd Mon Sep 17 00:00:00 2001 From: Marcus Boerger Date: Fri, 17 Oct 2003 17:19:44 +0000 Subject: [PATCH] Added c-api for iterators # After 4 Month work and endless discussions... --- Zend/ZendTS.dsp | 8 +++ Zend/zend.h | 6 +- Zend/zend_API.h | 4 +- Zend/zend_default_classes.c | 1 + Zend/zend_exceptions.c | 1 + Zend/zend_execute.c | 76 ++++++++++++++++++++----- Zend/zend_iterators.c | 107 ++++++++++++++++++++++++++++++++++++ Zend/zend_iterators.h | 89 ++++++++++++++++++++++++++++++ configure.in | 3 +- 9 files changed, 279 insertions(+), 16 deletions(-) create mode 100755 Zend/zend_iterators.c create mode 100755 Zend/zend_iterators.h diff --git a/Zend/ZendTS.dsp b/Zend/ZendTS.dsp index 96155423242..b9537f94866 100644 --- a/Zend/ZendTS.dsp +++ b/Zend/ZendTS.dsp @@ -200,6 +200,10 @@ SOURCE=.\zend_ini_scanner.c # End Source File # Begin Source File +SOURCE=.\zend_iterators.c +# End Source File +# Begin Source File + SOURCE=".\zend_language_parser.c" # End Source File # Begin Source File @@ -367,6 +371,10 @@ SOURCE=.\zend_ini_scanner.h SOURCE=.\zend_istdiostream.h # End Source File # Begin Source File + +SOURCE=.\zend_iterators.h +# End Source File +# Begin Source File SOURCE=".\zend_language_parser.h" # End Source File diff --git a/Zend/zend.h b/Zend/zend.h index ca40000f0b1..20c36fd8bb4 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -308,6 +308,8 @@ union _zend_function; #define ZEND_CE_ABSTRACT ZEND_ACC_ABSTRACT /* same as ZEND_ACC_ABSTRACT */ +#include "zend_iterators.h" + struct _zend_class_entry { char type; char *name; @@ -331,9 +333,11 @@ struct _zend_class_entry { union _zend_function *__set; union _zend_function *__call; - + zend_class_iterator_funcs *iterator_funcs; + /* handlers */ zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC); + zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object TSRMLS_DC); zend_class_entry **interfaces; zend_uint num_interfaces; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 256b83a723b..5fd3d3ea824 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -124,7 +124,9 @@ typedef struct _zend_function_entry { class_container.__call = handle_fcall; \ class_container.__get = handle_propget; \ class_container.__set = handle_propset; \ - class_container.num_interfaces = 0; \ + class_container.num_interfaces = 0; \ + class_container.get_iterator = NULL; \ + class_container.iterator_funcs = NULL; \ } int zend_next_free_module(void); diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index 4bd6850b8f5..ad4533329e1 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -506,6 +506,7 @@ ZEND_API void zend_register_default_classes(TSRMLS_D) { zend_register_default_exception(TSRMLS_C); zend_register_reflection_api(TSRMLS_C); + zend_register_iterator_wrapper(TSRMLS_C); } /* diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 4bd6850b8f5..ad4533329e1 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -506,6 +506,7 @@ ZEND_API void zend_register_default_classes(TSRMLS_D) { zend_register_default_exception(TSRMLS_C); zend_register_reflection_api(TSRMLS_C); + zend_register_iterator_wrapper(TSRMLS_C); } /* diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 3bab0b9cb3a..fae7e65de30 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -3534,11 +3534,20 @@ int zend_fe_reset_handler(ZEND_OPCODE_HANDLER_ARGS) { zval *array_ptr, **array_ptr_ptr; HashTable *fe_ht; + zend_object_iterator *iter = NULL; + zend_class_entry *ce = NULL; if (EX(opline)->extended_value) { array_ptr_ptr = get_zval_ptr_ptr(&EX(opline)->op1, EX(Ts), BP_VAR_R); if (array_ptr_ptr == NULL) { MAKE_STD_ZVAL(array_ptr); + } else if (Z_TYPE_PP(array_ptr_ptr) == IS_OBJECT) { + ce = Z_OBJCE_PP(array_ptr_ptr); + if (!ce || ce->get_iterator == NULL) { + SEPARATE_ZVAL_IF_NOT_REF(array_ptr_ptr); + (*array_ptr_ptr)->refcount++; + } + array_ptr = *array_ptr_ptr; } else { SEPARATE_ZVAL_IF_NOT_REF(array_ptr_ptr); array_ptr = *array_ptr_ptr; @@ -3553,16 +3562,32 @@ int zend_fe_reset_handler(ZEND_OPCODE_HANDLER_ARGS) *tmp = *array_ptr; INIT_PZVAL(tmp); array_ptr = tmp; + } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { + ce = Z_OBJCE_P(array_ptr); } else { array_ptr->refcount++; } } + if (ce && ce->get_iterator) { + iter = ce->get_iterator(ce, array_ptr TSRMLS_CC); + + if (iter) { + array_ptr = zend_iterator_wrap(iter TSRMLS_CC); + } else { + array_ptr->refcount++; + } + } + PZVAL_LOCK(array_ptr); EX_T(EX(opline)->result.u.var).var.ptr = array_ptr; EX_T(EX(opline)->result.u.var).var.ptr_ptr = &EX_T(EX(opline)->result.u.var).var.ptr; - if ((fe_ht = HASH_OF(array_ptr)) != NULL) { + if (iter) { + if (iter->funcs->rewind) { + iter->funcs->rewind(iter TSRMLS_CC); + } + } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { /* probably redundant */ zend_hash_internal_pointer_reset(fe_ht); } else { @@ -3586,20 +3611,37 @@ int zend_fe_fetch_handler(ZEND_OPCODE_HANDLER_ARGS) uint str_key_len; ulong int_key; HashTable *fe_ht; + zend_object_iterator *iter = NULL; PZVAL_LOCK(array); - fe_ht = HASH_OF(array); - if (!fe_ht) { - zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num; - return 0; /* CHECK_ME */ - } else if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) { - EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num; - return 0; /* CHECK_ME */ - } - array_init(result); + switch (zend_iterator_unwrap(array, &iter TSRMLS_CC)) { + case ZEND_ITER_INVALID: + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num; + return 0; /* CHECK_ME */ + + case ZEND_ITER_PLAIN_ARRAY: + /* good old fashioned foreach on an array */ + fe_ht = HASH_OF(array); + if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) { + /* reached end of iteration */ + EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num; + return 0; /* CHECK_ME */ + } + break; + case ZEND_ITER_OBJECT: + if (iter->funcs->has_more(iter TSRMLS_CC) == FAILURE) { + /* reached end of iteration */ + EX(opline) = op_array->opcodes+EX(opline)->op2.u.opline_num; + return 0; /* CHECK_ME */ + } + iter->funcs->get_current_data(iter, &value TSRMLS_CC); + break; + } + + array_init(result); if (EX(opline)->extended_value) { SEPARATE_ZVAL_IF_NOT_REF(value); @@ -3610,7 +3652,10 @@ int zend_fe_fetch_handler(ZEND_OPCODE_HANDLER_ARGS) ALLOC_ZVAL(key); INIT_PZVAL(key); - switch (zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL)) { + + switch (iter ? + iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC) : + zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL)) { case HASH_KEY_IS_STRING: key->value.str.val = str_key; key->value.str.len = str_key_len-1; @@ -3623,7 +3668,12 @@ int zend_fe_fetch_handler(ZEND_OPCODE_HANDLER_ARGS) EMPTY_SWITCH_DEFAULT_CASE() } zend_hash_index_update(result->value.ht, 1, &key, sizeof(zval *), NULL); - zend_hash_move_forward(fe_ht); + + if (iter) { + iter->funcs->move_forward(iter TSRMLS_CC); + } else { + zend_hash_move_forward(fe_ht); + } NEXT_OPCODE(); } diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c new file mode 100755 index 00000000000..73f8903f292 --- /dev/null +++ b/Zend/zend_iterators.c @@ -0,0 +1,107 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2003 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "zend.h" +#include "zend_API.h" + +static zend_class_entry zend_iterator_class_entry; + +static zend_class_entry *iter_handler_get_ce(zval *object TSRMLS_DC) +{ + return &zend_iterator_class_entry; +} + +static zend_object_handlers iterator_object_handlers = { + ZEND_OBJECTS_STORE_HANDLERS, + NULL, /* prop read */ + NULL, /* prop write */ + NULL, /* read dim */ + NULL, /* write dim */ + NULL, + NULL, /* get */ + NULL, /* set */ + NULL, /* isset */ + NULL, /* unset */ + NULL, /* dim unset */ + NULL, /* props get */ + NULL, /* method get */ + NULL, /* call */ + NULL, /* get ctor */ + iter_handler_get_ce, + NULL, /* get class name */ + NULL, /* compare */ + NULL /* cast */ +}; + +ZEND_API void zend_register_iterator_wrapper(TSRMLS_D) +{ + INIT_CLASS_ENTRY(zend_iterator_class_entry, "__iterator_wrapper", NULL); +} + +static void iter_wrapper_dtor(void *object, zend_object_handle handle TSRMLS_DC) +{ + zend_object_iterator *iter = (zend_object_iterator*)object; + iter->funcs->dtor(iter TSRMLS_CC); +} + +ZEND_API zval *zend_iterator_wrap(zend_object_iterator *iter TSRMLS_DC) +{ + zval *wrapped; + + MAKE_STD_ZVAL(wrapped); + Z_TYPE_P(wrapped) = IS_OBJECT; + wrapped->value.obj.handle = zend_objects_store_put(iter, iter_wrapper_dtor, NULL TSRMLS_CC); + wrapped->value.obj.handlers = &iterator_object_handlers; + + return wrapped; +} + +ZEND_API enum zend_object_iterator_kind zend_iterator_unwrap( + zval *array_ptr, zend_object_iterator **iter TSRMLS_DC) +{ + zend_class_entry *ce; + + switch (Z_TYPE_P(array_ptr)) { + case IS_OBJECT: + ce = Z_OBJCE_P(array_ptr); + if (ce == &zend_iterator_class_entry) { + *iter = (zend_object_iterator *)zend_object_store_get_object(array_ptr TSRMLS_CC); + return ZEND_ITER_OBJECT; + } + return ZEND_ITER_INVALID; + + case IS_ARRAY: + *iter = NULL; + return HASH_OF(array_ptr) ? ZEND_ITER_PLAIN_ARRAY : ZEND_ITER_INVALID; + + default: + *iter = NULL; + return ZEND_ITER_INVALID; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/Zend/zend_iterators.h b/Zend/zend_iterators.h new file mode 100755 index 00000000000..6ac93dc69fd --- /dev/null +++ b/Zend/zend_iterators.h @@ -0,0 +1,89 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2003 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* These iterators were designed to operate within the foreach() + * structures provided by the engine, but could be extended for use + * with other iterative engine opcodes. + * These methods have similar semantics to the zend_hash API functions + * with similar names. + * */ + +typedef struct _zend_object_iterator zend_object_iterator; + +typedef struct _zend_object_iterator_funcs { + /* release all resources associated with this iterator instance */ + void (*dtor)(zend_object_iterator *iter TSRMLS_DC); + + /* rewind to start of data (optional, may be NULL) */ + void (*rewind)(zend_object_iterator *iter TSRMLS_DC); + + /* check for end of iteration (FAILURE or SUCCESS for more data) */ + int (*has_more)(zend_object_iterator *iter TSRMLS_DC); + + /* fetch the item data for the current element */ + void (*get_current_data)(zend_object_iterator *iter, zval ***data TSRMLS_DC); + + /* fetch the key for the current element (return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG) */ + int (*get_current_key)(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC); + + /* step forwards to next element */ + void (*move_forward)(zend_object_iterator *iter TSRMLS_DC); +} zend_object_iterator_funcs; + +struct _zend_object_iterator { + void *data; + zend_object_iterator_funcs *funcs; +}; + +typedef zval *(*zend_object_new_iterator_t)(zend_class_entry *ce, zval *object TSRMLS_DC); + +typedef struct _zend_class_iterator_funcs { + zend_object_iterator_funcs funcs; + zend_object_new_iterator_t new_iterator; + union _zend_function *zf_new_iterator; + union _zend_function *zf_has_more; + union _zend_function *zf_current; + union _zend_function *zf_key; + union _zend_function *zf_next; + union _zend_function *zf_rewind; +} zend_class_iterator_funcs; + +enum zend_object_iterator_kind { + ZEND_ITER_INVALID, + ZEND_ITER_PLAIN_ARRAY, + ZEND_ITER_OBJECT +}; + +/* given a zval, returns stuff that can be used to iterate it. */ +ZEND_API enum zend_object_iterator_kind zend_iterator_unwrap(zval *array_ptr, zend_object_iterator **iter TSRMLS_DC); + +/* given an iterator, wrap it up as a zval for use by the engine opcodes */ +ZEND_API zval *zend_iterator_wrap(zend_object_iterator *iter TSRMLS_DC); + +ZEND_API void zend_register_iterator_wrapper(TSRMLS_D); + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/configure.in b/configure.in index 39eacd28720..b4ee1723392 100644 --- a/configure.in +++ b/configure.in @@ -1173,7 +1173,8 @@ PHP_ADD_SOURCES(Zend, \ zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \ zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \ zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \ - zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c) + zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ + zend_iterators.c) if test -r "$abs_srcdir/Zend/zend_objects.c"; then PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c zend_mm.c \