From 9fc46d3a46144dc63fea49b8caeabfa4f3b5ac9b Mon Sep 17 00:00:00 2001 From: SVN Migration Date: Thu, 14 Dec 2000 14:18:39 +0000 Subject: This commit was manufactured by cvs2svn to create tag 'php_4_0_4RC5'. --- TSRM/TSRM.h | 116 ++ TSRM/configure.in | 33 + Zend/acconfig.h | 68 ++ Zend/configure.in | 45 + Zend/zend_API.h | 426 ++++++++ Zend/zend_builtin_functions.c | 1013 ++++++++++++++++++ Zend/zend_compile.c | 2352 +++++++++++++++++++++++++++++++++++++++++ Zend/zend_execute.h | 213 ++++ Zend/zend_execute_API.c | 708 +++++++++++++ Zend/zend_hash.c | 1278 ++++++++++++++++++++++ Zend/zend_language_parser.y | 726 +++++++++++++ Zend/zend_modules.h | 85 ++ Zend/zend_operators.c | 1753 ++++++++++++++++++++++++++++++ 13 files changed, 8816 insertions(+) create mode 100644 TSRM/TSRM.h create mode 100644 TSRM/configure.in create mode 100644 Zend/acconfig.h create mode 100644 Zend/configure.in create mode 100644 Zend/zend_API.h create mode 100644 Zend/zend_builtin_functions.c create mode 100644 Zend/zend_compile.c create mode 100644 Zend/zend_execute.h create mode 100644 Zend/zend_execute_API.c create mode 100644 Zend/zend_hash.c create mode 100644 Zend/zend_language_parser.y create mode 100644 Zend/zend_modules.h create mode 100644 Zend/zend_operators.c diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h new file mode 100644 index 0000000000..fa789b6732 --- /dev/null +++ b/TSRM/TSRM.h @@ -0,0 +1,116 @@ +/* + +----------------------------------------------------------------------+ + | Thread Safe Resource Manager | + +----------------------------------------------------------------------+ + | Copyright (c) 1999, 2000, Andi Gutmans, Sascha Schumann, Zeev Suraski| + | This source file is subject to the TSRM license, that is bundled | + | with this package in the file LICENSE | + +----------------------------------------------------------------------+ + | Authors: Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +#ifndef TSRM_H +#define TSRM_H + +#ifdef HAVE_CONFIG_H +# include "tsrm_config.h" +#endif + +/* Only compile multi-threading functions if we're in ZTS mode */ +#ifdef ZTS + +#ifdef WIN32 +# define TSRM_WIN32 +#endif + +#ifdef TSRM_WIN32 +# include +#elif defined(GNUPTH) +# include +#elif defined(PTHREADS) +# include +#endif + +typedef int ts_rsrc_id; + +#ifdef TSRM_WIN32 +# ifdef TSRM_EXPORTS +# define TSRM_API __declspec(dllexport) +# else +# define TSRM_API __declspec(dllimport) +# endif +#else +# define TSRM_API +#endif + + +/* Define THREAD_T and MUTEX_T */ +#ifdef TSRM_WIN32 +# define THREAD_T DWORD +# define MUTEX_T CRITICAL_SECTION * +#elif defined(GNUPTH) +# define THREAD_T pth_t +# define MUTEX_T pth_mutex_t * +#elif defined(PTHREADS) +# define THREAD_T pthread_t +# define MUTEX_T pthread_mutex_t * +#elif defined(NSAPI) +# define THREAD_T SYS_THREAD +# define MUTEX_T CRITICAL +#elif defined(PI3WEB) +# define THREAD_T PIThread * +# define MUTEX_T PISync * +#endif + +typedef void (*ts_allocate_ctor)(void *); +typedef void (*ts_allocate_dtor)(void *); + +#define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts + +#ifdef __cplusplus +extern "C" { +#endif + +/* startup/shutdown */ +TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename); +TSRM_API void tsrm_shutdown(void); + +/* allocates a new thread-safe-resource id */ +TSRM_API ts_rsrc_id ts_allocate_id(size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); + +/* fetches the requested resource for the current thread */ +TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id); +#define ts_resource(id) ts_resource_ex(id, NULL) + +/* frees all resources allocated for the current thread */ +TSRM_API void ts_free_thread(void); + +/* deallocates all occurrences of a given id */ +TSRM_API void ts_free_id(ts_rsrc_id id); + + +/* Debug support */ +#define TSRM_ERROR_LEVEL_ERROR 1 +#define TSRM_ERROR_LEVEL_CORE 2 +#define TSRM_ERROR_LEVEL_INFO 3 +TSRM_API int tsrm_error(int level, const char *format, ...); +TSRM_API void tsrm_error_set(int level, char *debug_filename); + +/* utility functions */ +TSRM_API THREAD_T tsrm_thread_id(void); +TSRM_API MUTEX_T tsrm_mutex_alloc(void); +TSRM_API void tsrm_mutex_free(MUTEX_T mutexp); +TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp); +TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp); + +TSRM_API void *tsrm_set_new_thread_begin_handler(void (*new_thread_begin_handler)(THREAD_T thread_id)); +TSRM_API void *tsrm_set_new_thread_end_handler(void (*new_thread_end_handler)(THREAD_T thread_id)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTS */ + +#endif /* TSRM_H */ diff --git a/TSRM/configure.in b/TSRM/configure.in new file mode 100644 index 0000000000..3034268db8 --- /dev/null +++ b/TSRM/configure.in @@ -0,0 +1,33 @@ +dnl $Id$ +dnl +dnl Minimalistic configure.in for TSRM. +dnl + +AC_INIT(TSRM.c) +AM_INIT_AUTOMAKE(TSRM, 1.0, nodefine) +AM_CONFIG_HEADER(tsrm_config.h) + +sinclude(tsrm.m4) + +TSRM_BASIC_CHECKS +TSRM_THREADS_CHECKS + +AM_PROG_LIBTOOL +if test "$enable_debug" != "yes"; then + AM_SET_LIBTOOL_VARIABLE([--silent]) +fi + +dnl TSRM_PTHREAD + +AC_CHECK_HEADERS( +utime.h \ +dirent.h \ +stdarg.h \ +alloca.h \ +unistd.h \ +limits.h +) + +AC_DEFINE(ZTS,1,[ ]) + +AC_OUTPUT(Makefile) diff --git a/Zend/acconfig.h b/Zend/acconfig.h new file mode 100644 index 0000000000..d88fefb1b7 --- /dev/null +++ b/Zend/acconfig.h @@ -0,0 +1,68 @@ +#define ZEND_API +#define ZEND_DLEXPORT + +@TOP@ + +#undef uint +#undef ulong + +/* Define if you want to enable memory limit support */ +#define MEMORY_LIMIT 0 + +@BOTTOM@ + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#else +# include +#endif + +#if ZEND_BROKEN_SPRINTF +int zend_sprintf(char *buffer, const char *format, ...); +#else +# define zend_sprintf sprintf +#endif + +#include + +#ifdef HAVE_ISNAN +#define zend_isnan(a) isnan(a) +#elif defined(NAN) +#define zend_isnan(a) (((a)==NAN)?1:0) +#else +#define zend_isnan(a) 0 +#endif + +#ifdef HAVE_ISINF +#define zend_isinf(a) isinf(a) +#elif defined(INFINITY) +/* Might not work, but is required by ISO C99 */ +#define zend_isinf(a) (((a)==INFINITY)?1:0) +#else +#define zend_isinf(a) 0 +#endif + +#ifdef HAVE_FINITE +#define zend_finite(a) finite(a) +#elif defined(HAVE_ISFINITE) || defined(isfinite) +#define zend_finite(a) isfinite(a) +#elif defined(fpclassify) +#define zend_finite(a) ((fpclassify((a))!=FP_INFINITE&&fpclassify((a))!=FP_NAN)?1:0) +#else +#define zend_finite(a) (zend_isnan(a) ? 0 : zend_isinf(a) ? 0 : 1) +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/Zend/configure.in b/Zend/configure.in new file mode 100644 index 0000000000..ea2a24adf3 --- /dev/null +++ b/Zend/configure.in @@ -0,0 +1,45 @@ +dnl $Id$ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(zend.c) +AM_INIT_AUTOMAKE(zend, 0.80A, nodefine) +AM_CONFIG_HEADER(zend_config.h) +AM_SANITY_CHECK +AM_MAINTAINER_MODE +AC_PROG_CC +AM_PROG_LEX +AM_PROG_CC_STDC +ZEND_VERSION=$VERSION + +dnl We want this one before the checks, so the checks can modify CFLAGS. +test -z "$CFLAGS" && auto_cflags=1 + +sinclude(Zend.m4) + +LIBZEND_BASIC_CHECKS + +AM_PROG_LIBTOOL +if test "$enable_debug" != "yes"; then + AM_SET_LIBTOOL_VARIABLE([--silent]) +fi + +dnl +dnl Check for /usr/pkg/{lib,include} which is where NetBSD puts binary +dnl and source packages. This should be harmless on other OSs. +dnl +if test -d /usr/pkg/include -a -d /usr/pkg/lib ; then + CFLAGS="$CFLAGS -I/usr/pkg/include" + LDFLAGS="$LDFLAGS -L/usr/pkg/lib" +fi + +LIBZEND_ENABLE_DEBUG +LIBZEND_OTHER_CHECKS + +EXTRA_LIBS="$LIBS" +LIBS="" +AC_SUBST(EXTRA_LIBS) +AC_OUTPUT(Makefile) + +# Local Variables: +# tab-width: 4 +# End: diff --git a/Zend/zend_API.h b/Zend/zend_API.h new file mode 100644 index 0000000000..8125193fcd --- /dev/null +++ b/Zend/zend_API.h @@ -0,0 +1,426 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#ifndef ZEND_API_H +#define ZEND_API_H + +#include "modules.h" +#include "zend_list.h" +#include "zend_fast_cache.h" +#include "zend_operators.h" +#include "zend_variables.h" +#include "zend_execute.h" + +#define ZEND_FN(name) zend_if_##name +#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) +#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) + +#define ZEND_NAMED_FE(zend_name, name, arg_types) { #zend_name, name, arg_types }, +#define ZEND_FE(name, arg_types) ZEND_NAMED_FE(name, ZEND_FN(name), arg_types) +#define ZEND_FALIAS(name, alias, arg_types) ZEND_NAMED_FE(name, ZEND_FN(alias), arg_types) + +#define ZEND_MINIT(module) zend_minit_##module +#define ZEND_MSHUTDOWN(module) zend_mshutdown_##module +#define ZEND_RINIT(module) zend_rinit_##module +#define ZEND_RSHUTDOWN(module) zend_rshutdown_##module +#define ZEND_MINFO(module) zend_info_##module +#define ZEND_GINIT(module) zend_ginit_##module +#define ZEND_GSHUTDOWN(module) zend_gshutdown_##module + +#define ZEND_MINIT_FUNCTION(module) int ZEND_MINIT(module)(INIT_FUNC_ARGS) +#define ZEND_MSHUTDOWN_FUNCTION(module) int ZEND_MSHUTDOWN(module)(SHUTDOWN_FUNC_ARGS) +#define ZEND_RINIT_FUNCTION(module) int ZEND_RINIT(module)(INIT_FUNC_ARGS) +#define ZEND_RSHUTDOWN_FUNCTION(module) int ZEND_RSHUTDOWN(module)(SHUTDOWN_FUNC_ARGS) +#define ZEND_MINFO_FUNCTION(module) void ZEND_MINFO(module)(ZEND_MODULE_INFO_FUNC_ARGS) +#define ZEND_GINIT_FUNCTION(module) int ZEND_GINIT(module)(GINIT_FUNC_ARGS) +#define ZEND_GSHUTDOWN_FUNCTION(module) int ZEND_GSHUTDOWN(module)(void) + +#define ZEND_GET_MODULE(name) \ + ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; } + +#define ZEND_BEGIN_MODULE_GLOBALS(module_name) \ + typedef struct _zend_##module_name##_globals { +#define ZEND_END_MODULE_GLOBALS(module_name) \ + } zend_##module_name##_globals; + +#ifdef ZTS + +#define ZEND_DECLARE_MODULE_GLOBALS(module_name) \ + static ts_rsrc_id module_name##_globals_id; +#define ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor, globals_dtor) \ + module_name##_globals_id = ts_allocate_id(sizeof(zend_##module_name##_globals), (ts_allocate_ctor) globals_ctor, (ts_allocate_dtor) globals_dtor); + +#else + +#define ZEND_DECLARE_MODULE_GLOBALS(module_name) \ + static zend_##module_name##_globals module_name##_globals; +#define ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor, globals_dtor) \ + globals_ctor(&module_name##_globals); + +#endif + + + +#define INIT_CLASS_ENTRY(class_container, class_name, functions) \ + { \ + class_container.name = strdup(class_name); \ + class_container.name_length = sizeof(class_name)-1; \ + class_container.builtin_functions = functions; \ + class_container.handle_function_call = NULL; \ + class_container.handle_property_get = NULL; \ + class_container.handle_property_set = NULL; \ + } + +#define INIT_OVERLOADED_CLASS_ENTRY(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset) \ + { \ + class_container.name = strdup(class_name); \ + class_container.name_length = sizeof(class_name)-1; \ + class_container.builtin_functions = functions; \ + class_container.handle_function_call = handle_fcall; \ + class_container.handle_property_get = handle_propget; \ + class_container.handle_property_set = handle_propset; \ + } + + + +int zend_next_free_module(void); + +ZEND_API int zend_get_parameters(int ht, int param_count, ...); +ZEND_API int zend_get_parameters_array(int ht, int param_count, zval **argument_array); +ZEND_API int zend_get_parameters_ex(int param_count, ...); +ZEND_API int zend_get_parameters_array_ex(int param_count, zval ***argument_array); + +ZEND_API int ParameterPassedByReference(int ht, uint n); + +int zend_register_functions(zend_function_entry *functions, HashTable *function_table, int type); +void zend_unregister_functions(zend_function_entry *functions, int count, HashTable *function_table); +ZEND_API int zend_register_module(zend_module_entry *module_entry); + +ZEND_API zend_class_entry *zend_register_internal_class(zend_class_entry *class_entry); +ZEND_API zend_class_entry *zend_register_internal_class_ex(zend_class_entry *class_entry, zend_class_entry *parent_ce, char *parent_name); + +ZEND_API zend_module_entry *zend_get_module(int module_number); +ZEND_API int zend_disable_function(char *function_name, uint function_name_length); + +ZEND_API void wrong_param_count(void); + +#define getThis() (this_ptr) + +#define WRONG_PARAM_COUNT ZEND_WRONG_PARAM_COUNT() +#define WRONG_PARAM_COUNT_WITH_RETVAL(ret) ZEND_WRONG_PARAM_COUNT_WITH_RETVAL(ret) +#define ARG_COUNT(dummy) (ht) +#define ZEND_NUM_ARGS() (ht) +#define ZEND_WRONG_PARAM_COUNT() { wrong_param_count(); return; } +#define ZEND_WRONG_PARAM_COUNT_WITH_RETVAL(ret) { wrong_param_count(); return ret; } + +#ifndef ZEND_WIN32 +#define DLEXPORT +#endif + +ZEND_API int zend_startup_module(zend_module_entry *module); + +#define array_init(arg) _array_init((arg) ZEND_FILE_LINE_CC) +#define object_init(arg) _object_init((arg) ZEND_FILE_LINE_CC) +#define object_init_ex(arg, ce) _object_init_ex((arg), (ce) ZEND_FILE_LINE_CC) +ZEND_API int _array_init(zval *arg ZEND_FILE_LINE_DC); +ZEND_API int _object_init(zval *arg ZEND_FILE_LINE_DC); +ZEND_API int _object_init_ex(zval *arg, zend_class_entry *ce ZEND_FILE_LINE_DC); + +/* no longer supported */ +ZEND_API int add_assoc_function(zval *arg, char *key,void (*function_ptr)(INTERNAL_FUNCTION_PARAMETERS)); + +ZEND_API int add_assoc_long(zval *arg, char *key, long n); +ZEND_API int add_assoc_unset(zval *arg, char *key); +ZEND_API int add_assoc_bool(zval *arg, char *key, int b); +ZEND_API int add_assoc_resource(zval *arg, char *key, int r); +ZEND_API int add_assoc_double(zval *arg, char *key, double d); +ZEND_API int add_assoc_string(zval *arg, char *key, char *str, int duplicate); +ZEND_API int add_assoc_stringl(zval *arg, char *key, char *str, uint length, int duplicate); + +ZEND_API int add_index_long(zval *arg, uint idx, long n); +ZEND_API int add_index_unset(zval *arg, uint idx); +ZEND_API int add_index_bool(zval *arg, uint idx, int b); +ZEND_API int add_index_resource(zval *arg, uint idx, int r); +ZEND_API int add_index_double(zval *arg, uint idx, double d); +ZEND_API int add_index_string(zval *arg, uint idx, char *str, int duplicate); +ZEND_API int add_index_stringl(zval *arg, uint idx, char *str, uint length, int duplicate); + +ZEND_API int add_next_index_long(zval *arg, long n); +ZEND_API int add_next_index_unset(zval *arg); +ZEND_API int add_next_index_bool(zval *arg, int b); +ZEND_API int add_next_index_resource(zval *arg, int r); +ZEND_API int add_next_index_double(zval *arg, double d); +ZEND_API int add_next_index_string(zval *arg, char *str, int duplicate); +ZEND_API int add_next_index_stringl(zval *arg, char *str, uint length, int duplicate); + +ZEND_API int add_get_index_long(zval *arg, uint idx, long l, void **dest); +ZEND_API int add_get_index_double(zval *arg, uint idx, double d, void **dest); +ZEND_API int add_get_assoc_string(zval *arg, char *key, char *str, void **dest, int duplicate); +ZEND_API int add_get_assoc_stringl(zval *arg, char *key, char *str, uint length, void **dest, int duplicate); +ZEND_API int add_get_index_string(zval *arg, uint idx, char *str, void **dest, int duplicate); +ZEND_API int add_get_index_stringl(zval *arg, uint idx, char *str, uint length, void **dest, int duplicate); + +ZEND_API int call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, int param_count, zval *params[]); +ZEND_API int call_user_function_ex(HashTable *function_table, zval *object, zval *function_name, zval **retval_ptr_ptr, int param_count, zval **params[], int no_separation, HashTable *symbol_table); + +ZEND_API int add_property_long(zval *arg, char *key, long l); +ZEND_API int add_property_unset(zval *arg, char *key); +ZEND_API int add_property_bool(zval *arg, char *key, int b); +ZEND_API int add_property_resource(zval *arg, char *key, long r); +ZEND_API int add_property_double(zval *arg, char *key, double d); +ZEND_API int add_property_string(zval *arg, char *key, char *str, int duplicate); +ZEND_API int add_property_stringl(zval *arg, char *key, char *str, uint length, int duplicate); + +ZEND_API int zend_set_hash_symbol(zval *symbol, char *name, int name_length, + int is_ref, int num_symbol_tables, ...); + +#define add_method(arg,key,method) add_assoc_function((arg),(key),(method)) + +#define ZVAL_RESOURCE(z,l) { \ + (z)->type = IS_RESOURCE; \ + (z)->value.lval = l; \ + } +#define ZVAL_BOOL(z,b) { \ + (z)->type = IS_BOOL; \ + (z)->value.lval = b; \ + } +#define ZVAL_NULL(z) { \ + (z)->type = IS_NULL; \ + } +#define ZVAL_LONG(z,l) { \ + (z)->type = IS_LONG; \ + (z)->value.lval = l; \ + } +#define ZVAL_DOUBLE(z,d) { \ + (z)->type = IS_DOUBLE; \ + (z)->value.dval = d; \ + } +#define ZVAL_STRING(z,s,duplicate) { \ + char *__s=(s); \ + (z)->value.str.len = strlen(__s); \ + (z)->value.str.val = (duplicate?estrndup(__s,(z)->value.str.len):__s); \ + (z)->type = IS_STRING; \ + } +#define ZVAL_STRINGL(z,s,l,duplicate) { \ + char *__s=(s); int __l=l; \ + (z)->value.str.len = __l; \ + (z)->value.str.val = (duplicate?estrndup(__s,__l):__s); \ + (z)->type = IS_STRING; \ + } + +#define ZVAL_EMPTY_STRING(z) { \ + (z)->value.str.len = 0; \ + (z)->value.str.val = empty_string; \ + (z)->type = IS_STRING; \ + } + +#define ZVAL_FALSE { (z)->value.lval = 0; (z)->type = IS_BOOL; } +#define ZVAL_TRUE { (z)->value.lval = 1; (z)->type = IS_BOOL; } + +#define RETVAL_RESOURCE(l) { \ + return_value->type = IS_RESOURCE;\ + return_value->value.lval = l; \ + } +#define RETVAL_BOOL(b) { \ + return_value->type = IS_BOOL; \ + return_value->value.lval = b; \ + } +#define RETVAL_NULL() { \ + return_value->type = IS_NULL; \ + } +#define RETVAL_LONG(l) { \ + return_value->type = IS_LONG; \ + return_value->value.lval = l; \ + } +#define RETVAL_DOUBLE(d) { \ + return_value->type = IS_DOUBLE; \ + return_value->value.dval = d; \ + } +#define RETVAL_STRING(s,duplicate) { \ + char *__s=(s); \ + return_value->value.str.len = strlen(__s); \ + return_value->value.str.val = (duplicate?estrndup(__s,return_value->value.str.len):__s); \ + return_value->type = IS_STRING; \ + } +#define RETVAL_STRINGL(s,l,duplicate) { \ + char *__s=(s); int __l=l; \ + return_value->value.str.len = __l; \ + return_value->value.str.val = (duplicate?estrndup(__s,__l):__s); \ + return_value->type = IS_STRING; \ + } + +#define RETVAL_EMPTY_STRING() { \ + return_value->value.str.len = 0; \ + return_value->value.str.val = empty_string; \ + return_value->type = IS_STRING; \ + } + +#define RETVAL_FALSE { return_value->value.lval = 0; return_value->type = IS_BOOL; } +#define RETVAL_TRUE { return_value->value.lval = 1; return_value->type = IS_BOOL; } + +#define RETURN_RESOURCE(l) { \ + return_value->type = IS_RESOURCE;\ + return_value->value.lval = l; \ + return; \ + } + +#define RETURN_BOOL(b) { \ + return_value->type = IS_BOOL; \ + return_value->value.lval = b; \ + return; \ + } + +#define RETURN_NULL() { \ + return_value->type = IS_NULL; \ + return; \ + } + +#define RETURN_LONG(l) { \ + return_value->type = IS_LONG; \ + return_value->value.lval = l; \ + return; \ + } +#define RETURN_DOUBLE(d) { \ + return_value->type = IS_DOUBLE; \ + return_value->value.dval = d; \ + return; \ + } +#define RETURN_STRING(s,duplicate) { \ + char *__s=(s); \ + return_value->value.str.len = strlen(__s); \ + return_value->value.str.val = (duplicate?estrndup(__s,return_value->value.str.len):__s); \ + return_value->type = IS_STRING; \ + return; \ + } +#define RETURN_STRINGL(s,l,duplicate) { \ + char *__s=(s); int __l=l; \ + return_value->value.str.len = __l; \ + return_value->value.str.val = (duplicate?estrndup(__s,__l):__s); \ + return_value->type = IS_STRING; \ + return; \ + } + +#define RETURN_EMPTY_STRING() { \ + return_value->value.str.len = 0; \ + return_value->value.str.val = empty_string; \ + return_value->type = IS_STRING; \ + return; \ + } + +#define RETURN_FALSE { RETVAL_FALSE; return; } +#define RETURN_TRUE { RETVAL_TRUE; return; } + +#define SET_VAR_STRING(n,v) { \ + { \ + zval *var; \ + char *str=(v); /* prevent 'v' from being evaluated more than once */ \ + \ + ALLOC_ZVAL(var); \ + var->value.str.val = (str); \ + var->value.str.len = strlen((str)); \ + var->type = IS_STRING; \ + ZEND_SET_GLOBAL_VAR(n, var); \ + } \ + } + +#define SET_VAR_STRINGL(n,v,l) { \ + { \ + zval *var; \ + \ + ALLOC_ZVAL(var); \ + var->value.str.val = (v); \ + var->value.str.len = (l); \ + var->type = IS_STRING; \ + ZEND_SET_GLOBAL_VAR(n, var); \ + } \ + } + +#define SET_VAR_LONG(n,v) { \ + { \ + zval *var; \ + \ + ALLOC_ZVAL(var); \ + var->value.lval = (v); \ + var->type = IS_LONG; \ + ZEND_SET_GLOBAL_VAR(n, var); \ + } \ + } + +#define SET_VAR_DOUBLE(n,v) { \ + { \ + zval *var; \ + \ + ALLOC_ZVAL(var); \ + var->value.dval = (v); \ + var->type = IS_DOUBLE; \ + ZEND_SET_GLOBAL_VAR(n, var); \ + } \ + } + + +#define ZEND_SET_SYMBOL(symtable, name, var) \ + { \ + char *_name = (name); \ + \ + ZEND_SET_SYMBOL_WITH_LENGTH(symtable, _name, strlen(_name)+1, var, 1, 0); \ + } + +#define ZEND_SET_SYMBOL_WITH_LENGTH(symtable, name, name_length, var, _refcount, _is_ref) \ + { \ + zval **orig_var; \ + \ + if (zend_hash_find(symtable, (name), (name_length), (void **) &orig_var)==SUCCESS \ + && PZVAL_IS_REF(*orig_var)) { \ + (var)->refcount = (*orig_var)->refcount; \ + (var)->is_ref = 1; \ + \ + if (_refcount) { \ + (var)->refcount += _refcount-1; \ + } \ + zval_dtor(*orig_var); \ + **orig_var = *(var); \ + FREE_ZVAL(var); \ + } else { \ + (var)->is_ref = _is_ref; \ + if (_refcount) { \ + (var)->refcount = _refcount; \ + } \ + zend_hash_update(symtable, (name), (name_length), &(var), sizeof(zval *), NULL); \ + } \ + } + + +#define ZEND_SET_GLOBAL_VAR(name, var) \ + ZEND_SET_SYMBOL(&EG(symbol_table), name, var) + +#define ZEND_SET_GLOBAL_VAR_WITH_LENGTH(name, name_length, var, _refcount, _is_ref) \ + ZEND_SET_SYMBOL_WITH_LENGTH(&EG(symbol_table), name, name_length, var, _refcount, _is_ref) + +#define HASH_OF(p) ((p)->type==IS_ARRAY ? (p)->value.ht : (((p)->type==IS_OBJECT ? (p)->value.obj.properties : NULL))) +#define ZVAL_IS_NULL(z) ((z)->type==IS_NULL) + +#endif /* ZEND_API_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c new file mode 100644 index 0000000000..abb9d19a88 --- /dev/null +++ b/Zend/zend_builtin_functions.c @@ -0,0 +1,1013 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#include "zend.h" +#include "zend_API.h" +#include "zend_builtin_functions.h" +#include "zend_constants.h" + +#undef ZEND_TEST_EXCEPTIONS + +static ZEND_FUNCTION(zend_version); +static ZEND_FUNCTION(func_num_args); +static ZEND_FUNCTION(func_get_arg); +static ZEND_FUNCTION(func_get_args); +static ZEND_NAMED_FUNCTION(zend_if_strlen); +static ZEND_FUNCTION(strcmp); +static ZEND_FUNCTION(strncmp); +static ZEND_FUNCTION(strcasecmp); +static ZEND_FUNCTION(strncasecmp); +static ZEND_FUNCTION(each); +static ZEND_FUNCTION(error_reporting); +static ZEND_FUNCTION(define); +static ZEND_FUNCTION(defined); +static ZEND_FUNCTION(get_class); +static ZEND_FUNCTION(get_parent_class); +static ZEND_FUNCTION(method_exists); +static ZEND_FUNCTION(class_exists); +static ZEND_FUNCTION(function_exists); +static ZEND_FUNCTION(leak); +#ifdef ZEND_TEST_EXCEPTIONS +static ZEND_FUNCTION(crash); +#endif +static ZEND_FUNCTION(get_included_files); +static ZEND_FUNCTION(is_subclass_of); +static ZEND_FUNCTION(get_class_vars); +static ZEND_FUNCTION(get_object_vars); +static ZEND_FUNCTION(get_class_methods); +static ZEND_FUNCTION(trigger_error); +static ZEND_FUNCTION(set_error_handler); +static ZEND_FUNCTION(restore_error_handler); +static ZEND_FUNCTION(get_declared_classes); +static ZEND_FUNCTION(get_defined_functions); +static ZEND_FUNCTION(get_defined_vars); +static ZEND_FUNCTION(create_function); +static ZEND_FUNCTION(get_resource_type); +#if ZEND_DEBUG +static ZEND_FUNCTION(zend_test_func); +#endif + +unsigned char first_arg_force_ref[] = { 1, BYREF_FORCE }; +unsigned char first_arg_allow_ref[] = { 1, BYREF_ALLOW }; +unsigned char second_arg_force_ref[] = { 2, BYREF_NONE, BYREF_FORCE }; +unsigned char second_arg_allow_ref[] = { 2, BYREF_NONE, BYREF_ALLOW }; + +static zend_function_entry builtin_functions[] = { + ZEND_FE(zend_version, NULL) + ZEND_FE(func_num_args, NULL) + ZEND_FE(func_get_arg, NULL) + ZEND_FE(func_get_args, NULL) + { "strlen", zend_if_strlen, NULL }, + ZEND_FE(strcmp, NULL) + ZEND_FE(strncmp, NULL) + ZEND_FE(strcasecmp, NULL) + ZEND_FE(strncasecmp, NULL) + ZEND_FE(each, first_arg_force_ref) + ZEND_FE(error_reporting, NULL) + ZEND_FE(define, NULL) + ZEND_FE(defined, NULL) + ZEND_FE(get_class, NULL) + ZEND_FE(get_parent_class, NULL) + ZEND_FE(method_exists, NULL) + ZEND_FE(class_exists, NULL) + ZEND_FE(function_exists, NULL) + ZEND_FE(leak, NULL) +#ifdef ZEND_TEST_EXCEPTIONS + ZEND_FE(crash, NULL) +#endif + ZEND_FE(get_included_files, NULL) + ZEND_FALIAS(get_required_files, get_included_files, NULL) + ZEND_FE(is_subclass_of, NULL) + ZEND_FE(get_class_vars, NULL) + ZEND_FE(get_object_vars, NULL) + ZEND_FE(get_class_methods, NULL) + ZEND_FE(trigger_error, NULL) + ZEND_FALIAS(user_error, trigger_error, NULL) + ZEND_FE(set_error_handler, NULL) + ZEND_FE(restore_error_handler, NULL) + ZEND_FE(get_declared_classes, NULL) + ZEND_FE(get_defined_functions, NULL) + ZEND_FE(get_defined_vars, NULL) + ZEND_FE(create_function, NULL) + ZEND_FE(get_resource_type, NULL) +#if ZEND_DEBUG + ZEND_FE(zend_test_func, NULL) +#endif + { NULL, NULL, NULL } +}; + + +int zend_startup_builtin_functions() +{ + return zend_register_functions(builtin_functions, NULL, MODULE_PERSISTENT); +} + + +ZEND_FUNCTION(zend_version) +{ + RETURN_STRINGL(ZEND_VERSION, sizeof(ZEND_VERSION)-1, 1); +} + + +ZEND_FUNCTION(func_num_args) +{ + void **p; + int arg_count; + + p = EG(argument_stack).top_element-1-1; + arg_count = (ulong) *p; /* this is the amount of arguments passed to func_num_args(); */ + p -= 1+arg_count; + if (*p) { + zend_error(E_ERROR, "func_num_args(): Can't be used as a function parameter"); + } + --p; + if (p>=EG(argument_stack).elements) { + RETURN_LONG((ulong) *p); + } else { + zend_error(E_WARNING, "func_num_args(): Called from the global scope - no function context"); + RETURN_LONG(-1); + } +} + + +ZEND_FUNCTION(func_get_arg) +{ + void **p; + int arg_count; + zval **z_requested_offset; + zval *arg; + long requested_offset; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &z_requested_offset)==FAILURE) { + RETURN_FALSE; + } + convert_to_long_ex(z_requested_offset); + requested_offset = (*z_requested_offset)->value.lval; + + p = EG(argument_stack).top_element-1-1; + arg_count = (ulong) *p; /* this is the amount of arguments passed to func_get_arg(); */ + p -= 1+arg_count; + if (*p) { + zend_error(E_ERROR, "func_get_arg(): Can't be used as a function parameter"); + } + --p; + if (p=arg_count) { + zend_error(E_WARNING, "func_get_arg(): Argument %d not passed to function", requested_offset); + RETURN_FALSE; + } + + arg = *(p-(arg_count-requested_offset)); + *return_value = *arg; + zval_copy_ctor(return_value); +} + + +ZEND_FUNCTION(func_get_args) +{ + void **p; + int arg_count; + int i; + + p = EG(argument_stack).top_element-1-1; + arg_count = (ulong) *p; /* this is the amount of arguments passed to func_get_args(); */ + p -= 1+arg_count; + if (*p) { + zend_error(E_ERROR, "func_get_args(): Can't be used as a function parameter"); + } + --p; + + if (pvalue.ht, &element, sizeof(zval *), NULL); + } +} + + +/* {{{ proto int strlen(string str) + Get string length */ +ZEND_NAMED_FUNCTION(zend_if_strlen) +{ + zval **str; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &str) == FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_string_ex(str); + RETVAL_LONG((*str)->value.str.len); +} +/* }}} */ + +/* {{{ proto int strcmp(string str1, string str2) + Binary safe string comparison */ +ZEND_FUNCTION(strcmp) +{ + zval **s1, **s2; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &s1, &s2) == FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_string_ex(s1); + convert_to_string_ex(s2); + RETURN_LONG(zend_binary_zval_strcmp(*s1,*s2)); +} +/* }}} */ + +/* {{{ proto int strncmp(string str1, string str2, int len) + Binary safe string comparison */ +ZEND_FUNCTION(strncmp) +{ + zval **s1, **s2, **s3; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &s1, &s2, &s3) == FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_string_ex(s1); + convert_to_string_ex(s2); + convert_to_long_ex(s3); + RETURN_LONG(zend_binary_zval_strncmp(*s1,*s2,*s3)); +} +/* }}} */ + +/* {{{ proto int strcasecmp(string str1, string str2) + Binary safe case-insensitive string comparison */ +ZEND_FUNCTION(strcasecmp) +{ + zval **s1, **s2; + + if (ZEND_NUM_ARGS()!=2 || zend_get_parameters_ex(2, &s1, &s2) == FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_string_ex(s1); + convert_to_string_ex(s2); + RETURN_LONG(zend_binary_zval_strcasecmp(*s1, *s2)); +} +/* }}} */ + +/* {{{ proto int strncasecmp(string str1, string str2, int len) + Binary safe string comparison */ +ZEND_FUNCTION(strncasecmp) +{ + zval **s1, **s2, **s3; + + if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &s1, &s2, &s3) == FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_string_ex(s1); + convert_to_string_ex(s2); + convert_to_long_ex(s3); + RETURN_LONG(zend_binary_zval_strncasecmp(*s1,*s2,*s3)); +} +/* }}} */ + +ZEND_FUNCTION(each) +{ + zval **array,*entry,**entry_ptr, *tmp; + char *string_key; + ulong num_key; + zval **inserted_pointer; + HashTable *target_hash; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &array) == FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + target_hash = HASH_OF(*array); + if (!target_hash) { + zend_error(E_WARNING,"Variable passed to each() is not an array or object"); + return; + } + if (zend_hash_get_current_data(target_hash, (void **) &entry_ptr)==FAILURE) { + RETURN_FALSE; + } + array_init(return_value); + entry = *entry_ptr; + + /* add value elements */ + if (entry->is_ref) { + ALLOC_ZVAL(tmp); + *tmp = *entry; + zval_copy_ctor(tmp); + tmp->is_ref=0; + tmp->refcount=0; + entry=tmp; + } + zend_hash_index_update(return_value->value.ht, 1, &entry, sizeof(zval *), NULL); + entry->refcount++; + zend_hash_update(return_value->value.ht, "value", sizeof("value"), &entry, sizeof(zval *), NULL); + entry->refcount++; + + /* add the key elements */ + switch (zend_hash_get_current_key(target_hash, &string_key, &num_key)) { + case HASH_KEY_IS_STRING: + add_get_index_string(return_value,0,string_key,(void **) &inserted_pointer,0); + break; + case HASH_KEY_IS_LONG: + add_get_index_long(return_value,0,num_key, (void **) &inserted_pointer); + break; + } + zend_hash_update(return_value->value.ht, "key", sizeof("key"), inserted_pointer, sizeof(zval *), NULL); + (*inserted_pointer)->refcount++; + zend_hash_move_forward(target_hash); +} + +ZEND_FUNCTION(error_reporting) +{ + zval **arg; + int old_error_reporting; + + old_error_reporting = EG(error_reporting); + switch (ZEND_NUM_ARGS()) { + case 0: + break; + case 1: + if (zend_get_parameters_ex(1,&arg) == FAILURE) { + RETURN_FALSE; + } + convert_to_long_ex(arg); + EG(error_reporting)=(*arg)->value.lval; + break; + default: + ZEND_WRONG_PARAM_COUNT(); + break; + } + + RETVAL_LONG(old_error_reporting); +} + +ZEND_FUNCTION(define) +{ + zval **var, **val, **non_cs; + int case_sensitive; + zend_constant c; + + switch(ZEND_NUM_ARGS()) { + case 2: + if (zend_get_parameters_ex(2, &var, &val)==FAILURE) { + RETURN_FALSE; + } + case_sensitive = CONST_CS; + break; + case 3: + if (zend_get_parameters_ex(3, &var, &val, &non_cs)==FAILURE) { + RETURN_FALSE; + } + convert_to_long_ex(non_cs); + if ((*non_cs)->value.lval) { + case_sensitive = 0; + } else { + case_sensitive = CONST_CS; + } + break; + default: + ZEND_WRONG_PARAM_COUNT(); + break; + } + + switch((*val)->type) { + case IS_LONG: + case IS_DOUBLE: + case IS_STRING: + case IS_BOOL: + case IS_RESOURCE: + case IS_NULL: + break; + default: + zend_error(E_WARNING,"Constants may only evaluate to scalar values"); + RETURN_FALSE; + break; + } + convert_to_string_ex(var); + + c.value = **val; + zval_copy_ctor(&c.value); + c.flags = case_sensitive; /* non persistent */ + c.name = zend_strndup((*var)->value.str.val, (*var)->value.str.len); + c.name_len = (*var)->value.str.len+1; + if(zend_register_constant(&c ELS_CC) == SUCCESS) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} + + +ZEND_FUNCTION(defined) +{ + zval **var; + zval c; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &var)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + + convert_to_string_ex(var); + if (zend_get_constant((*var)->value.str.val, (*var)->value.str.len, &c)) { + zval_dtor(&c); + RETURN_LONG(1); + } else { + RETURN_LONG(0); + } +} + +/* {{{ proto string get_class(object object) + Retrieves the class name */ +ZEND_FUNCTION(get_class) +{ + zval **arg; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &arg)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + if ((*arg)->type != IS_OBJECT) { + RETURN_FALSE; + } + RETURN_STRINGL((*arg)->value.obj.ce->name, (*arg)->value.obj.ce->name_length, 1); +} +/* }}} */ + +/* {{{ proto string get_parent_class(object object) + Retrieves the parent class name */ +ZEND_FUNCTION(get_parent_class) +{ + zval **arg; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &arg)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + if (((*arg)->type != IS_OBJECT) || !(*arg)->value.obj.ce->parent) { + RETURN_FALSE; + } + RETURN_STRINGL((*arg)->value.obj.ce->parent->name, (*arg)->value.obj.ce->parent->name_length, 1); +} +/* }}} */ + +/* {{{ proto bool is_subclass_of(object object, string class_name) + Returns true if the object has this class as one of its parents */ +ZEND_FUNCTION(is_subclass_of) +{ + zval **obj, **class_name; + char *lcname; + zend_class_entry *parent_ce = NULL; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &obj, &class_name)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + + if ((*obj)->type != IS_OBJECT) { + RETURN_FALSE; + } + + convert_to_string_ex(class_name); + lcname = estrndup((*class_name)->value.str.val, (*class_name)->value.str.len); + zend_str_tolower(lcname, (*class_name)->value.str.len); + + for (parent_ce = (*obj)->value.obj.ce->parent; parent_ce != NULL; parent_ce = parent_ce->parent) { + if (!strcmp(parent_ce->name, lcname)) { + efree(lcname); + RETURN_TRUE; + } + } + efree(lcname); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto array get_class_vars(string class_name) + Returns an array of default properties of the class */ +ZEND_FUNCTION(get_class_vars) +{ + zval **class_name; + char *lcname; + zend_class_entry *ce; + zval *tmp; + CLS_FETCH(); + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &class_name)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + + convert_to_string_ex(class_name); + lcname = estrndup((*class_name)->value.str.val, (*class_name)->value.str.len); + zend_str_tolower(lcname, (*class_name)->value.str.len); + + if (zend_hash_find(CG(class_table), lcname, (*class_name)->value.str.len+1, (void **)&ce)==FAILURE) { + efree(lcname); + RETURN_FALSE; + } else { + efree(lcname); + array_init(return_value); + if (!ce->constants_updated) { + zend_hash_apply_with_argument(&ce->default_properties, (int (*)(void *,void *)) zval_update_constant, (void *) 1); + ce->constants_updated = 1; + } + zend_hash_copy(return_value->value.ht, &ce->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + } +} +/* }}} */ + +/* {{{ proto array get_object_vars(object obj) + Returns an array of object properties */ +ZEND_FUNCTION(get_object_vars) +{ + zval **obj; + zval *tmp; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &obj) == FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + + if ((*obj)->type != IS_OBJECT) { + RETURN_FALSE; + } + + array_init(return_value); + zend_hash_copy(return_value->value.ht, (*obj)->value.obj.properties, + (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); +} +/* }}} */ + +/* {{{ proto array get_class_methods(string class_name) + Returns an array of class methods' names */ +ZEND_FUNCTION(get_class_methods) +{ + zval **class_name; + zval *method_name; + char *lcname; + zend_class_entry *ce; + char *string_key; + ulong num_key; + int key_type; + CLS_FETCH(); + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &class_name)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + + convert_to_string_ex(class_name); + lcname = estrndup((*class_name)->value.str.val, (*class_name)->value.str.len); + zend_str_tolower(lcname, (*class_name)->value.str.len); + + if (zend_hash_find(CG(class_table), lcname, (*class_name)->value.str.len+1, (void **)&ce)==FAILURE) { + efree(lcname); + RETURN_NULL(); + } else { + efree(lcname); + array_init(return_value); + zend_hash_internal_pointer_reset(&ce->function_table); + while ((key_type = zend_hash_get_current_key(&ce->function_table, &string_key, &num_key)) != HASH_KEY_NON_EXISTANT) { + if (key_type == HASH_KEY_IS_STRING) { + MAKE_STD_ZVAL(method_name); + ZVAL_STRING(method_name, string_key, 0); + zend_hash_next_index_insert(return_value->value.ht, &method_name, sizeof(zval *), NULL); + } + zend_hash_move_forward(&ce->function_table); + } + } +} +/* }}} */ + +/* {{{ proto bool method_exists(object object, string method) + Checks if the class method exists */ +ZEND_FUNCTION(method_exists) +{ + zval **klass, **method_name; + char *lcname; + + if (ZEND_NUM_ARGS()!=2 || zend_get_parameters_ex(2, &klass, &method_name)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + if ((*klass)->type != IS_OBJECT) { + RETURN_FALSE; + } + convert_to_string_ex(method_name); + lcname = estrndup((*method_name)->value.str.val, (*method_name)->value.str.len); + zend_str_tolower(lcname, (*method_name)->value.str.len); + if(zend_hash_exists(&(*klass)->value.obj.ce->function_table, lcname, (*method_name)->value.str.len+1)) { + efree(lcname); + RETURN_TRUE; + } else { + efree(lcname); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool class_exists(string classname) + Checks if the class exists */ +ZEND_FUNCTION(class_exists) +{ + zval **class_name; + char *lcname; + CLS_FETCH(); + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &class_name)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_string_ex(class_name); + lcname = estrndup((*class_name)->value.str.val, (*class_name)->value.str.len); + zend_str_tolower(lcname, (*class_name)->value.str.len); + if (zend_hash_exists(CG(class_table), lcname, (*class_name)->value.str.len+1)) { + efree(lcname); + RETURN_TRUE; + } else { + efree(lcname); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool function_exists(string function_name) + Checks if the function exists */ +ZEND_FUNCTION(function_exists) +{ + zval **function_name; + char *lcname; + int retval; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &function_name)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_string_ex(function_name); + lcname = estrndup((*function_name)->value.str.val, (*function_name)->value.str.len); + zend_str_tolower(lcname, (*function_name)->value.str.len); + + retval = zend_hash_exists(EG(function_table), lcname, (*function_name)->value.str.len+1); + efree(lcname); + + RETURN_BOOL(retval); +} +/* }}} */ + + +ZEND_FUNCTION(leak) +{ + int leakbytes=3; + zval **leak; + + if (ZEND_NUM_ARGS()>=1) { + if (zend_get_parameters_ex(1, &leak)==SUCCESS) { + convert_to_long_ex(leak); + leakbytes = (*leak)->value.lval; + } + } + + emalloc(leakbytes); +} + + +#ifdef ZEND_TEST_EXCEPTIONS +ZEND_FUNCTION(crash) +{ + char *nowhere=NULL; + + memcpy(nowhere, "something", sizeof("something")); +} +#endif + + +static int copy_import_use_file(zend_file_handle *fh, zval *array) +{ + if (fh->filename) { + char *extension_start; + + extension_start = strstr(fh->filename, zend_uv.import_use_extension); + if (extension_start) { + *extension_start = 0; + if (fh->opened_path) { + add_assoc_string(array, fh->filename, fh->opened_path, 1); + } else { + add_assoc_stringl(array, fh->filename, "N/A", sizeof("N/A")-1, 1); + } + *extension_start = zend_uv.import_use_extension[0]; + } + } + return 0; +} + + +/* {{{ proto array get_included_files(void) + Returns an array with the file names that were include_once()'d */ +ZEND_FUNCTION(get_included_files) +{ + char *entry; + if (ZEND_NUM_ARGS() != 0) { + ZEND_WRONG_PARAM_COUNT(); + } + + array_init(return_value); + zend_hash_internal_pointer_reset(&EG(included_files)); + while(zend_hash_get_current_key(&EG(included_files), &entry,NULL) == HASH_KEY_IS_STRING) { + add_next_index_string(return_value,entry,0); + zend_hash_move_forward(&EG(included_files)); + } + + /* zend_hash_apply_with_argument(&EG(included_files), (apply_func_arg_t) copy_import_use_file, return_value); */ +} +/* }}} */ + + +/* {{{ proto void trigger_error(string messsage [, int error_type]) + Generates a user-level error/warning/notice message */ +ZEND_FUNCTION(trigger_error) +{ + int error_type = E_USER_NOTICE; + zval **z_error_type, **z_error_message; + + switch(ZEND_NUM_ARGS()) { + case 1: + if (zend_get_parameters_ex(1, &z_error_message)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + break; + case 2: + if (zend_get_parameters_ex(2, &z_error_message, &z_error_type)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_long_ex(z_error_type); + error_type = (*z_error_type)->value.lval; + switch (error_type) { + case E_USER_ERROR: + case E_USER_WARNING: + case E_USER_NOTICE: + break; + default: + zend_error(E_WARNING, "Invalid error type specified"); + RETURN_FALSE; + break; + } + break; + default: + ZEND_WRONG_PARAM_COUNT(); + } + convert_to_string_ex(z_error_message); + zend_error(error_type, "%s", (*z_error_message)->value.str.val); + RETURN_TRUE; +} +/* }}} */ + + +/* {{{ proto string set_error_handler(string error_handler) + Sets a user-defined error handler function. Returns the previously defined error handler, or false on error */ +ZEND_FUNCTION(set_error_handler) +{ + zval **error_handler; + zend_bool had_orig_error_handler=0; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &error_handler)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + + convert_to_string_ex(error_handler); + if (EG(user_error_handler)) { + had_orig_error_handler = 1; + *return_value = *EG(user_error_handler); + zval_copy_ctor(return_value); + zend_ptr_stack_push(&EG(user_error_handlers), EG(user_error_handler)); + } + ALLOC_ZVAL(EG(user_error_handler)); + + if (Z_STRLEN_PP(error_handler)==0) { /* unset user-defined handler */ + FREE_ZVAL(EG(user_error_handler)); + EG(user_error_handler) = NULL; + RETURN_TRUE; + } + + *EG(user_error_handler) = **error_handler; + zval_copy_ctor(EG(user_error_handler)); + + if (!had_orig_error_handler) { + RETURN_NULL(); + } +} +/* }}} */ + + + +/* {{{ proto void restore_error_handler(void) + Restores the previously defined error handler function */ +ZEND_FUNCTION(restore_error_handler) +{ + if (EG(user_error_handler)) { + zval_ptr_dtor(&EG(user_error_handler)); + } + if (zend_ptr_stack_num_elements(&EG(user_error_handlers))==0) { + EG(user_error_handler) = NULL; + } else { + EG(user_error_handler) = zend_ptr_stack_pop(&EG(user_error_handlers)); + } + RETURN_TRUE; +} + + +static int copy_class_name(zend_class_entry *ce, int num_args, va_list args, zend_hash_key *hash_key) +{ + zval *array = va_arg(args, zval *); + + if (hash_key->nKeyLength==0 || hash_key->arKey[0]!=0) { + add_next_index_stringl(array, ce->name, ce->name_length, 1); + } + return 0; +} + +/* {{{ proto array get_declared_classes(void) + Returns an array of all declared classes. */ +ZEND_FUNCTION(get_declared_classes) +{ + CLS_FETCH(); + + if (ZEND_NUM_ARGS() != 0) { + ZEND_WRONG_PARAM_COUNT(); + } + + array_init(return_value); + zend_hash_apply_with_arguments(CG(class_table), (apply_func_args_t)copy_class_name, 1, return_value); +} +/* }}} */ + +static int copy_function_name(zend_function *func, int num_args, va_list args, zend_hash_key *hash_key) +{ + zval *internal_ar = va_arg(args, zval *), + *user_ar = va_arg(args, zval *); + + if (hash_key->nKeyLength == 0 || hash_key->arKey[0] == 0) { + return 0; + } + + if (func->type == ZEND_INTERNAL_FUNCTION) { + add_next_index_stringl(internal_ar, hash_key->arKey, hash_key->nKeyLength, 1); + } else if (func->type == ZEND_USER_FUNCTION) { + add_next_index_stringl(user_ar, hash_key->arKey, hash_key->nKeyLength, 1); + } + + return 0; +} + +/* {{{ proto array get_defined_functions(void) + Returns an array of all defined functions */ +ZEND_FUNCTION(get_defined_functions) +{ + zval *internal; + zval *user; + + if (ZEND_NUM_ARGS() != 0) { + ZEND_WRONG_PARAM_COUNT(); + } + + MAKE_STD_ZVAL(internal); + MAKE_STD_ZVAL(user); + + array_init(internal); + array_init(user); + array_init(return_value); + + zend_hash_apply_with_arguments(EG(function_table), (apply_func_args_t)copy_function_name, 2, internal, user); + + if (zend_hash_add(return_value->value.ht, "internal", sizeof("internal"), (void **)&internal, sizeof(zval *), NULL) == FAILURE) { + zend_error(E_WARNING, "Cannot add internal functions to return value from get_defined_functions()"); + RETURN_FALSE; + } + + if (zend_hash_add(return_value->value.ht, "user", sizeof("user"), (void **)&user, sizeof(zval *), NULL) == FAILURE) { + zend_error(E_WARNING, "Cannot add user functions to return value from get_defined_functions()"); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto array get_defined_vars(void) + Returns an associative array of names and values of all currently defined variable names (variables in the current scope) */ +ZEND_FUNCTION(get_defined_vars) +{ + zval *tmp; + + array_init(return_value); + + zend_hash_copy(return_value->value.ht, EG(active_symbol_table), + (copy_ctor_func_t)zval_add_ref, &tmp, sizeof(zval *)); +} +/* }}} */ + +#define LAMBDA_TEMP_FUNCNAME "__lambda_func" + +/* {{{ proto string create_function(string args, string code) + Creates an anonymous function, and returns its name (funny, eh?) */ +ZEND_FUNCTION(create_function) +{ + char *eval_code, *function_name; + int eval_code_length, function_name_length; + zval **z_function_args, **z_function_code; + int retval; + char *eval_name; + CLS_FETCH(); + + if (ZEND_NUM_ARGS()!=2 || zend_get_parameters_ex(2, &z_function_args, &z_function_code)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + + convert_to_string_ex(z_function_args); + convert_to_string_ex(z_function_code); + + eval_code_length = sizeof("function " LAMBDA_TEMP_FUNCNAME) + +Z_STRLEN_PP(z_function_args) + +2 /* for the args parentheses */ + +2 /* for the curly braces */ + +Z_STRLEN_PP(z_function_code); + + eval_code = (char *) emalloc(eval_code_length); + sprintf(eval_code, "function " LAMBDA_TEMP_FUNCNAME "(%s){%s}", Z_STRVAL_PP(z_function_args), Z_STRVAL_PP(z_function_code)); + + eval_name = zend_make_compiled_string_description("runtime-created function"); + retval = zend_eval_string(eval_code, NULL, eval_name CLS_CC ELS_CC); + efree(eval_code); + efree(eval_name); + + if (retval==SUCCESS) { + zend_function *func; + + if (zend_hash_find(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME), (void **) &func)==FAILURE) { + zend_error(E_ERROR, "Unexpected inconsistency in create_function()"); + RETURN_FALSE; + } + function_add_ref(func); + + function_name = (char *) emalloc(sizeof("0lambda_")+MAX_LENGTH_OF_LONG); + + do { + sprintf(function_name, "%clambda_%d", 0, ++EG(lambda_count)); + function_name_length = strlen(function_name+1)+1; + } while (zend_hash_add(EG(function_table), function_name, function_name_length+1, func, sizeof(zend_function), NULL)==FAILURE); + zend_hash_del(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME)); + RETURN_STRINGL(function_name, function_name_length, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + + +#if ZEND_DEBUG +ZEND_FUNCTION(zend_test_func) +{ + zval *arg1, *arg2; + + zend_get_parameters(ht, 2, &arg1, &arg2); +} +#endif + + +ZEND_FUNCTION(get_resource_type) +{ + char *resource_type; + zval **z_resource_type; + + if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &z_resource_type)==FAILURE) { + ZEND_WRONG_PARAM_COUNT(); + } + + if (Z_TYPE_PP(z_resource_type) != IS_RESOURCE) { + zend_error(E_WARNING, "Supplied argument is not a valid resource handle"); + RETURN_FALSE; + } + + resource_type = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(z_resource_type)); + if (resource_type) { + RETURN_STRING(resource_type, 1); + } else { + RETURN_STRING("Unknown", 1); + } +} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c new file mode 100644 index 0000000000..fc138a5ed4 --- /dev/null +++ b/Zend/zend_compile.c @@ -0,0 +1,2352 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#include "zend_language_parser.h" +#include "zend.h" +#include "zend_compile.h" +#include "zend_llist.h" +#include "zend_API.h" +#include "zend_fast_cache.h" + + +ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type CLS_DC); + + +#ifndef ZTS +ZEND_API zend_compiler_globals compiler_globals; +ZEND_API zend_executor_globals executor_globals; +#endif + +static void free_filename(void *p) +{ + efree(*((char **) p)); +} + + +static void build_runtime_defined_function_key(zval *result, zval *name, zend_op *opline CLS_DC) +{ + char lineno_buf[32]; + uint lineno_len; + char *filename; + + lineno_len = zend_sprintf(lineno_buf, "%d", opline->lineno); + if (CG(active_op_array)->filename) { + filename = CG(active_op_array)->filename; + } else { + filename = "-"; + } + + /* NULL, name length, filename length, line number length */ + result->value.str.len = 1+name->value.str.len+strlen(filename)+lineno_len+1; + result->value.str.val = (char *) emalloc(result->value.str.len); /* Extra null byte is calculated a line before */ + sprintf(result->value.str.val, "%c%s%s%s", '\0', name->value.str.val, filename, lineno_buf); + result->type = IS_STRING; + result->refcount = 1; +} + + +static void init_compiler_declarables(CLS_D ELS_DC) +{ + CG(declarables).ticks.type = IS_LONG; + CG(declarables).ticks.value.lval = 0; +} + + +void init_compiler(CLS_D ELS_DC) +{ + zend_stack_init(&CG(bp_stack)); + zend_stack_init(&CG(function_call_stack)); + zend_stack_init(&CG(switch_cond_stack)); + zend_stack_init(&CG(foreach_copy_stack)); + zend_stack_init(&CG(object_stack)); + zend_stack_init(&CG(declare_stack)); + CG(active_class_entry) = NULL; + zend_llist_init(&CG(list_llist), sizeof(list_llist_element), NULL, 0); + zend_llist_init(&CG(dimension_llist), sizeof(int), NULL, 0); + zend_hash_init(&CG(filenames_table), 5, NULL, (dtor_func_t) free_filename, 0); + CG(handle_op_arrays) = 1; + CG(in_compilation) = 0; + zend_init_rsrc_list(ELS_C); + CG(unclean_shutdown) = 0; + zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) zend_file_handle_dtor, 0); + init_compiler_declarables(CLS_C ELS_CC); +} + + +void shutdown_compiler(CLS_D) +{ + zend_stack_destroy(&CG(bp_stack)); + zend_stack_destroy(&CG(function_call_stack)); + zend_stack_destroy(&CG(switch_cond_stack)); + zend_stack_destroy(&CG(foreach_copy_stack)); + zend_stack_destroy(&CG(object_stack)); + zend_stack_destroy(&CG(declare_stack)); + zend_hash_destroy(&CG(filenames_table)); + zend_llist_destroy(&CG(open_files)); +} + + +ZEND_API char *zend_set_compiled_filename(char *new_compiled_filename) +{ + char **pp, *p; + int length = strlen(new_compiled_filename); + CLS_FETCH(); + + if (zend_hash_find(&CG(filenames_table), new_compiled_filename, length+1, (void **) &pp)==SUCCESS) { + CG(compiled_filename) = *pp; + return *pp; + } + p = estrndup(new_compiled_filename, length); + zend_hash_update(&CG(filenames_table), new_compiled_filename, length+1, &p, sizeof(char *), (void **) &pp); + CG(compiled_filename) = p; + return p; +} + + +ZEND_API void zend_restore_compiled_filename(char *original_compiled_filename) +{ + CLS_FETCH(); + + CG(compiled_filename) = original_compiled_filename; +} + + +ZEND_API char *zend_get_compiled_filename(CLS_D) +{ + return CG(compiled_filename); +} + + +ZEND_API int zend_get_compiled_lineno(CLS_D) +{ + return CG(zend_lineno); +} + + +ZEND_API zend_bool zend_is_compiling() +{ + CLS_FETCH(); + + return CG(in_compilation); +} + + +static zend_uint get_temporary_variable(zend_op_array *op_array) +{ + return (op_array->T)++; +} + + +void zend_do_binary_op(int op, znode *result, znode *op1, znode *op2 CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = op; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *op1; + opline->op2 = *op2; + *result = opline->result; +} + + +void zend_do_unary_op(int op, znode *result, znode *op1 CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = op; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *op1; + *result = opline->result; + SET_UNUSED(opline->op2); +} + + +void zend_do_binary_assign_op(int op, znode *result, znode *op1, znode *op2 CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = op; + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *op1; + opline->op2 = *op2; + *result = opline->result; +} + + + +void zend_do_fetch_globals(znode *varname CLS_DC) +{ + if (!CG(active_op_array)->uses_globals + && varname->op_type == IS_CONST + && varname->u.constant.type == IS_STRING + && varname->u.constant.value.str.len == (sizeof("GLOBALS")-1) + && !memcmp(varname->u.constant.value.str.val, "GLOBALS", sizeof("GLOBALS")-1)) { + CG(active_op_array)->uses_globals = 1; + } +} + +void fetch_simple_variable_ex(znode *result, znode *varname, int bp, int op CLS_DC) +{ + zend_op opline; + zend_op *opline_ptr; + zend_llist *fetch_list_ptr; + + if (bp) { + opline_ptr = &opline; + init_op(opline_ptr CLS_CC); + } else { + opline_ptr = get_next_op(CG(active_op_array) CLS_CC); + } + + opline_ptr->opcode = op; + opline_ptr->result.op_type = IS_VAR; + opline_ptr->result.u.EA.type = 0; + opline_ptr->result.u.var = get_temporary_variable(CG(active_op_array)); + opline_ptr->op1 = *varname; + *result = opline_ptr->result; + SET_UNUSED(opline_ptr->op2); + opline_ptr->op2.u.fetch_type = ZEND_FETCH_LOCAL; + + if (bp) { + zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); + zend_llist_add_element(fetch_list_ptr, opline_ptr); + } +} + +void fetch_simple_variable(znode *result, znode *varname, int bp CLS_DC) +{ + /* the default mode must be Write, since fetch_simple_variable() is used to define function arguments */ + fetch_simple_variable_ex(result, varname, bp, ZEND_FETCH_W CLS_CC); +} + + +void fetch_array_begin(znode *result, znode *varname, znode *first_dim CLS_DC) +{ + fetch_simple_variable(result, varname, 1 CLS_CC); + + fetch_array_dim(result, result, first_dim CLS_CC); +} + + +void fetch_array_dim(znode *result, znode *parent, znode *dim CLS_DC) +{ + zend_op opline; + zend_llist *fetch_list_ptr; + + init_op(&opline CLS_CC); + opline.opcode = ZEND_FETCH_DIM_W; /* the backpatching routine assumes W */ + opline.result.op_type = IS_VAR; + opline.result.u.EA.type = 0; + opline.result.u.var = get_temporary_variable(CG(active_op_array)); + opline.op1 = *parent; + opline.op2 = *dim; + opline.extended_value = ZEND_FETCH_STANDARD; + *result = opline.result; + + zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); + zend_llist_add_element(fetch_list_ptr, &opline); +} + + +void fetch_string_offset(znode *result, znode *parent, znode *offset CLS_DC) +{ + fetch_array_dim(result, parent, offset CLS_CC); +} + + +void zend_do_print(znode *result, znode *arg CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->opcode = ZEND_PRINT; + opline->op1 = *arg; + SET_UNUSED(opline->op2); + *result = opline->result; +} + + +void zend_do_echo(znode *arg CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_ECHO; + opline->op1 = *arg; + SET_UNUSED(opline->op2); +} + + +void zend_do_assign(znode *result, znode *variable, znode *value CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_ASSIGN; + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *variable; + opline->op2 = *value; + *result = opline->result; +} + + +void zend_do_assign_ref(znode *result, znode *lvar, znode *rvar CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_ASSIGN_REF; + if (result) { + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + *result = opline->result; + } else { + /* SET_UNUSED(opline->result); */ + opline->result.u.EA.type |= EXT_TYPE_UNUSED; + } + opline->op1 = *lvar; + opline->op2 = *rvar; +} + + +static inline void do_begin_loop(CLS_D) +{ + zend_brk_cont_element *brk_cont_element; + int parent; + + parent = CG(active_op_array)->current_brk_cont; + CG(active_op_array)->current_brk_cont = CG(active_op_array)->last_brk_cont; + brk_cont_element = get_next_brk_cont_element(CG(active_op_array)); + brk_cont_element->parent = parent; +} + + +static inline void do_end_loop(int cont_addr CLS_DC) +{ + CG(active_op_array)->brk_cont_array[CG(active_op_array)->current_brk_cont].cont = cont_addr; + CG(active_op_array)->brk_cont_array[CG(active_op_array)->current_brk_cont].brk = get_next_op_number(CG(active_op_array)); + CG(active_op_array)->current_brk_cont = CG(active_op_array)->brk_cont_array[CG(active_op_array)->current_brk_cont].parent; +} + + +void zend_do_while_cond(znode *expr, znode *close_bracket_token CLS_DC) +{ + int while_cond_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMPZ; + opline->op1 = *expr; + close_bracket_token->u.opline_num = while_cond_op_number; + SET_UNUSED(opline->op2); + + do_begin_loop(CLS_C); + INC_BPC(CG(active_op_array)); +} + + +void zend_do_while_end(znode *while_token, znode *close_bracket_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + /* add unconditional jump */ + opline->opcode = ZEND_JMP; + opline->op1.u.opline_num = while_token->u.opline_num; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + + /* update while's conditional jmp */ + CG(active_op_array)->opcodes[close_bracket_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); + + do_end_loop(while_token->u.opline_num CLS_CC); + + DEC_BPC(CG(active_op_array)); +} + + +void zend_do_for_cond(znode *expr, znode *second_semicolon_token CLS_DC) +{ + int for_cond_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMPZNZ; + opline->op1 = *expr; /* the conditional expression */ + second_semicolon_token->u.opline_num = for_cond_op_number; + SET_UNUSED(opline->op2); +} + + +void zend_do_for_before_statement(znode *cond_start, znode *second_semicolon_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMP; + opline->op1.u.opline_num = cond_start->u.opline_num; + CG(active_op_array)->opcodes[second_semicolon_token->u.opline_num].extended_value = get_next_op_number(CG(active_op_array)); + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + + do_begin_loop(CLS_C); + + INC_BPC(CG(active_op_array)); +} + + +void zend_do_for_end(znode *second_semicolon_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMP; + opline->op1.u.opline_num = second_semicolon_token->u.opline_num+1; + CG(active_op_array)->opcodes[second_semicolon_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + + do_end_loop(second_semicolon_token->u.opline_num+1 CLS_CC); + + DEC_BPC(CG(active_op_array)); +} + + +void zend_do_pre_incdec(znode *result, znode *op1, int op CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = op; + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *op1; + *result = opline->result; + SET_UNUSED(opline->op2); +} + + +void zend_do_post_incdec(znode *result, znode *op1, int op CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = op; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *op1; + *result = opline->result; + SET_UNUSED(opline->op2); +} + + +void zend_do_if_cond(znode *cond, znode *closing_bracket_token CLS_DC) +{ + int if_cond_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMPZ; + opline->op1 = *cond; + closing_bracket_token->u.opline_num = if_cond_op_number; + SET_UNUSED(opline->op2); + INC_BPC(CG(active_op_array)); +} + + +void zend_do_if_after_statement(znode *closing_bracket_token, unsigned char initialize CLS_DC) +{ + int if_end_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + zend_llist *jmp_list_ptr; + + opline->opcode = ZEND_JMP; + /* save for backpatching */ + if (initialize) { + zend_llist jmp_list; + + zend_llist_init(&jmp_list, sizeof(int), NULL, 0); + zend_stack_push(&CG(bp_stack), (void *) &jmp_list, sizeof(zend_llist)); + } + zend_stack_top(&CG(bp_stack), (void **) &jmp_list_ptr); + zend_llist_add_element(jmp_list_ptr, &if_end_op_number); + + CG(active_op_array)->opcodes[closing_bracket_token->u.opline_num].op2.u.opline_num = if_end_op_number+1; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); +} + + +void zend_do_if_end(CLS_D) +{ + int next_op_number = get_next_op_number(CG(active_op_array)); + zend_llist *jmp_list_ptr; + zend_llist_element *le; + + zend_stack_top(&CG(bp_stack), (void **) &jmp_list_ptr); + for (le=jmp_list_ptr->head; le; le = le->next) { + CG(active_op_array)->opcodes[*((int *) le->data)].op1.u.opline_num = next_op_number; + } + zend_llist_destroy(jmp_list_ptr); + zend_stack_del_top(&CG(bp_stack)); + DEC_BPC(CG(active_op_array)); +} + + +void zend_do_begin_variable_parse(CLS_D) +{ + zend_llist fetch_list; + + zend_llist_init(&fetch_list, sizeof(zend_op), NULL, 0); + zend_stack_push(&CG(bp_stack), (void *) &fetch_list, sizeof(zend_llist)); +} + + +void zend_do_end_variable_parse(int type, int arg_offset CLS_DC) +{ + zend_llist *fetch_list_ptr; + zend_llist_element *le; + zend_op *opline, *opline_ptr; + + zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); + + le = fetch_list_ptr->head; + + while (le) { + opline_ptr = (zend_op *)le->data; + opline = get_next_op(CG(active_op_array) CLS_CC); + memcpy(opline, opline_ptr, sizeof(zend_op)); + switch (type) { + case BP_VAR_R: + if (opline->opcode == ZEND_FETCH_DIM_W && opline->op2.op_type == IS_UNUSED) { + zend_error(E_COMPILE_ERROR, "Cannot use [] for reading"); + } + opline->opcode -= 3; + break; + case BP_VAR_W: + break; + case BP_VAR_RW: + opline->opcode += 3; + break; + case BP_VAR_IS: + opline->opcode += 6; /* 3+3 */ + break; + case BP_VAR_FUNC_ARG: + opline->opcode += 9; /* 3+3+3 */ + opline->extended_value = arg_offset; + break; + case BP_VAR_UNSET: + opline->opcode += 12; /* 3+3+3+3 */ + break; + } + le = le->next; + } + zend_llist_destroy(fetch_list_ptr); + zend_stack_del_top(&CG(bp_stack)); +} + + +static zend_bool is_method_call(CLS_D) +{ + zend_llist *fetch_list_ptr; + zend_llist_element *cur; + zend_op *cur_opline; + + zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); + + cur = fetch_list_ptr->head; + /* There is always at least one node in the list */ + while (cur->next) { + cur = cur->next; + } + cur_opline = (zend_op *)cur->data; + if (cur_opline->opcode == ZEND_FETCH_OBJ_W) { + return 1; + } + return 0; +} + + +void zend_do_init_string(znode *result CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_INIT_STRING; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + *result = opline->result; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); +} + + +void zend_do_add_char(znode *result, znode *op1, znode *op2 CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_ADD_CHAR; + opline->op1 = *op1; + opline->op2 = *op2; + opline->op2.op_type = IS_CONST; + opline->result = opline->op1; + *result = opline->result; +} + + +void zend_do_add_string(znode *result, znode *op1, znode *op2 CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_ADD_STRING; + opline->op1 = *op1; + opline->op2 = *op2; + opline->op2.op_type = IS_CONST; + opline->result = opline->op1; + *result = opline->result; +} + + +void zend_do_add_variable(znode *result, znode *op1, znode *op2 CLS_DC) +{ + zend_op *opline; + + if (op1->op_type == IS_CONST) { + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_INIT_STRING; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + *result = opline->result; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + + if (op1->u.constant.value.str.len>0) { + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_ADD_STRING; + opline->result = *result; + opline->op1 = *result; + opline->op2 = *op1; + opline->result = opline->op1; + } else { + zval_dtor(&op1->u.constant); + } + } else { + *result = *op1; + } + + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_ADD_VAR; + opline->result = *result; + opline->op1 = *result; + opline->op2 = *op2; + *result = opline->result; +} + + +void zend_do_free(znode *op1 CLS_DC) +{ + if (op1->op_type==IS_TMP_VAR) { + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_FREE; + opline->op1 = *op1; + SET_UNUSED(opline->op2); + } else if (op1->op_type==IS_VAR) { + zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1]; + + if (opline->opcode == ZEND_END_SILENCE) { + opline--; + } + if (opline->result.op_type == op1->op_type + && opline->result.u.var == op1->u.var) { + opline->result.u.EA.type |= EXT_TYPE_UNUSED; + } else { + while (opline>CG(active_op_array)->opcodes) { + /* This should be an object instantiation + * Find JMP_NO_CTOR, mark the preceding ASSIGN and the + * proceeding INIT_FCALL_BY_NAME as unused + */ + if (opline->opcode == ZEND_JMP_NO_CTOR) { + (opline-1)->result.u.EA.type |= EXT_TYPE_UNUSED; + (opline+1)->op1.u.EA.type |= EXT_TYPE_UNUSED; + break; + } else if (opline->opcode == ZEND_FETCH_DIM_R + && opline->op1.op_type == IS_VAR + && opline->op1.u.var == op1->u.var) { + /* This should the end of a list() construct + * Mark its result as unused + */ + opline->extended_value = ZEND_FETCH_STANDARD; + break; + } else if (opline->result.op_type==IS_VAR + && opline->result.u.var == op1->u.var) { + break; + } + opline--; + } + } + } +} + + +void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference CLS_DC) +{ + zend_op_array op_array; + char *name = function_name->u.constant.value.str.val; + int name_len = function_name->u.constant.value.str.len; + int function_begin_line = function_token->u.opline_num; + + function_token->u.op_array = CG(active_op_array); + zend_str_tolower(name, name_len); + + init_op_array(&op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE CLS_CC); + + op_array.function_name = name; + op_array.arg_types = NULL; + op_array.return_reference = return_reference; + + if (is_method) { + zend_hash_update(&CG(active_class_entry)->function_table, name, name_len+1, &op_array, sizeof(zend_op_array), (void **) &CG(active_op_array)); + } else { + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_DECLARE_FUNCTION_OR_CLASS; + opline->op1.op_type = IS_CONST; + build_runtime_defined_function_key(&opline->op1.u.constant, &function_name->u.constant, opline CLS_CC); + opline->op2.op_type = IS_CONST; + opline->op2.u.constant.type = IS_STRING; + opline->op2.u.constant.value.str.val = estrndup(name, name_len); + opline->op2.u.constant.value.str.len = name_len; + opline->op2.u.constant.refcount = 1; + opline->extended_value = ZEND_DECLARE_FUNCTION; + zend_hash_update(CG(function_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &op_array, sizeof(zend_op_array), (void **) &CG(active_op_array)); + } + + if (CG(extended_info)) { + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_EXT_NOP; + opline->lineno = function_begin_line; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + } + + { + /* Push a seperator to the switch and foreach stacks */ + zend_switch_entry switch_entry; + + switch_entry.cond.op_type = IS_UNUSED; + switch_entry.default_case = 0; + switch_entry.control_var = 0; + + zend_stack_push(&CG(switch_cond_stack), (void *) &switch_entry, sizeof(switch_entry)); + + zend_stack_push(&CG(foreach_copy_stack), (void *) &switch_entry.cond, sizeof(znode)); + } +} + + +void zend_do_end_function_declaration(znode *function_token CLS_DC) +{ + zend_do_extended_info(CLS_C); + zend_do_return(NULL, 0 CLS_CC); + pass_two(CG(active_op_array)); + CG(active_op_array) = function_token->u.op_array; + + /* Pop the switch and foreach seperators */ + zend_stack_del_top(&CG(switch_cond_stack)); + zend_stack_del_top(&CG(foreach_copy_stack)); +} + + +void zend_do_receive_arg(int op, znode *var, znode *offset, znode *initialization, unsigned char pass_type CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = op; + opline->result = *var; + opline->op1 = *offset; + if ((op == ZEND_RECV_INIT)) { + opline->op2 = *initialization; + } else { + SET_UNUSED(opline->op2); + } + if (!CG(active_op_array)->arg_types) { + if (pass_type==BYREF_FORCE) { + int i; + + CG(active_op_array)->arg_types = (unsigned char *) emalloc(sizeof(unsigned char)*(offset->u.constant.value.lval+1)); + for (i=1; iu.constant.value.lval; i++) { + CG(active_op_array)->arg_types[i] = BYREF_NONE; + } + CG(active_op_array)->arg_types[0]=(unsigned char) offset->u.constant.value.lval; + CG(active_op_array)->arg_types[offset->u.constant.value.lval] = pass_type; + } + } else { + CG(active_op_array)->arg_types = (unsigned char *) erealloc(CG(active_op_array)->arg_types, sizeof(unsigned char)*(offset->u.constant.value.lval+1)); + CG(active_op_array)->arg_types[0]=(unsigned char) offset->u.constant.value.lval; + CG(active_op_array)->arg_types[offset->u.constant.value.lval] = pass_type; + } +} + + +int zend_do_begin_function_call(znode *function_name CLS_DC) +{ + zend_function *function; + + zend_str_tolower(function_name->u.constant.value.str.val, function_name->u.constant.value.str.len); + if (zend_hash_find(CG(function_table), function_name->u.constant.value.str.val,function_name->u.constant.value.str.len+1, (void **) &function)==FAILURE) { + znode tmp = *function_name; + + zval_copy_ctor(&tmp.u.constant); + zend_do_begin_dynamic_function_call(&tmp CLS_CC); + return 1; /* Dynamic */ + } + + switch (function->type) { + case ZEND_USER_FUNCTION: { + zend_op_array *op_array = (zend_op_array *) function; + + zend_stack_push(&CG(function_call_stack), (void *) &op_array, sizeof(zend_function *)); + } + break; + case ZEND_INTERNAL_FUNCTION: { + zend_internal_function *internal_function = (zend_internal_function *) function; + + zend_stack_push(&CG(function_call_stack), (void *) &internal_function, sizeof(zend_function *)); + } + break; + } + zend_do_extended_fcall_begin(CLS_C); + return 0; +} + + +void zend_do_begin_dynamic_function_call(znode *function_name CLS_DC) +{ + unsigned char *ptr = NULL; + int last_op_number; + zend_op *last_op; + + if (function_name->op_type != IS_CONST && is_method_call(CLS_C)) { + zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); + last_op_number = get_next_op_number(CG(active_op_array))-1; + last_op = &CG(active_op_array)->opcodes[last_op_number]; + last_op->opcode = ZEND_INIT_FCALL_BY_NAME; + last_op->extended_value = ZEND_MEMBER_FUNC_CALL; + } else { + zend_op *opline; + + if (function_name->op_type != IS_CONST) { + zend_do_end_variable_parse(BP_VAR_R, 0 CLS_CC); + } + + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_INIT_FCALL_BY_NAME; + opline->op2 = *function_name; + opline->extended_value = 0; + SET_UNUSED(opline->op1); + } + zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *)); + zend_do_extended_fcall_begin(CLS_C); +} + + +void zend_do_begin_class_member_function_call(znode *class_name, znode *function_name CLS_DC) +{ + unsigned char *ptr = NULL; + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_INIT_FCALL_BY_NAME; + zend_str_tolower(class_name->u.constant.value.str.val, class_name->u.constant.value.str.len); + if ((class_name->u.constant.value.str.len == sizeof("parent")-1) + && !memcmp(class_name->u.constant.value.str.val, "parent", sizeof("parent")-1)) { + if (!CG(active_class_entry) || !CG(active_class_entry)->parent) { + zend_error(E_COMPILE_ERROR, "No parent class available in this context"); + } + efree(class_name->u.constant.value.str.val); + class_name->u.constant.value.str.len = CG(active_class_entry)->parent->name_length; + class_name->u.constant.value.str.val = estrndup(CG(active_class_entry)->parent->name, class_name->u.constant.value.str.len); + } + opline->op1 = *class_name; + opline->op2 = *function_name; + opline->extended_value = ZEND_MEMBER_FUNC_CALL; + zval_copy_ctor(&opline->op2.u.constant); + zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *)); +} + + +void zend_do_end_function_call(znode *function_name, znode *result, znode *argument_list, int is_method, int is_dynamic_fcall CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + if (function_name->op_type==IS_CONST && !is_method && !is_dynamic_fcall) { + opline->opcode = ZEND_DO_FCALL; + } else { + opline->opcode = ZEND_DO_FCALL_BY_NAME; + } + opline->op1 = *function_name; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->result.op_type = IS_VAR; + *result = opline->result; + SET_UNUSED(opline->op2); + opline->op2.u.constant.value.lval = is_method; + zend_stack_del_top(&CG(function_call_stack)); + opline->extended_value = argument_list->u.constant.value.lval; +} + + +void zend_do_pass_param(znode *param, int op, int offset CLS_DC) +{ + zend_op *opline; + unsigned char *arg_types; + int original_op=op; + zend_function **function_ptr_ptr, *function_ptr; + int send_by_reference; + + + zend_stack_top(&CG(function_call_stack), (void **) &function_ptr_ptr); + function_ptr = *function_ptr_ptr; + + if (original_op==ZEND_SEND_REF + && !CG(allow_call_time_pass_reference)) { + zend_error(E_COMPILE_WARNING, + "Call-time pass-by-reference has been deprecated - argument passed by value; " + "If you would like to pass it by reference, modify the declaration of %s(). " + "If you would like to enable call-time pass-by-reference, you can set " + "allow_call_time_pass_reference to true in your INI file. " + "However, future versions may not support this any longer. ", + (function_ptr?function_ptr->common.function_name:"[runtime function name]"), + offset+1); + } + + if (function_ptr) { + arg_types = function_ptr->common.arg_types; + } else { + arg_types = NULL; + } + + send_by_reference = ARG_SHOULD_BE_SENT_BY_REF(offset, 1, arg_types)?ZEND_ARG_SEND_BY_REF:0; + + if (op == ZEND_SEND_VAL && param->op_type == IS_VAR) { + op = ZEND_SEND_VAR_NO_REF; + } + + if (op!=ZEND_SEND_VAR_NO_REF && send_by_reference == ZEND_ARG_SEND_BY_REF) { + /* change to passing by reference */ + switch (param->op_type) { + case IS_VAR: + op = ZEND_SEND_REF; + break; + default: + zend_error(E_COMPILE_ERROR, "Only variables can be passed by reference"); + break; + } + } + + if (original_op == ZEND_SEND_VAR) { + switch(op) { + case ZEND_SEND_VAR_NO_REF: + zend_do_end_variable_parse(BP_VAR_R, 0 CLS_CC); + break; + case ZEND_SEND_VAR: + if (function_ptr) { + zend_do_end_variable_parse(BP_VAR_R, 0 CLS_CC); + } else { + zend_do_end_variable_parse(BP_VAR_FUNC_ARG, offset CLS_CC); + } + break; + case ZEND_SEND_REF: + zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); + break; + } + } + + opline = get_next_op(CG(active_op_array) CLS_CC); + + if (op == ZEND_SEND_VAR_NO_REF) { + if (function_ptr) { + opline->extended_value = ZEND_ARG_COMPILE_TIME_BOUND | send_by_reference; + } else { + opline->extended_value = 0; + } + } else { + if (function_ptr) { + opline->extended_value = ZEND_DO_FCALL; + } else { + opline->extended_value = ZEND_DO_FCALL_BY_NAME; + } + } + opline->opcode = op; + opline->op1 = *param; + opline->op2.u.opline_num = offset; + SET_UNUSED(opline->op2); +} + + +static int generate_free_switch_expr(zend_switch_entry *switch_entry CLS_DC) +{ + zend_op *opline; + + if (switch_entry->cond.op_type!=IS_VAR && switch_entry->cond.op_type!=IS_TMP_VAR) { + return 1; + } + + opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_SWITCH_FREE; + opline->op1 = switch_entry->cond; + SET_UNUSED(opline->op2); + opline->extended_value = 0; + return 0; +} + +static int generate_free_foreach_copy(znode *foreach_copy CLS_DC) +{ + zend_op *opline; + + if (foreach_copy->op_type!=IS_VAR && foreach_copy->op_type!=IS_TMP_VAR) { + return 1; + } + + opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_SWITCH_FREE; + opline->op1 = *foreach_copy; + SET_UNUSED(opline->op2); + opline->extended_value = 1; + return 0; +} + +void zend_do_return(znode *expr, int do_end_vparse CLS_DC) +{ + zend_op *opline; + + if (do_end_vparse) { + if (CG(active_op_array)->return_reference) { + zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); + } else { + zend_do_end_variable_parse(BP_VAR_R, 0 CLS_CC); + } + } +#ifdef ZTS + zend_stack_apply_with_argument(&CG(switch_cond_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element, void *)) generate_free_switch_expr CLS_CC); + zend_stack_apply_with_argument(&CG(foreach_copy_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element, void *)) generate_free_foreach_copy CLS_CC); +#else + zend_stack_apply(&CG(switch_cond_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_switch_expr); + zend_stack_apply(&CG(foreach_copy_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_foreach_copy); +#endif + + opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_RETURN; + + if (expr) { + opline->op1 = *expr; + } else { + opline->op1.op_type = IS_CONST; + INIT_ZVAL(opline->op1.u.constant); + } + SET_UNUSED(opline->op2); +} + + +ZEND_API void function_add_ref(zend_function *function) +{ + if (function->type == ZEND_USER_FUNCTION) { + zend_op_array *op_array = &function->op_array; + + (*op_array->refcount)++; + if (op_array->static_variables) { + HashTable *static_variables = op_array->static_variables; + zval *tmp_zval; + + ALLOC_HASHTABLE(op_array->static_variables); + zend_hash_init(op_array->static_variables, 2, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(op_array->static_variables, static_variables, (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *)); + } + } +} + + +static void do_inherit_parent_constructor(zend_class_entry *ce) +{ + if (ce->parent + && !zend_hash_exists(&ce->function_table, ce->name, ce->name_length+1)) { + zend_function *function; + + if (zend_hash_find(&ce->parent->function_table, ce->parent->name, ce->parent->name_length+1, (void **) &function)==SUCCESS) { + /* inherit parent's constructor */ + zend_hash_update(&ce->function_table, ce->name, ce->name_length+1, function, sizeof(zend_function), NULL); + function_add_ref(function); + } + } +} + +void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) +{ + zend_function tmp_zend_function; + zval *tmp; + + /* Perform inheritance */ + zend_hash_merge(&ce->default_properties, &parent_ce->default_properties, (void (*)(void *)) zval_add_ref, (void *) &tmp, sizeof(zval *), 0); + zend_hash_merge(&ce->function_table, &parent_ce->function_table, (void (*)(void *)) function_add_ref, &tmp_zend_function, sizeof(zend_function), 0); + ce->parent = parent_ce; + do_inherit_parent_constructor(ce); +} + + +ZEND_API int do_bind_function_or_class(zend_op *opline, HashTable *function_table, HashTable *class_table, int compile_time) +{ + switch (opline->extended_value) { + case ZEND_DECLARE_FUNCTION: { + zend_function *function; + + zend_hash_find(function_table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, (void *) &function); + if (zend_hash_add(function_table, opline->op2.u.constant.value.str.val, opline->op2.u.constant.value.str.len+1, function, sizeof(zend_function), NULL)==FAILURE) { + if (!compile_time) { + zend_error(E_ERROR, "Cannot redeclare %s()", opline->op2.u.constant.value.str.val); + } + return FAILURE; + } else { + (*function->op_array.refcount)++; + function->op_array.static_variables = NULL; /* NULL out the unbound function */ + return SUCCESS; + } + } + break; + case ZEND_DECLARE_CLASS: { + zend_class_entry *ce; + + if (zend_hash_find(class_table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, (void **) &ce)==FAILURE) { + zend_error(E_ERROR, "Internal Zend error - Missing class information for %s", opline->op1.u.constant.value.str.val); + return FAILURE; + } + (*ce->refcount)++; + if (zend_hash_add(class_table, opline->op2.u.constant.value.str.val, opline->op2.u.constant.value.str.len+1, ce, sizeof(zend_class_entry), NULL)==FAILURE) { + (*ce->refcount)--; + if (!compile_time) { + zend_error(E_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val); + } + return FAILURE; + } else { + return SUCCESS; + } + } + break; + case ZEND_DECLARE_INHERITED_CLASS: { + zend_class_entry *ce, *parent_ce; + int parent_name_length; + char *class_name, *parent_name; + int found_ce; + + + found_ce = zend_hash_find(class_table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, (void **) &ce); + + /* Restore base class / derived class names */ + class_name = strchr(opline->op2.u.constant.value.str.val, ':'); + if (!class_name) { + zend_error(E_CORE_ERROR, "Invalid runtime class entry"); + } + class_name++; + + if (found_ce==FAILURE) { + zend_error(E_ERROR, "Cannot redeclare class %s", class_name); + return FAILURE; + } + + (*ce->refcount)++; + + /* Obtain parent class */ + parent_name_length = class_name - opline->op2.u.constant.value.str.val - 1; + parent_name = estrndup(opline->op2.u.constant.value.str.val, parent_name_length); + if (zend_hash_find(class_table, parent_name, parent_name_length+1, (void **) &parent_ce)==FAILURE) { + if (!compile_time) { + zend_error(E_ERROR, "Class %s: Cannot inherit from undefined class %s", class_name, parent_name); + } + (*ce->refcount)--; + efree(parent_name); + return FAILURE; + } + efree(parent_name); + + zend_do_inheritance(ce, parent_ce); + + /* Register the derived class */ + if (zend_hash_add(class_table, class_name, strlen(class_name)+1, ce, sizeof(zend_class_entry), NULL)==FAILURE) { + if (!compile_time) { + zend_error(E_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val); + } + (*ce->refcount)--; + zend_hash_destroy(&ce->function_table); + zend_hash_destroy(&ce->default_properties); + return FAILURE; + } + return SUCCESS; + } + break; + } + return FAILURE; +} + + +void zend_do_early_binding(CLS_D) +{ + zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1]; + HashTable *table; + + if (do_bind_function_or_class(opline, CG(function_table), CG(class_table), 1)==FAILURE) { + return; + } + switch (opline->extended_value) { + case ZEND_DECLARE_FUNCTION: + table = CG(function_table); + break; + case ZEND_DECLARE_CLASS: + table = CG(class_table); + break; + default: + zend_error(E_COMPILE_ERROR, "Invalid binding type"); + return; + } + zend_hash_del(table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len); + zval_dtor(&opline->op1.u.constant); + zval_dtor(&opline->op2.u.constant); + opline->opcode = ZEND_NOP; + memset(&opline->op1, 0, sizeof(znode)); + memset(&opline->op2, 0, sizeof(znode)); + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); +} + + +void zend_do_boolean_or_begin(znode *expr1, znode *op_token CLS_DC) +{ + int next_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMPNZ_EX; + if (expr1->op_type == IS_TMP_VAR) { + opline->result = *expr1; + } else { + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->result.op_type = IS_TMP_VAR; + } + opline->op1 = *expr1; + SET_UNUSED(opline->op2); + + op_token->u.opline_num = next_op_number; + + *expr1 = opline->result; +} + + +void zend_do_boolean_or_end(znode *result, znode *expr1, znode *expr2, znode *op_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + *result = *expr1; /* we saved the original result in expr1 */ + opline->opcode = ZEND_BOOL; + opline->result = *result; + opline->op1 = *expr2; + SET_UNUSED(opline->op2); + + CG(active_op_array)->opcodes[op_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); +} + + +void zend_do_boolean_and_begin(znode *expr1, znode *op_token CLS_DC) +{ + int next_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMPZ_EX; + if (expr1->op_type == IS_TMP_VAR) { + opline->result = *expr1; + } else { + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->result.op_type = IS_TMP_VAR; + } + opline->op1 = *expr1; + SET_UNUSED(opline->op2); + + op_token->u.opline_num = next_op_number; + + *expr1 = opline->result; +} + + +void zend_do_boolean_and_end(znode *result, znode *expr1, znode *expr2, znode *op_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + *result = *expr1; /* we saved the original result in expr1 */ + opline->opcode = ZEND_BOOL; + opline->result = *result; + opline->op1 = *expr2; + SET_UNUSED(opline->op2); + + CG(active_op_array)->opcodes[op_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); +} + + +void zend_do_do_while_begin(CLS_D) +{ + do_begin_loop(CLS_C); + INC_BPC(CG(active_op_array)); +} + + +void zend_do_do_while_end(znode *do_token, znode *expr_open_bracket, znode *expr CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMPNZ; + opline->op1 = *expr; + opline->op2.u.opline_num = do_token->u.opline_num; + SET_UNUSED(opline->op2); + + do_end_loop(expr_open_bracket->u.opline_num CLS_CC); + + DEC_BPC(CG(active_op_array)); +} + + +void zend_do_brk_cont(int op, znode *expr CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = op; + opline->op1.u.opline_num = CG(active_op_array)->current_brk_cont; + SET_UNUSED(opline->op1); + if (expr) { + opline->op2 = *expr; + } else { + opline->op2.u.constant.type = IS_LONG; + opline->op2.u.constant.value.lval = 1; + INIT_PZVAL(&opline->op2.u.constant); + opline->op2.op_type = IS_CONST; + } +} + + +void zend_do_switch_cond(znode *cond CLS_DC) +{ + zend_switch_entry switch_entry; + zend_op *opline; + + /* Initialize the conditional value */ + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_BOOL; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->result.op_type = IS_TMP_VAR; + opline->op1.op_type = IS_CONST; + opline->op1.u.constant.type = IS_BOOL; + opline->op1.u.constant.value.lval = 0; + INIT_PZVAL(&opline->op1.u.constant); + SET_UNUSED(opline->op2); + + switch_entry.cond = *cond; + switch_entry.default_case = -1; + switch_entry.control_var = opline->result.u.var; + zend_stack_push(&CG(switch_cond_stack), (void *) &switch_entry, sizeof(switch_entry)); + + do_begin_loop(CLS_C); + + INC_BPC(CG(active_op_array)); +} + + + +void zend_do_switch_end(znode *case_list CLS_DC) +{ + zend_op *opline; + zend_switch_entry *switch_entry_ptr; + + zend_stack_top(&CG(switch_cond_stack), (void **) &switch_entry_ptr); + + if (case_list->op_type != IS_UNUSED) { /* non-empty switch */ + int next_op_number = get_next_op_number(CG(active_op_array)); + + CG(active_op_array)->opcodes[case_list->u.opline_num].op1.u.opline_num = next_op_number; + } + + /* add code to jmp to default case */ + if (switch_entry_ptr->default_case != -1) { + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_JMPZ; + opline->op1.op_type = IS_TMP_VAR; + opline->op1.u.var = switch_entry_ptr->control_var; + opline->op2.u.opline_num = switch_entry_ptr->default_case; + SET_UNUSED(opline->op2); + } + + + /* remember break/continue loop information */ + CG(active_op_array)->brk_cont_array[CG(active_op_array)->current_brk_cont].cont = CG(active_op_array)->brk_cont_array[CG(active_op_array)->current_brk_cont].brk = get_next_op_number(CG(active_op_array)); + CG(active_op_array)->current_brk_cont = CG(active_op_array)->brk_cont_array[CG(active_op_array)->current_brk_cont].parent; + + if (switch_entry_ptr->cond.op_type==IS_VAR || switch_entry_ptr->cond.op_type==IS_TMP_VAR) { + /* emit free for the switch condition*/ + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_SWITCH_FREE; + opline->op1 = switch_entry_ptr->cond; + SET_UNUSED(opline->op2); + } + if (switch_entry_ptr->cond.op_type == IS_CONST) { + zval_dtor(&switch_entry_ptr->cond.u.constant); + } + + zend_stack_del_top(&CG(switch_cond_stack)); + + DEC_BPC(CG(active_op_array)); +} + + +void zend_do_case_before_statement(znode *case_list, znode *case_token, znode *case_expr CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + int next_op_number; + zend_switch_entry *switch_entry_ptr; + znode result; + + zend_stack_top(&CG(switch_cond_stack), (void **) &switch_entry_ptr); + + opline->opcode = ZEND_CASE; + opline->result.u.var = switch_entry_ptr->control_var; + opline->result.op_type = IS_TMP_VAR; + opline->op1 = switch_entry_ptr->cond; + opline->op2 = *case_expr; + if (opline->op1.op_type == IS_CONST) { + zval_copy_ctor(&opline->op1.u.constant); + } + result = opline->result; + + next_op_number = get_next_op_number(CG(active_op_array)); + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_JMPZ; + opline->op1 = result; + SET_UNUSED(opline->op2); + case_token->u.opline_num = next_op_number; + + if (case_list->op_type==IS_UNUSED) { + return; + } + next_op_number = get_next_op_number(CG(active_op_array)); + CG(active_op_array)->opcodes[case_list->u.opline_num].op1.u.opline_num = next_op_number; +} + + +void zend_do_case_after_statement(znode *result, znode *case_token CLS_DC) +{ + int next_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMP; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + result->u.opline_num = next_op_number; + + switch (CG(active_op_array)->opcodes[case_token->u.opline_num].opcode) { + case ZEND_JMP: + CG(active_op_array)->opcodes[case_token->u.opline_num].op1.u.opline_num = get_next_op_number(CG(active_op_array)); + break; + case ZEND_JMPZ: + CG(active_op_array)->opcodes[case_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); + break; + } +} + + + +void zend_do_default_before_statement(znode *case_list, znode *default_token CLS_DC) +{ + int next_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + zend_switch_entry *switch_entry_ptr; + + zend_stack_top(&CG(switch_cond_stack), (void **) &switch_entry_ptr); + + opline->opcode = ZEND_JMP; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + default_token->u.opline_num = next_op_number; + + next_op_number = get_next_op_number(CG(active_op_array)); + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_BOOL; + opline->result.u.var = switch_entry_ptr->control_var; + opline->result.op_type = IS_TMP_VAR; + opline->op1.op_type = IS_CONST; + opline->op1.u.constant.type = IS_BOOL; + opline->op1.u.constant.value.lval = 1; + INIT_PZVAL(&opline->op1.u.constant); + SET_UNUSED(opline->op2); + switch_entry_ptr->default_case = next_op_number; + + if (case_list->op_type==IS_UNUSED) { + return; + } + next_op_number = get_next_op_number(CG(active_op_array)); + CG(active_op_array)->opcodes[case_list->u.opline_num].op1.u.opline_num = next_op_number; +} + + +void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + int runtime_inheritance = 0; + + if (CG(active_class_entry)) { + zend_error(E_COMPILE_ERROR, "Class declarations may not be nested"); + return; + } + CG(class_entry).type = ZEND_USER_CLASS; + CG(class_entry).name = class_name->u.constant.value.str.val; + CG(class_entry).name_length = class_name->u.constant.value.str.len; + CG(class_entry).refcount = (int *) emalloc(sizeof(int)); + *CG(class_entry).refcount = 1; + CG(class_entry).constants_updated = 0; + + zend_str_tolower(CG(class_entry).name, CG(class_entry).name_length); + + zend_hash_init(&CG(class_entry).function_table, 10, NULL, ZEND_FUNCTION_DTOR, 0); + zend_hash_init(&CG(class_entry).default_properties, 10, NULL, ZVAL_PTR_DTOR, 0); + + /* code for inheritance from parent class */ + if (parent_class_name) { + zend_class_entry *parent_class; + zend_function tmp_zend_function; + zval *tmp; + + zend_str_tolower(parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len); + + if (zend_hash_find(CG(class_table), parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len+1, (void **) &parent_class)==SUCCESS) { + /* copy functions */ + zend_hash_copy(&CG(class_entry).function_table, &parent_class->function_table, (copy_ctor_func_t) function_add_ref, &tmp_zend_function, sizeof(zend_function)); + + /* copy default properties */ + zend_hash_copy(&CG(class_entry).default_properties, &parent_class->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + + CG(class_entry).parent = parent_class; + + zval_dtor(&parent_class_name->u.constant); + } else { + runtime_inheritance = 1; + CG(class_entry).parent = NULL; + } + } else { + CG(class_entry).parent = NULL; + } + + CG(class_entry).handle_function_call = NULL; + CG(class_entry).handle_property_set = NULL; + CG(class_entry).handle_property_get = NULL; + + opline->opcode = ZEND_DECLARE_FUNCTION_OR_CLASS; + opline->op1.op_type = IS_CONST; + build_runtime_defined_function_key(&opline->op1.u.constant, &class_name->u.constant, opline CLS_CC); + opline->op2.op_type = IS_CONST; + opline->op2.u.constant.type = IS_STRING; + opline->op2.u.constant.refcount = 1; + if (runtime_inheritance) { + char *full_class_name; + + opline->op2.u.constant.value.str.len = parent_class_name->u.constant.value.str.len+1+CG(class_entry).name_length; + full_class_name = opline->op2.u.constant.value.str.val = (char *) emalloc(opline->op2.u.constant.value.str.len+1); + + memcpy(full_class_name, parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len); + full_class_name += parent_class_name->u.constant.value.str.len; + full_class_name[0] = ':'; + full_class_name++; + memcpy(full_class_name, CG(class_entry).name, CG(class_entry).name_length); + zval_dtor(&parent_class_name->u.constant); + full_class_name += CG(class_entry).name_length; + full_class_name[0] = 0; + opline->extended_value = ZEND_DECLARE_INHERITED_CLASS; + } else { + opline->op2.u.constant.value.str.val = estrndup(CG(class_entry).name, CG(class_entry).name_length); + opline->op2.u.constant.value.str.len = CG(class_entry).name_length; + opline->extended_value = ZEND_DECLARE_CLASS; + } + + zend_hash_update(CG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &CG(class_entry), sizeof(zend_class_entry), (void **) &CG(active_class_entry)); +} + + +void zend_do_end_class_declaration(CLS_D) +{ + do_inherit_parent_constructor(CG(active_class_entry)); + CG(active_class_entry) = NULL; +} + + +void zend_do_declare_property(znode *var_name, znode *value CLS_DC) +{ + if (value) { + zval *property; + + ALLOC_ZVAL(property); + + *property = value->u.constant; + zend_hash_update(&CG(active_class_entry)->default_properties, var_name->u.constant.value.str.val, var_name->u.constant.value.str.len+1, &property, sizeof(zval *), NULL); + } + FREE_PNODE(var_name); +} + + +void zend_do_fetch_property(znode *result, znode *object, znode *property CLS_DC) +{ + zend_op opline; + zend_llist *fetch_list_ptr; + + init_op(&opline CLS_CC); + opline.opcode = ZEND_FETCH_OBJ_W; /* the backpatching routine assumes W */ + opline.result.op_type = IS_VAR; + opline.result.u.EA.type = 0; + opline.result.u.var = get_temporary_variable(CG(active_op_array)); + opline.op1 = *object; + opline.op2 = *property; + *result = opline.result; + + zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); + zend_llist_add_element(fetch_list_ptr, &opline); +} + + +void zend_do_push_object(znode *object CLS_DC) +{ + zend_stack_push(&CG(object_stack), object, sizeof(znode)); +} + + +void zend_do_pop_object(znode *object CLS_DC) +{ + znode *tmp; + + zend_stack_top(&CG(object_stack), (void **) &tmp); + *object = *tmp; + zend_stack_del_top(&CG(object_stack)); +} + + +void zend_do_begin_new_object(znode *new_token, znode *class_name CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + unsigned char *ptr = NULL; + + opline->opcode = ZEND_NEW; + opline->result.op_type = IS_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *class_name; + SET_UNUSED(opline->op2); + + new_token->u.opline_num = get_next_op_number(CG(active_op_array)); + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_JMP_NO_CTOR; + opline->op1 = (opline-1)->result; + SET_UNUSED(opline->op2); + + if (class_name->op_type == IS_CONST) { + zval_copy_ctor(&class_name->u.constant); + } + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_INIT_FCALL_BY_NAME; + opline->op1 = (opline-2)->result; + opline->op2 = *class_name; + opline->extended_value = ZEND_MEMBER_FUNC_CALL | ZEND_CTOR_CALL; + zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(unsigned char *)); +} + + +void zend_do_end_new_object(znode *result, znode *class_name, znode *new_token, znode *argument_list CLS_DC) +{ + znode ctor_result; + + if (class_name->op_type == IS_CONST) { + zval_copy_ctor(&class_name->u.constant); + } + zend_do_end_function_call(class_name, &ctor_result, argument_list, 1, 0 CLS_CC); + zend_do_free(&ctor_result CLS_CC); + + CG(active_op_array)->opcodes[new_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); + *result = CG(active_op_array)->opcodes[new_token->u.opline_num].op1; +} + + +void zend_do_fetch_constant(znode *result, znode *constant_name, int mode CLS_DC) +{ + switch (mode) { + case ZEND_CT: + *result = *constant_name; + result->u.constant.type = IS_CONSTANT; + break; + case ZEND_RT: { + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_FETCH_CONSTANT; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *constant_name; + *result = opline->result; + SET_UNUSED(opline->op2); + } + break; + } +} + + +void zend_do_shell_exec(znode *result, znode *cmd CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + switch (cmd->op_type) { + case IS_TMP_VAR: + opline->opcode = ZEND_SEND_VAL; + break; + default: + opline->opcode = ZEND_SEND_VAR; + break; + } + opline->op1 = *cmd; + opline->op2.u.opline_num = 0; + opline->extended_value = ZEND_DO_FCALL; + SET_UNUSED(opline->op2); + + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_DO_FCALL; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->result.op_type = IS_VAR; + opline->op1.u.constant.value.str.val = estrndup("shell_exec",sizeof("shell_exec")-1); + opline->op1.u.constant.value.str.len = sizeof("shell_exec")-1; + INIT_PZVAL(&opline->op1.u.constant); + opline->op1.u.constant.type = IS_STRING; + opline->op1.op_type = IS_CONST; + opline->extended_value = 1; + SET_UNUSED(opline->op2); + *result = opline->result; +} + + + +void zend_do_init_array(znode *result, znode *expr, znode *offset, int is_ref CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_INIT_ARRAY; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->result.op_type = IS_TMP_VAR; + *result = opline->result; + if (expr) { + opline->op1 = *expr; + if (offset) { + opline->op2 = *offset; + } else { + SET_UNUSED(opline->op2); + } + } else { + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + } + opline->extended_value = is_ref; +} + + +void zend_do_add_array_element(znode *result, znode *expr, znode *offset, int is_ref CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_ADD_ARRAY_ELEMENT; + opline->result = *result; + opline->op1 = *expr; + if (offset) { + opline->op2 = *offset; + } else { + SET_UNUSED(opline->op2); + } + opline->extended_value = is_ref; +} + + + +void zend_do_add_static_array_element(znode *result, znode *offset, znode *expr) +{ + zval *element; + + ALLOC_ZVAL(element); + *element = expr->u.constant; + if (offset) { + switch (offset->u.constant.type) { + case IS_CONSTANT: + case IS_STRING: + zend_hash_update(result->u.constant.value.ht, offset->u.constant.value.str.val, offset->u.constant.value.str.len+1, &element, sizeof(zval *), NULL); + zval_dtor(&offset->u.constant); + break; + case IS_LONG: + zend_hash_index_update(result->u.constant.value.ht, offset->u.constant.value.lval, &element, sizeof(zval *), NULL); + break; + } + } else { + zend_hash_next_index_insert(result->u.constant.value.ht, &element, sizeof(zval *), NULL); + } +} + + +void zend_do_add_list_element(znode *element CLS_DC) +{ + list_llist_element lle; + + if (element) { + lle.var = *element; + zend_llist_copy(&lle.dimensions, &CG(dimension_llist)); + zend_llist_prepend_element(&CG(list_llist), &lle); + } + (*((int *)CG(dimension_llist).tail->data))++; +} + + +void zend_do_new_list_begin(CLS_D) +{ + int current_dimension = 0; + zend_llist_add_element(&CG(dimension_llist), ¤t_dimension); +} + + +void zend_do_new_list_end(CLS_D) +{ + zend_llist_remove_tail(&CG(dimension_llist)); + (*((int *)CG(dimension_llist).tail->data))++; +} + + +void zend_do_list_init(CLS_D) +{ + zend_llist_init(&CG(list_llist), sizeof(list_llist_element), NULL, 0); + zend_llist_init(&CG(dimension_llist), sizeof(int), NULL, 0); + zend_do_new_list_begin(CLS_C); +} + + +void zend_do_list_end(znode *result, znode *expr CLS_DC) +{ + zend_llist_element *le; + zend_llist_element *dimension; + zend_op *opline; + znode last_container; + + le = CG(list_llist).head; + while (le) { + zend_llist *tmp_dimension_llist = &((list_llist_element *)le->data)->dimensions; + dimension = tmp_dimension_llist->head; + while (dimension) { + opline = get_next_op(CG(active_op_array) CLS_CC); + if (dimension == tmp_dimension_llist->head) { /* first */ + last_container = *expr; + switch(expr->op_type) { + case IS_VAR: + opline->opcode = ZEND_FETCH_DIM_R; + break; + case IS_TMP_VAR: + opline->opcode = ZEND_FETCH_DIM_TMP_VAR; + break; + case IS_CONST: /* fetch_dim_tmp_var will handle this bogus fetch */ + zval_copy_ctor(&expr->u.constant); + opline->opcode = ZEND_FETCH_DIM_TMP_VAR; + break; + } + } else { + opline->opcode = ZEND_FETCH_DIM_R; + } + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = last_container; + opline->op2.op_type = IS_CONST; + opline->op2.u.constant.type = IS_LONG; + opline->op2.u.constant.value.lval = *((int *) dimension->data); + INIT_PZVAL(&opline->op2.u.constant); + opline->extended_value = ZEND_FETCH_ADD_LOCK; + last_container = opline->result; + dimension = dimension->next; + } + ((list_llist_element *) le->data)->value = last_container; + zend_llist_destroy(&((list_llist_element *) le->data)->dimensions); + zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); + zend_do_assign(result, &((list_llist_element *) le->data)->var, &((list_llist_element *) le->data)->value CLS_CC); + CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; + le = le->next; + } + zend_llist_destroy(&CG(dimension_llist)); + zend_llist_destroy(&CG(list_llist)); + *result = *expr; +} + + +void zend_do_fetch_global_or_static_variable(znode *varname, znode *static_assignment, int fetch_type CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + znode lval; + znode result; + + if (fetch_type==ZEND_FETCH_STATIC && static_assignment) { + zval *tmp; + + ALLOC_ZVAL(tmp); + convert_to_string(&varname->u.constant); + *tmp = static_assignment->u.constant; + if (!CG(active_op_array)->static_variables) { + ALLOC_HASHTABLE(CG(active_op_array)->static_variables); + zend_hash_init(CG(active_op_array)->static_variables, 2, NULL, ZVAL_PTR_DTOR, 0); + } + zend_hash_update(CG(active_op_array)->static_variables, varname->u.constant.value.str.val, varname->u.constant.value.str.len+1, &tmp, sizeof(zval *), NULL); + } + + + opline->opcode = ZEND_FETCH_W; /* the default mode must be Write, since fetch_simple_variable() is used to define function arguments */ + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *varname; + SET_UNUSED(opline->op2); + opline->op2.u.fetch_type = fetch_type; + result = opline->result; + + if (varname->op_type == IS_CONST) { + zval_copy_ctor(&varname->u.constant); + } + fetch_simple_variable(&lval, varname, 0 CLS_CC); /* Relies on the fact that the default fetch is BP_VAR_W */ + + zend_do_assign_ref(NULL, &lval, &result CLS_CC); + CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; +} + + +void zend_do_cast(znode *result, znode *expr, int type CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_CAST; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *expr; + SET_UNUSED(opline->op2); + opline->extended_value = type; + *result = opline->result; +} + + +void zend_do_include_or_eval(int type, znode *result, znode *op1 CLS_DC) +{ + zend_do_extended_fcall_begin(CLS_C); + { + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_INCLUDE_OR_EVAL; + opline->result.op_type = IS_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *op1; + SET_UNUSED(opline->op2); + opline->op2.u.constant.value.lval = type; + *result = opline->result; + if (type==ZEND_REQUIRE) { + opline->result.u.EA.type |= EXT_TYPE_UNUSED; + } + } + zend_do_extended_fcall_end(CLS_C); +} + + +void zend_do_indirect_references(znode *result, znode *num_references, znode *variable CLS_DC) +{ + int i; + + zend_do_end_variable_parse(BP_VAR_R, 0 CLS_CC); + for (i=1; iu.constant.value.lval; i++) { + fetch_simple_variable_ex(result, variable, 0, ZEND_FETCH_R CLS_CC); + *variable = *result; + } + zend_do_begin_variable_parse(CLS_C); + fetch_simple_variable(result, variable, 1 CLS_CC); +} + + +void zend_do_unset(znode *variable CLS_DC) +{ + zend_op *last_op; + + last_op = &CG(active_op_array)->opcodes[get_next_op_number(CG(active_op_array))-1]; + + switch (last_op->opcode) { + case ZEND_FETCH_UNSET: + last_op->opcode = ZEND_UNSET_VAR; + break; + case ZEND_FETCH_DIM_UNSET: + case ZEND_FETCH_OBJ_UNSET: + last_op->opcode = ZEND_UNSET_DIM_OBJ; + break; + + } +} + + +void zend_do_isset_or_isempty(int type, znode *result, znode *variable CLS_DC) +{ + zend_op *opline; + + zend_do_end_variable_parse(BP_VAR_IS, 0 CLS_CC); + opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_ISSET_ISEMPTY; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *variable; + opline->op2.u.constant.value.lval = type; + SET_UNUSED(opline->op2); + *result = opline->result; +} + + +void zend_do_foreach_begin(znode *foreach_token, znode *array, znode *open_brackets_token, znode *as_token, int variable CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + /* Preform array reset */ + opline->opcode = ZEND_FE_RESET; + opline->result.op_type = IS_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *array; + SET_UNUSED(opline->op2); + if (variable) { + opline->extended_value = 1; + } else { + opline->extended_value = 0; + } + *open_brackets_token = opline->result; + + zend_stack_push(&CG(foreach_copy_stack), (void *) &opline->result, sizeof(znode)); + + /* save the location of the beginning of the loop (array fetching) */ + foreach_token->u.opline_num = get_next_op_number(CG(active_op_array)); + + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_FE_FETCH; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *open_brackets_token; + SET_UNUSED(opline->op2); + *as_token = opline->result; +} + + +void zend_do_foreach_cont(znode *value, znode *key, znode *as_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + znode result_value, result_key, dummy; + + if (key->op_type != IS_UNUSED) { + znode *tmp; + + /* switch between the key and value... */ + tmp = key; + key = value; + value = tmp; + } + + opline->opcode = ZEND_FETCH_DIM_TMP_VAR; + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.opline_num = get_temporary_variable(CG(active_op_array)); + opline->op1 = *as_token; + opline->op2.op_type = IS_CONST; + opline->op2.u.constant.type = IS_LONG; + opline->op2.u.constant.value.lval = 0; + opline->extended_value = ZEND_FETCH_STANDARD; /* ignored in fetch_dim_tmp_var, but what the hell. */ + result_value = opline->result; + + if (key->op_type != IS_UNUSED) { + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_FETCH_DIM_TMP_VAR; + opline->result.op_type = IS_VAR; + opline->result.u.EA.type = 0; + opline->result.u.opline_num = get_temporary_variable(CG(active_op_array)); + opline->op1 = *as_token; + opline->op2.op_type = IS_CONST; + opline->op2.u.constant.type = IS_LONG; + opline->op2.u.constant.value.lval = 1; + opline->extended_value = ZEND_FETCH_STANDARD; /* ignored in fetch_dim_tmp_var, but what the hell. */ + result_key = opline->result; + } + + zend_do_assign(&dummy, value, &result_value CLS_CC); + CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; + if (key->op_type != IS_UNUSED) { + zend_do_assign(&dummy, key, &result_key CLS_CC); + CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; + } + zend_do_free(as_token CLS_CC); + + do_begin_loop(CLS_C); + INC_BPC(CG(active_op_array)); +} + + +void zend_do_foreach_end(znode *foreach_token, znode *open_brackets_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMP; + opline->op1.u.opline_num = foreach_token->u.opline_num; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + + CG(active_op_array)->opcodes[foreach_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); + + do_end_loop(foreach_token->u.opline_num CLS_CC); + + generate_free_foreach_copy(open_brackets_token CLS_CC); + + zend_stack_del_top(&CG(foreach_copy_stack)); + + DEC_BPC(CG(active_op_array)); +} + + +void zend_do_declare_begin(CLS_D) +{ + zend_stack_push(&CG(declare_stack), &CG(declarables), sizeof(zend_declarables)); +} + + +void zend_do_declare_stmt(znode *var, znode *val CLS_DC) +{ + convert_to_string(&var->u.constant); + + if (!zend_binary_strcasecmp(var->u.constant.value.str.val, var->u.constant.value.str.len, "ticks", sizeof("ticks")-1)) { + convert_to_long(&val->u.constant); + CG(declarables).ticks = val->u.constant; + } + zval_dtor(&var->u.constant); +} + + +void zend_do_declare_end(CLS_D) +{ + zend_declarables *declarables; + + zend_stack_top(&CG(declare_stack), (void **) &declarables); + CG(declarables) = *declarables; +} + + +void zend_do_end_heredoc(CLS_D) +{ + int opline_num = get_next_op_number(CG(active_op_array))-1; + zend_op *opline = &CG(active_op_array)->opcodes[opline_num]; + + if (opline->opcode != ZEND_ADD_STRING) { + return; + } + + opline->op2.u.constant.value.str.val[(opline->op2.u.constant.value.str.len--)-1] = 0; + if (opline->op2.u.constant.value.str.len>0) { + if (opline->op2.u.constant.value.str.val[opline->op2.u.constant.value.str.len-1]=='\r') { + opline->op2.u.constant.value.str.val[(opline->op2.u.constant.value.str.len--)-1] = 0; + } + } +} + + +void zend_do_exit(znode *result, znode *message CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_EXIT; + opline->op1 = *message; + SET_UNUSED(opline->op2); + + result->op_type = IS_CONST; + result->u.constant.type = IS_BOOL; + result->u.constant.value.lval = 1; +} + + +void zend_do_begin_silence(znode *strudel_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_BEGIN_SILENCE; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + *strudel_token = opline->result; +} + + +void zend_do_end_silence(znode *strudel_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_END_SILENCE; + opline->op1 = *strudel_token; + SET_UNUSED(opline->op2); +} + + +void zend_do_begin_qm_op(znode *cond, znode *qm_token CLS_DC) +{ + int jmpz_op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline; + + opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_JMPZ; + opline->op1 = *cond; + SET_UNUSED(opline->op2); + opline->op2.u.opline_num = jmpz_op_number; + *qm_token = opline->op2; + + INC_BPC(CG(active_op_array)); +} + + +void zend_do_qm_true(znode *true_value, znode *qm_token, znode *colon_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + CG(active_op_array)->opcodes[qm_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array))+1; /* jmp over the ZEND_JMP */ + + opline->opcode = ZEND_QM_ASSIGN; + opline->result.op_type = IS_TMP_VAR; + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->op1 = *true_value; + SET_UNUSED(opline->op2); + + *qm_token = opline->result; + colon_token->u.opline_num = get_next_op_number(CG(active_op_array)); + + opline = get_next_op(CG(active_op_array) CLS_CC); + opline->opcode = ZEND_JMP; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); +} + + +void zend_do_qm_false(znode *result, znode *false_value, znode *qm_token, znode *colon_token CLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_QM_ASSIGN; + opline->result = *qm_token; + opline->op1 = *false_value; + SET_UNUSED(opline->op2); + + CG(active_op_array)->opcodes[colon_token->u.opline_num].op1.u.opline_num = get_next_op_number(CG(active_op_array)); + + *result = opline->result; + + DEC_BPC(CG(active_op_array)); +} + + +void zend_do_extended_info(CLS_D) +{ + zend_op *opline; + + if (!CG(extended_info)) { + return; + } + + opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_EXT_STMT; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); +} + + +void zend_do_extended_fcall_begin(CLS_D) +{ + zend_op *opline; + + if (!CG(extended_info)) { + return; + } + + opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_EXT_FCALL_BEGIN; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); +} + + +void zend_do_extended_fcall_end(CLS_D) +{ + zend_op *opline; + + if (!CG(extended_info)) { + return; + } + + opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_EXT_FCALL_END; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); +} + +void zend_do_ticks(CLS_D) +{ + if (CG(declarables).ticks.value.lval) { + zend_op *opline = get_next_op(CG(active_op_array) CLS_CC); + + opline->opcode = ZEND_TICKS; + opline->op1.u.constant = CG(declarables).ticks; + opline->op1.op_type = IS_CONST; + SET_UNUSED(opline->op2); + } +} + + +int zendlex(znode *zendlval CLS_DC) +{ + int retval; + + zendlval->u.constant.type = IS_LONG; + retval = lex_scan(&zendlval->u.constant CLS_CC); + switch(retval) { + case T_COMMENT: + case T_OPEN_TAG: + case T_WHITESPACE: + retval = zendlex(zendlval CLS_CC); + break; + case T_CLOSE_TAG: + retval = ';'; /* implicit ; */ + break; + case T_OPEN_TAG_WITH_ECHO: + retval = T_ECHO; + break; + case T_END_HEREDOC: + efree(zendlval->u.constant.value.str.val); + break; + } + + INIT_PZVAL(&zendlval->u.constant); + zendlval->op_type = IS_CONST; + return retval; +} diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h new file mode 100644 index 0000000000..6a5680d1fc --- /dev/null +++ b/Zend/zend_execute.h @@ -0,0 +1,213 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#ifndef ZEND_EXECUTE_H +#define ZEND_EXECUTE_H + +#include "zend_compile.h" +#include "zend_hash.h" +#include "zend_variables.h" +#include "zend_execute_locks.h" + +typedef union _temp_variable { + zval tmp_var; + struct { + zval **ptr_ptr; + zval *ptr; + } var; + struct { + zval tmp_var; /* a dummy */ + + union { + struct { + zval *str; + int offset; + } str_offset; + zend_property_reference overloaded_element; + } data; + + unsigned char type; + } EA; +} temp_variable; + + +ZEND_API extern void (*zend_execute)(zend_op_array *op_array ELS_DC); + +void init_executor(CLS_D ELS_DC); +void shutdown_executor(ELS_D); +void execute(zend_op_array *op_array ELS_DC); +ZEND_API int zend_is_true(zval *op); +static inline void safe_free_zval_ptr(zval *p) +{ + ELS_FETCH(); + + if (p!=EG(uninitialized_zval_ptr)) { + FREE_ZVAL(p); + } +} + +ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name CLS_DC ELS_DC); +static inline int i_zend_is_true(zval *op) +{ + int result; + + switch (op->type) { + case IS_NULL: + result = 0; + break; + case IS_LONG: + case IS_BOOL: + case IS_RESOURCE: + result = (op->value.lval?1:0); + break; + case IS_DOUBLE: + result = (op->value.dval ? 1 : 0); + break; + case IS_STRING: + if (op->value.str.len == 0 + || (op->value.str.len==1 && op->value.str.val[0]=='0')) { + result = 0; + } else { + result = 1; + } + break; + case IS_ARRAY: + result = (zend_hash_num_elements(op->value.ht)?1:0); + break; + case IS_OBJECT: + result = (zend_hash_num_elements(op->value.obj.properties)?1:0); + break; + default: + result = 0; + break; + } + return result; +} + +ZEND_API int zval_update_constant(zval **pp, void *arg); + +/* dedicated Zend executor functions - do not use! */ +static inline void zend_ptr_stack_clear_multiple(ELS_D) +{ + void **p = EG(argument_stack).top_element-2; + int delete_count = (ulong) *p; + + EG(argument_stack).top -= (delete_count+2); + while (--delete_count>=0) { + zval_ptr_dtor((zval **) --p); + } + EG(argument_stack).top_element = p; +} + +static inline int zend_ptr_stack_get_arg(int requested_arg, void **data ELS_DC) +{ + void **p = EG(argument_stack).top_element-2; + int arg_count = (ulong) *p; + + if (requested_arg>arg_count) { + return FAILURE; + } + *data = (p-arg_count+requested_arg-1); + return SUCCESS; +} + +#if SUPPORT_INTERACTIVE +void execute_new_code(CLS_D); +#endif + + +/* services */ +ZEND_API char *get_active_function_name(void); +ZEND_API char *zend_get_executed_filename(ELS_D); +ZEND_API uint zend_get_executed_lineno(ELS_D); +ZEND_API zend_bool zend_is_executing(void); + +ZEND_API void zend_set_timeout(long seconds); +ZEND_API void zend_unset_timeout(void); +ZEND_API void zend_timeout(int dummy); + +#ifdef ZEND_WIN32 +void zend_init_timeout_thread(); +void zend_shutdown_timeout_thread(); +#define WM_REGISTER_ZEND_TIMEOUT (WM_USER+1) +#define WM_UNREGISTER_ZEND_TIMEOUT (WM_USER+2) +#endif + +#define zendi_zval_copy_ctor(p) zval_copy_ctor(&(p)) +#define zendi_zval_dtor(p) zval_dtor(&(p)) + +#define active_opline (*EG(opline_ptr)) + +static inline void zend_assign_to_variable_reference(znode *result, zval **variable_ptr_ptr, zval **value_ptr_ptr, temp_variable *Ts ELS_DC) +{ + zval *variable_ptr; + zval *value_ptr; + + if (!value_ptr_ptr || !variable_ptr_ptr) { + zend_error(E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); + return; + } + + variable_ptr = *variable_ptr_ptr; + value_ptr = *value_ptr_ptr; + + if (variable_ptr == EG(error_zval_ptr) || value_ptr==EG(error_zval_ptr)) { + variable_ptr_ptr = &EG(uninitialized_zval_ptr); +/* } else if (variable_ptr==&EG(uninitialized_zval) || variable_ptr!=value_ptr) { */ + } else if (variable_ptr_ptr != value_ptr_ptr) { + variable_ptr->refcount--; + if (variable_ptr->refcount==0) { + zendi_zval_dtor(*variable_ptr); + FREE_ZVAL(variable_ptr); + } + + if (!PZVAL_IS_REF(value_ptr)) { + /* break it away */ + value_ptr->refcount--; + if (value_ptr->refcount>0) { + ALLOC_ZVAL(*value_ptr_ptr); + **value_ptr_ptr = *value_ptr; + value_ptr = *value_ptr_ptr; + zendi_zval_copy_ctor(*value_ptr); + } + value_ptr->refcount = 1; + value_ptr->is_ref = 1; + } + + *variable_ptr_ptr = value_ptr; + value_ptr->refcount++; + } else { + if (variable_ptr->refcount>1) { /* we need to break away */ + SEPARATE_ZVAL(variable_ptr_ptr); + } + (*variable_ptr_ptr)->is_ref = 1; + } + + if (result && !(result->u.EA.type & EXT_TYPE_UNUSED)) { + Ts[result->u.var].var.ptr_ptr = variable_ptr_ptr; + SELECTIVE_PZVAL_LOCK(*variable_ptr_ptr, result); + AI_USE_PTR(Ts[result->u.var].var); + } +} + +#define IS_OVERLOADED_OBJECT 1 +#define IS_STRING_OFFSET 2 + +#endif /* ZEND_EXECUTE_H */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c new file mode 100644 index 0000000000..9834499195 --- /dev/null +++ b/Zend/zend_execute_API.c @@ -0,0 +1,708 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#include +#include + +#include "zend.h" +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_API.h" +#include "zend_ptr_stack.h" +#include "zend_constants.h" +#include "zend_extensions.h" +#include "zend_execute_locks.h" +#if HAVE_SYS_TIME_H +#include +#endif + + +ZEND_API void (*zend_execute)(zend_op_array *op_array ELS_DC); + +#ifdef ZEND_WIN32 +#include +/* true global */ +static WNDCLASS wc; +static HWND timeout_window; +static HANDLE timeout_thread_event; +static DWORD timeout_thread_id; +static int timeout_thread_initialized=0; +#endif + + +#if ZEND_DEBUG +static void (*original_sigsegv_handler)(int); +static void zend_handle_sigsegv(int dummy) +{ + fflush(stdout); + fflush(stderr); + if (original_sigsegv_handler==zend_handle_sigsegv) { + signal(SIGSEGV, original_sigsegv_handler); + } else { + signal(SIGSEGV, SIG_DFL); + } + { + ELS_FETCH(); + + fprintf(stderr, "SIGSEGV caught on opcode %d on opline %d of %s() at %s:%d\n\n", + active_opline->opcode, + active_opline-EG(active_op_array)->opcodes, + get_active_function_name(), + zend_get_executed_filename(ELS_C), + zend_get_executed_lineno(ELS_C)); + } + if (original_sigsegv_handler!=zend_handle_sigsegv) { + original_sigsegv_handler(dummy); + } +} +#endif + + +static void zend_extension_activator(zend_extension *extension) +{ + if (extension->activate) { + extension->activate(); + } +} + + +static void zend_extension_deactivator(zend_extension *extension) +{ + if (extension->deactivate) { + extension->deactivate(); + } +} + + +static int is_not_internal_function(zend_function *function) +{ + return(function->type != ZEND_INTERNAL_FUNCTION); +} + + +static int is_not_internal_class(zend_class_entry *ce) +{ + return(ce->type != ZEND_INTERNAL_CLASS); +} + + +void init_executor(CLS_D ELS_DC) +{ + INIT_ZVAL(EG(uninitialized_zval)); + INIT_ZVAL(EG(error_zval)); + EG(uninitialized_zval_ptr)=&EG(uninitialized_zval); + EG(error_zval_ptr)=&EG(error_zval); + zend_ptr_stack_init(&EG(arg_types_stack)); +/* destroys stack frame, therefore makes core dumps worthless */ +#if 0&&ZEND_DEBUG + original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv); +#endif + EG(return_value_ptr_ptr) = &EG(global_return_value_ptr); + EG(global_return_value_ptr) = NULL; + + EG(symtable_cache_ptr) = EG(symtable_cache)-1; + EG(symtable_cache_limit)=EG(symtable_cache)+SYMTABLE_CACHE_SIZE-1; + EG(no_extensions)=0; + + EG(function_table) = CG(function_table); + EG(class_table) = CG(class_table); + + EG(in_execution) = 0; + + zend_ptr_stack_init(&EG(argument_stack)); + + zend_hash_init(&EG(symbol_table), 50, NULL, ZVAL_PTR_DTOR, 0); + EG(active_symbol_table) = &EG(symbol_table); + + zend_llist_apply(&zend_extensions, (void (*)(void *)) zend_extension_activator); + EG(opline_ptr) = NULL; + EG(garbage_ptr) = 0; + + zend_hash_init(&EG(included_files), 5, NULL, NULL, 0); + + EG(ticks_count) = 0; + + EG(user_error_handler) = NULL; + + zend_ptr_stack_init(&EG(user_error_handlers)); + + EG(orig_error_reporting) = EG(error_reporting); + +#ifdef ZEND_WIN32 + EG(timed_out) = 0; +#endif +} + + +void shutdown_executor(ELS_D) +{ + zend_ptr_stack_destroy(&EG(arg_types_stack)); + + while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + zend_hash_destroy(*EG(symtable_cache_ptr)); + efree(*EG(symtable_cache_ptr)); + EG(symtable_cache_ptr)--; + } + zend_llist_apply(&zend_extensions, (void (*)(void *)) zend_extension_deactivator); + + zend_hash_destroy(&EG(symbol_table)); + + while (EG(garbage_ptr)--) { + if (EG(garbage)[EG(garbage_ptr)]->refcount==1) { + zval_ptr_dtor(&EG(garbage)[EG(garbage_ptr)]); + } + } + + zend_ptr_stack_destroy(&EG(argument_stack)); + + /* Destroy all op arrays */ + zend_hash_apply(EG(function_table), (int (*)(void *)) is_not_internal_function); + zend_hash_apply(EG(class_table), (int (*)(void *)) is_not_internal_class); + + zend_destroy_rsrc_list(ELS_C); /* must be destroyed after the main symbol table and + * op arrays are destroyed. + */ + + clean_non_persistent_constants(); +#if ZEND_DEBUG + signal(SIGSEGV, original_sigsegv_handler); +#endif + + zend_hash_destroy(&EG(included_files)); + + if (EG(user_error_handler)) { + zval_dtor(EG(user_error_handler)); + FREE_ZVAL(EG(user_error_handler)); + } + + zend_ptr_stack_clean(&EG(user_error_handlers), ZVAL_DESTRUCTOR, 1); + zend_ptr_stack_destroy(&EG(user_error_handlers)); + + EG(error_reporting) = EG(orig_error_reporting); +} + + +ZEND_API char *get_active_function_name() +{ + ELS_FETCH(); + + switch(EG(function_state_ptr)->function->type) { + case ZEND_USER_FUNCTION: { + char *function_name = ((zend_op_array *) EG(function_state_ptr)->function)->function_name; + + if (function_name) { + return function_name; + } else { + return "main"; + } + } + break; + case ZEND_INTERNAL_FUNCTION: + return ((zend_internal_function *) EG(function_state_ptr)->function)->function_name; + break; + default: + return NULL; + } +} + + +ZEND_API char *zend_get_executed_filename(ELS_D) +{ + if (EG(active_op_array)) { + return EG(active_op_array)->filename; + } else { + return "[no active file]"; + } +} + + +ZEND_API uint zend_get_executed_lineno(ELS_D) +{ + if (EG(opline_ptr)) { + return active_opline->lineno; + } else { + return 0; + } +} + + +ZEND_API zend_bool zend_is_executing() +{ + ELS_FETCH(); + + return EG(in_execution); +} + + +ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) +{ +#if DEBUG_ZEND>=2 + printf("Reducing refcount for %x (%x): %d->%d\n", *zval_ptr, zval_ptr, (*zval_ptr)->refcount, (*zval_ptr)->refcount-1); +#endif + (*zval_ptr)->refcount--; + if ((*zval_ptr)->refcount==0) { + zval_dtor(*zval_ptr); + safe_free_zval_ptr(*zval_ptr); + } else if (((*zval_ptr)->refcount == 1) && ((*zval_ptr)->type != IS_OBJECT)) { + (*zval_ptr)->is_ref = 0; + } +} + + +ZEND_API int zend_is_true(zval *op) +{ + return i_zend_is_true(op); +} + + +ZEND_API int zval_update_constant(zval **pp, void *arg) +{ + zval *p = *pp; + zend_bool inline_change = (zend_bool) (unsigned long) arg; + + if (p->type == IS_CONSTANT) { + zval c; + int refcount; + + SEPARATE_ZVAL(pp); + p = *pp; + + refcount = p->refcount; + + if (!zend_get_constant(p->value.str.val, p->value.str.len, &c)) { + zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'", + p->value.str.val, + p->value.str.val); + p->type = IS_STRING; + if (!inline_change) { + zval_copy_ctor(p); + } + } else { + if (inline_change) { + STR_FREE(p->value.str.val); + } + *p = c; + } + INIT_PZVAL(p); + p->refcount = refcount; + } else if (p->type == IS_CONSTANT_ARRAY) { + SEPARATE_ZVAL(pp); + p = *pp; + p->type = IS_ARRAY; + zend_hash_apply_with_argument(p->value.ht, (int (*)(void *,void *)) zval_update_constant, (void *) 1); + } + return 0; +} + + +int call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, int param_count, zval *params[]) +{ + zval ***params_array = (zval ***) emalloc(sizeof(zval **)*param_count); + int i; + int ex_retval; + zval *local_retval_ptr; + + for (i=0; itype==IS_ARRAY) { /* assume array($obj, $name) couple */ + zval **tmp_object_ptr, **tmp_real_function_name; + + if (zend_hash_index_find(function_name->value.ht, 0, (void **) &tmp_object_ptr)==FAILURE) { + return FAILURE; + } + if (zend_hash_index_find(function_name->value.ht, 1, (void **) &tmp_real_function_name)==FAILURE) { + return FAILURE; + } + function_name = *tmp_real_function_name; + SEPARATE_ZVAL_IF_NOT_REF(tmp_object_ptr); + object = *tmp_object_ptr; + object->is_ref = 1; + } + + if (object) { + if (object->type != IS_OBJECT) { + return FAILURE; + } + function_table = &object->value.obj.ce->function_table; + } + + if (function_name->type!=IS_STRING) { + return FAILURE; + } + + original_function_state_ptr = EG(function_state_ptr); + zend_str_tolower(function_name->value.str.val, function_name->value.str.len); + if (zend_hash_find(function_table, function_name->value.str.val, function_name->value.str.len+1, (void **) &function_state.function)==FAILURE) { + return FAILURE; + } + + for (i=0; icommon.arg_types + && icommon.arg_types[0] + && function_state.function->common.arg_types[i+1]==BYREF_FORCE + && !PZVAL_IS_REF(*params[i])) { + if ((*params[i])->refcount>1) { + zval *new_zval; + + if (no_separation) { + return FAILURE; + } + ALLOC_ZVAL(new_zval); + *new_zval = **params[i]; + zval_copy_ctor(new_zval); + new_zval->refcount = 1; + (*params[i])->refcount--; + *params[i] = new_zval; + } + (*params[i])->refcount++; + (*params[i])->is_ref = 1; + param = *params[i]; + } else if (*params[i] != &EG(uninitialized_zval)) { + (*params[i])->refcount++; + param = *params[i]; + } else { + ALLOC_ZVAL(param); + *param = **(params[i]); + INIT_PZVAL(param); + } + zend_ptr_stack_push(&EG(argument_stack), param); + } + + zend_ptr_stack_n_push(&EG(argument_stack), 2, (void *) (long) param_count, NULL); + + if (function_state.function->type == ZEND_USER_FUNCTION) { + calling_symbol_table = EG(active_symbol_table); + if (symbol_table) { + EG(active_symbol_table) = symbol_table; + } else { + ALLOC_HASHTABLE(EG(active_symbol_table)); + zend_hash_init(EG(active_symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0); + } + if (object) { + zval *dummy, **this_ptr; + + ALLOC_ZVAL(dummy); + INIT_ZVAL(*dummy); + + zend_hash_update(EG(active_symbol_table), "this", sizeof("this"), &dummy, sizeof(zval *), (void **) &this_ptr); + zend_assign_to_variable_reference(NULL, this_ptr, &object, NULL ELS_CC); + } + original_return_value = EG(return_value_ptr_ptr); + original_op_array = EG(active_op_array); + EG(return_value_ptr_ptr) = retval_ptr_ptr; + EG(active_op_array) = (zend_op_array *) function_state.function; + original_opline_ptr = EG(opline_ptr); + zend_execute(EG(active_op_array) ELS_CC); + if (!symbol_table) { + zend_hash_destroy(EG(active_symbol_table)); + FREE_HASHTABLE(EG(active_symbol_table)); + } + EG(active_symbol_table) = calling_symbol_table; + EG(active_op_array) = original_op_array; + EG(return_value_ptr_ptr)=original_return_value; + EG(opline_ptr) = original_opline_ptr; + } else { + ALLOC_INIT_ZVAL(*retval_ptr_ptr); + ((zend_internal_function *) function_state.function)->handler(param_count, *retval_ptr_ptr, object, 1 ELS_CC); + INIT_PZVAL(*retval_ptr_ptr); + } + zend_ptr_stack_clear_multiple(ELS_C); + EG(function_state_ptr) = original_function_state_ptr; + + return SUCCESS; +} + + +ZEND_API int zend_eval_string(char *str, zval *retval_ptr, char *string_name CLS_DC ELS_DC) +{ + zval pv; + zend_op_array *new_op_array; + zend_op_array *original_active_op_array = EG(active_op_array); + zend_function_state *original_function_state_ptr = EG(function_state_ptr); + int original_handle_op_arrays; + int retval; + + if (retval_ptr) { + pv.value.str.len = strlen(str)+sizeof("return ;")-1; + pv.value.str.val = emalloc(pv.value.str.len+1); + strcpy(pv.value.str.val, "return "); + strcat(pv.value.str.val, str); + strcat(pv.value.str.val, " ;"); + } else { + pv.value.str.len = strlen(str); + pv.value.str.val = estrndup(str, pv.value.str.len); + } + pv.type = IS_STRING; + + /*printf("Evaluating '%s'\n", pv.value.str.val);*/ + + original_handle_op_arrays = CG(handle_op_arrays); + CG(handle_op_arrays) = 0; + new_op_array = compile_string(&pv, string_name CLS_CC); + CG(handle_op_arrays) = original_handle_op_arrays; + + if (new_op_array) { + zval *local_retval_ptr=NULL; + zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); + zend_op **original_opline_ptr = EG(opline_ptr); + + EG(return_value_ptr_ptr) = &local_retval_ptr; + EG(active_op_array) = new_op_array; + EG(no_extensions)=1; + + zend_execute(new_op_array ELS_CC); + + if (local_retval_ptr) { + if (retval_ptr) { + COPY_PZVAL_TO_ZVAL(*retval_ptr, local_retval_ptr); + } else { + zval_ptr_dtor(&local_retval_ptr); + } + } else { + if (retval_ptr) { + INIT_ZVAL(*retval_ptr); + } + } + + EG(no_extensions)=0; + EG(opline_ptr) = original_opline_ptr; + EG(active_op_array) = original_active_op_array; + EG(function_state_ptr) = original_function_state_ptr; + destroy_op_array(new_op_array); + efree(new_op_array); + EG(return_value_ptr_ptr) = original_return_value_ptr_ptr; + retval = SUCCESS; + } else { + retval = FAILURE; + } + zval_dtor(&pv); + return retval; +} + + + +#if SUPPORT_INTERACTIVE +void execute_new_code(CLS_D) +{ + ELS_FETCH(); + + if (!EG(interactive) + || CG(active_op_array)->backpatch_count>0 + || CG(active_op_array)->function_name + || CG(active_op_array)->type!=ZEND_USER_FUNCTION) { + return; + } + CG(active_op_array)->start_op_number = CG(active_op_array)->last_executed_op_number; + CG(active_op_array)->end_op_number = CG(active_op_array)->last; + EG(active_op_array) = CG(active_op_array); + zend_execute(CG(active_op_array) ELS_CC); + zval_ptr_dtor(EG(return_value_ptr_ptr)); + CG(active_op_array)->start_op_number = CG(active_op_array)->last_executed_op_number; +} +#endif + + +ZEND_API void zend_timeout(int dummy) +{ + ELS_FETCH(); + + /* is there any point in this? we're terminating the request anyway... + PLS_FETCH(); + + PG(connection_status) |= PHP_CONNECTION_TIMEOUT; + */ + zend_error(E_ERROR, "Maximum execution time of %d second%s exceeded", + EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s"); +} + + +#ifdef ZEND_WIN32 +static LRESULT CALLBACK zend_timeout_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_REGISTER_ZEND_TIMEOUT: + /* wParam is the thread id pointer, lParam is the timeout amount in seconds */ + if (lParam==0) { + KillTimer(timeout_window, wParam); + } else { + SetTimer(timeout_window, wParam, lParam*1000, NULL); + } + break; + case WM_UNREGISTER_ZEND_TIMEOUT: + /* wParam is the thread id pointer */ + KillTimer(timeout_window, wParam); + break; + case WM_TIMER: { +#ifdef ZTS + zend_executor_globals *executor_globals; + + executor_globals = ts_resource_ex(executor_globals_id, &wParam); + if (!executor_globals) { + /* Thread died before receiving its timeout? */ + break; + } +#endif + KillTimer(timeout_window, wParam); + EG(timed_out) = 1; + } + break; + default: + return DefWindowProc(hWnd,message,wParam,lParam); + } + return 0; +} + + + +static unsigned __stdcall timeout_thread_proc(void *pArgs) +{ + MSG message; + + wc.style=0; + wc.lpfnWndProc = zend_timeout_WndProc; + wc.cbClsExtra=0; + wc.cbWndExtra=0; + wc.hInstance=NULL; + wc.hIcon=NULL; + wc.hCursor=NULL; + wc.hbrBackground=(HBRUSH)(COLOR_BACKGROUND + 5); + wc.lpszMenuName=NULL; + wc.lpszClassName = "Zend Timeout Window"; + if(!RegisterClass(&wc)) { + return -1; + } + timeout_window = CreateWindow(wc.lpszClassName, wc.lpszClassName, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); + SetEvent(timeout_thread_event); + while (GetMessage(&message, NULL, 0, 0)) { + SendMessage(timeout_window, message.message, message.wParam, message.lParam); + if (message.message == WM_QUIT) { + break; + } + } + DestroyWindow(timeout_window); + UnregisterClass(wc.lpszClassName, NULL); + return 0; +} + + +void zend_init_timeout_thread() +{ + timeout_thread_event = CreateEvent(NULL, FALSE, FALSE, NULL); + _beginthreadex(NULL, 0, timeout_thread_proc, NULL, 0, &timeout_thread_id); + WaitForSingleObject(timeout_thread_event, INFINITE); +} + + +void zend_shutdown_timeout_thread() +{ + if (!timeout_thread_initialized) { + return; + } + PostThreadMessage(timeout_thread_id, WM_QUIT, 0, 0); +} + +#endif + +/* This one doesn't exists on QNX */ +#ifndef SIGPROF +#define SIGPROF 27 +#endif + +void zend_set_timeout(long seconds) +{ + ELS_FETCH(); + + EG(timeout_seconds) = seconds; +#ifdef ZEND_WIN32 + if (timeout_thread_initialized==0 && InterlockedIncrement(&timeout_thread_initialized)==1) { + /* We start up this process-wide thread here and not in zend_startup(), because if Zend + * is initialized inside a DllMain(), you're not supposed to start threads from it. + */ + zend_init_timeout_thread(); + } + PostThreadMessage(timeout_thread_id, WM_REGISTER_ZEND_TIMEOUT, (WPARAM) GetCurrentThreadId(), (LPARAM) seconds); +#else +# ifdef HAVE_SETITIMER + { + struct itimerval t_r; /* timeout requested */ + sigset_t sigset; + + t_r.it_value.tv_sec = seconds; + t_r.it_value.tv_usec = t_r.it_interval.tv_sec = t_r.it_interval.tv_usec = 0; + + setitimer(ITIMER_PROF, &t_r, NULL); + signal(SIGPROF, zend_timeout); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPROF); + sigprocmask(SIG_UNBLOCK,&sigset,NULL); + } +# endif +#endif +} + + +void zend_unset_timeout(void) +{ + ELS_FETCH(); + +#ifdef ZEND_WIN32 + PostThreadMessage(timeout_thread_id, WM_UNREGISTER_ZEND_TIMEOUT, (WPARAM) GetCurrentThreadId(), (LPARAM) 0); +#else +# ifdef HAVE_SETITIMER + { + struct itimerval no_timeout; + + no_timeout.it_value.tv_sec = no_timeout.it_value.tv_usec = no_timeout.it_interval.tv_sec = no_timeout.it_interval.tv_usec = 0; + + setitimer(ITIMER_PROF, &no_timeout, NULL); + } +# endif +#endif +} diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c new file mode 100644 index 0000000000..8bcfe946e0 --- /dev/null +++ b/Zend/zend_hash.c @@ -0,0 +1,1278 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#include "zend.h" + +#include + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_LIMITS_H +# include +#endif + +#define HANDLE_NUMERIC(key, length, func) { \ + register char *tmp=key; \ + \ + if ((*tmp>='0' && *tmp<='9')) do { /* possibly a numeric index */ \ + char *end=tmp+length-1; \ + ulong idx; \ + \ + if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */ \ + break; \ + } \ + while (tmp='0' && *tmp<='9')) { \ + break; \ + } \ + tmp++; \ + } \ + if (tmp==end && *tmp=='\0') { /* a numeric index */ \ + idx = strtol(key, NULL, 10); \ + if (idx!=LONG_MAX) { \ + return func; \ + } \ + } \ + } while(0); \ +} + + +#define CONNECT_TO_BUCKET_DLLIST(element, list_head) \ + (element)->pNext = (list_head); \ + (element)->pLast = NULL; \ + if ((element)->pNext) { \ + (element)->pNext->pLast = (element); \ + } + +#define CONNECT_TO_GLOBAL_DLLIST(element, ht) \ + (element)->pListLast = (ht)->pListTail; \ + (ht)->pListTail = (element); \ + (element)->pListNext = NULL; \ + if ((element)->pListLast != NULL) { \ + (element)->pListLast->pListNext = (element); \ + } \ + if (!(ht)->pListHead) { \ + (ht)->pListHead = (element); \ + } \ + if ((ht)->pInternalPointer == NULL) { \ + (ht)->pInternalPointer = (element); \ + } + +#if ZEND_DEBUG +#define HT_OK 0 +#define HT_IS_DESTROYING 1 +#define HT_DESTROYED 2 +#define HT_CLEANING 3 + +static void _zend_is_inconsistent(HashTable *ht, char *file, int line) +{ + if (ht->inconsistent==HT_OK) { + return; + } + switch (ht->inconsistent) { + case HT_IS_DESTROYING: + zend_output_debug_string(1, "%s(%d) : ht=0x%08x is being destroyed", file, line, ht); + break; + case HT_DESTROYED: + zend_output_debug_string(1, "%s(%d) : ht=0x%08x is already destroyed", file, line, ht); + break; + case HT_CLEANING: + zend_output_debug_string(1, "%s(%d) : ht=0x%08x is being cleaned", file, line, ht); + break; + } + zend_bailout(); +} +#define IS_CONSISTENT(a) _zend_is_inconsistent(a,__FILE__,__LINE__); +#define SET_INCONSISTENT(n) ht->inconsistent = n; +#else +#define IS_CONSISTENT(a) +#define SET_INCONSISTENT(n) +#endif + +#define HASH_APPLY_BEGIN(ht) \ + if ((ht)->bApplyProtection) { \ + if ((ht)->nApplyCount>=3) { \ + zend_error(E_WARNING, "Nesting level too deep - recursive dependency?"); \ + return; \ + } \ + (ht)->nApplyCount++; \ + } + +#define HASH_APPLY_END(ht) \ + (ht)->nApplyCount--; + + +/* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */ +static uint PrimeNumbers[] = + {5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793, 2097397, 4194103, 8388857, 16777447, 33554201, 67108961, 134217487, 268435697, 536870683, 1073741621, 2147483399}; +static uint nNumPrimeNumbers = sizeof(PrimeNumbers) / sizeof(uint); + +#define ZEND_HASH_IF_FULL_DO_RESIZE(ht) \ + if ((ht)->nNumOfElements > (ht)->nTableSize) { \ + zend_hash_do_resize(ht); \ + } + +static int zend_hash_do_resize(HashTable *ht); + + +ZEND_API ulong hashpjw(char *arKey, uint nKeyLength) +{ + ulong h = 0, g; + char *arEnd=arKey+nKeyLength; + + while (arKey < arEnd) { + h = (h << 4) + *arKey++; + if ((g = (h & 0xF0000000))) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + return h; +} + + +#define UPDATE_DATA(ht, p, pData, nDataSize) \ + if (nDataSize == sizeof(void*)) { \ + if (!(p)->pDataPtr) { \ + pefree((p)->pData, (ht)->persistent); \ + } \ + memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \ + (p)->pData = &(p)->pDataPtr; \ + } else { \ + if ((p)->pDataPtr) { \ + (p)->pData = (void *) pemalloc(nDataSize, (ht)->persistent); \ + (p)->pDataPtr=NULL; \ + } \ + memcpy((p)->pData, pData, nDataSize); \ + } + +#define INIT_DATA(ht, p, pData, nDataSize); \ + if (nDataSize == sizeof(void*)) { \ + memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \ + (p)->pData = &(p)->pDataPtr; \ + } else { \ + (p)->pData = (void *) pemalloc(nDataSize, (ht)->persistent); \ + if (!(p)->pData) { \ + pefree(p, (ht)->persistent); \ + return FAILURE; \ + } \ + memcpy((p)->pData, pData, nDataSize); \ + (p)->pDataPtr=NULL; \ + } + + +ZEND_API int zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, int persistent) +{ + uint i; + + SET_INCONSISTENT(HT_OK); + + for (i = 0; i < nNumPrimeNumbers; i++) { + if (nSize <= PrimeNumbers[i]) { + nSize = PrimeNumbers[i]; + ht->nHashSizeIndex = i; + break; + } + } + if (i == nNumPrimeNumbers) { /* This shouldn't really happen unless the ask for a ridiculous size */ + nSize = PrimeNumbers[i - 1]; + ht->nHashSizeIndex = i - 1; + } + + /* Uses ecalloc() so that Bucket* == NULL */ + ht->arBuckets = (Bucket **) pecalloc(nSize, sizeof(Bucket *), persistent); + + if (!ht->arBuckets) { + return FAILURE; + } + if (pHashFunction == NULL) { + ht->pHashFunction = hashpjw; + } else { + ht->pHashFunction = pHashFunction; + } + ht->pDestructor = pDestructor; + ht->nTableSize = nSize; + ht->pListHead = NULL; + ht->pListTail = NULL; + ht->nNumOfElements = 0; + ht->nNextFreeElement = 0; + ht->pInternalPointer = NULL; + ht->persistent = persistent; + ht->nApplyCount = 0; + ht->bApplyProtection = 1; + return SUCCESS; +} + + +ZEND_API int zend_hash_init_ex(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, int persistent, zend_bool bApplyProtection) +{ + int retval = zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent); + + ht->bApplyProtection = bApplyProtection; + return retval; +} + + +ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection) +{ + ht->bApplyProtection = bApplyProtection; +} + + + +ZEND_API int zend_hash_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag) +{ + ulong h; + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + if (nKeyLength <= 0) { +#if ZEND_DEBUG + ZEND_PUTS("zend_hash_update: Can't put in empty key\n"); +#endif + return FAILURE; + } + + HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_update_or_next_insert(ht, idx, pData, nDataSize, pDest, flag)); + + h = ht->pHashFunction(arKey, nKeyLength); + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == nKeyLength)) { + if (!memcmp(p->arKey, arKey, nKeyLength)) { + if (flag & HASH_ADD) { + return FAILURE; + } + HANDLE_BLOCK_INTERRUPTIONS(); +#if ZEND_DEBUG + if (p->pData == pData) { + ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n"); + HANDLE_UNBLOCK_INTERRUPTIONS(); + return FAILURE; + } +#endif + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + UPDATE_DATA(ht, p, pData, nDataSize); + if (pDest) { + *pDest = p->pData; + } + HANDLE_UNBLOCK_INTERRUPTIONS(); + return SUCCESS; + } + } + p = p->pNext; + } + + p = (Bucket *) pemalloc(sizeof(Bucket)-1+nKeyLength, ht->persistent); + if (!p) { + return FAILURE; + } + memcpy(p->arKey, arKey, nKeyLength); + p->nKeyLength = nKeyLength; + INIT_DATA(ht, p, pData, nDataSize); + p->h = h; + CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]); + if (pDest) { + *pDest = p->pData; + } + + HANDLE_BLOCK_INTERRUPTIONS(); + CONNECT_TO_GLOBAL_DLLIST(p, ht); + ht->arBuckets[nIndex] = p; + HANDLE_UNBLOCK_INTERRUPTIONS(); + + ht->nNumOfElements++; + ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */ + return SUCCESS; +} + +ZEND_API int zend_hash_quick_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag) +{ + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + if (nKeyLength <= 0) { +#if ZEND_DEBUG + ZEND_PUTS("zend_hash_update: Can't put in empty key\n"); +#endif + return FAILURE; + } + + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == nKeyLength)) { + if (!memcmp(p->arKey, arKey, nKeyLength)) { + if (flag & HASH_ADD) { + return FAILURE; + } + HANDLE_BLOCK_INTERRUPTIONS(); +#if ZEND_DEBUG + if (p->pData == pData) { + ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n"); + HANDLE_UNBLOCK_INTERRUPTIONS(); + return FAILURE; + } +#endif + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + UPDATE_DATA(ht, p, pData, nDataSize); + if (pDest) { + *pDest = p->pData; + } + HANDLE_UNBLOCK_INTERRUPTIONS(); + return SUCCESS; + } + } + p = p->pNext; + } + + p = (Bucket *) pemalloc(sizeof(Bucket)-1+nKeyLength, ht->persistent); + if (!p) { + return FAILURE; + } + + memcpy(p->arKey, arKey, nKeyLength); + p->nKeyLength = nKeyLength; + INIT_DATA(ht, p, pData, nDataSize); + p->h = h; + + CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]); + + if (pDest) { + *pDest = p->pData; + } + + HANDLE_BLOCK_INTERRUPTIONS(); + ht->arBuckets[nIndex] = p; + CONNECT_TO_GLOBAL_DLLIST(p, ht); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + ht->nNumOfElements++; + ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */ + return SUCCESS; +} + + +ZEND_API int zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag) +{ + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + if (flag & HASH_NEXT_INSERT) { + h = ht->nNextFreeElement; + } + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->nKeyLength == 0) && (p->h == h)) { + if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) { + return FAILURE; + } + HANDLE_BLOCK_INTERRUPTIONS(); +#if ZEND_DEBUG + if (p->pData == pData) { + ZEND_PUTS("Fatal error in zend_hash_index_update: p->pData == pData\n"); + HANDLE_UNBLOCK_INTERRUPTIONS(); + return FAILURE; + } +#endif + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + UPDATE_DATA(ht, p, pData, nDataSize); + HANDLE_UNBLOCK_INTERRUPTIONS(); + if (h >= ht->nNextFreeElement) { + ht->nNextFreeElement = h + 1; + } + if (pDest) { + *pDest = p->pData; + } + return SUCCESS; + } + p = p->pNext; + } + p = (Bucket *) pemalloc(sizeof(Bucket)-1, ht->persistent); + if (!p) { + return FAILURE; + } + p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */ + p->h = h; + INIT_DATA(ht, p, pData, nDataSize); + if (pDest) { + *pDest = p->pData; + } + + CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]); + + HANDLE_BLOCK_INTERRUPTIONS(); + ht->arBuckets[nIndex] = p; + CONNECT_TO_GLOBAL_DLLIST(p, ht); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + if (h >= ht->nNextFreeElement) { + ht->nNextFreeElement = h + 1; + } + ht->nNumOfElements++; + ZEND_HASH_IF_FULL_DO_RESIZE(ht); + return SUCCESS; +} + + +static int zend_hash_do_resize(HashTable *ht) +{ + Bucket **t; + + IS_CONSISTENT(ht); + + if ((ht->nHashSizeIndex < nNumPrimeNumbers - 1)) { /* Let's double the table size */ + t = (Bucket **) perealloc_recoverable(ht->arBuckets, PrimeNumbers[ht->nHashSizeIndex + 1] * sizeof(Bucket *), ht->persistent); + if (t) { + HANDLE_BLOCK_INTERRUPTIONS(); + ht->arBuckets = t; + ht->nTableSize = PrimeNumbers[ht->nHashSizeIndex + 1]; + ht->nHashSizeIndex++; + zend_hash_rehash(ht); + HANDLE_UNBLOCK_INTERRUPTIONS(); + return SUCCESS; + } + return FAILURE; + } + return SUCCESS; +} + +ZEND_API int zend_hash_rehash(HashTable *ht) +{ + Bucket *p; + uint nIndex; + + IS_CONSISTENT(ht); + + memset(ht->arBuckets, 0, PrimeNumbers[ht->nHashSizeIndex] * sizeof(Bucket *)); + p = ht->pListHead; + while (p != NULL) { + nIndex = p->h % ht->nTableSize; + CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]); + ht->arBuckets[nIndex] = p; + p = p->pListNext; + } + return SUCCESS; +} + +ZEND_API int zend_hash_del_key_or_index(HashTable *ht, char *arKey, uint nKeyLength, ulong h, int flag) +{ + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + if (flag == HASH_DEL_KEY) { + HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_del_key_or_index(ht, arKey, nKeyLength, idx, HASH_DEL_INDEX)); + h = ht->pHashFunction(arKey, nKeyLength); + } + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && ((p->nKeyLength == 0) || /* Numeric index */ + ((p->nKeyLength == nKeyLength) && (!memcmp(p->arKey, arKey, nKeyLength))))) { + HANDLE_BLOCK_INTERRUPTIONS(); + if (p == ht->arBuckets[nIndex]) { + ht->arBuckets[nIndex] = p->pNext; + } else { + p->pLast->pNext = p->pNext; + } + if (p->pNext) { + p->pNext->pLast = p->pLast; + } + if (p->pListLast != NULL) { + p->pListLast->pListNext = p->pListNext; + } else { + /* Deleting the head of the list */ + ht->pListHead = p->pListNext; + } + if (p->pListNext != NULL) { + p->pListNext->pListLast = p->pListLast; + } else { + ht->pListTail = p->pListLast; + } + if (ht->pInternalPointer == p) { + ht->pInternalPointer = p->pListNext; + } + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + if (!p->pDataPtr) { + pefree(p->pData, ht->persistent); + } + pefree(p, ht->persistent); + HANDLE_UNBLOCK_INTERRUPTIONS(); + ht->nNumOfElements--; + return SUCCESS; + } + p = p->pNext; + } + return FAILURE; +} + + +ZEND_API void zend_hash_destroy(HashTable *ht) +{ + Bucket *p, *q; + + IS_CONSISTENT(ht); + + SET_INCONSISTENT(HT_IS_DESTROYING); + + p = ht->pListHead; + while (p != NULL) { + q = p; + p = p->pListNext; + if (ht->pDestructor) { + ht->pDestructor(q->pData); + } + if (!q->pDataPtr && q->pData) { + pefree(q->pData, ht->persistent); + } + pefree(q, ht->persistent); + } + pefree(ht->arBuckets, ht->persistent); + + SET_INCONSISTENT(HT_DESTROYED); +} + + +ZEND_API void zend_hash_clean(HashTable *ht) +{ + Bucket *p, *q; + + IS_CONSISTENT(ht); + + SET_INCONSISTENT(HT_CLEANING); + + p = ht->pListHead; + while (p != NULL) { + q = p; + p = p->pListNext; + if (ht->pDestructor) { + ht->pDestructor(q->pData); + } + if (!q->pDataPtr && q->pData) { + pefree(q->pData, ht->persistent); + } + pefree(q, ht->persistent); + } + memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *)); + ht->pListHead = NULL; + ht->pListTail = NULL; + ht->nNumOfElements = 0; + ht->nNextFreeElement = 0; + ht->pInternalPointer = NULL; + + SET_INCONSISTENT(HT_OK); +} + +/* This function is used by the various apply() functions. + * It deletes the passed bucket, and returns the address of the + * next bucket. The hash *may* be altered during that time, the + * returned value will still be valid. + */ +static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p) +{ + Bucket *retval; + + HANDLE_BLOCK_INTERRUPTIONS(); + + if (ht->pDestructor) { + ht->pDestructor(p->pData); + } + if (!p->pDataPtr) { + pefree(p->pData, ht->persistent); + } + retval = p->pListNext; + + if (p->pLast) { + p->pLast->pNext = p->pNext; + } else { + uint nIndex; + + nIndex = p->h % ht->nTableSize; + ht->arBuckets[nIndex] = p->pNext; + } + if (p->pNext) { + p->pNext->pLast = p->pLast; + } else { + /* Nothing to do as this list doesn't have a tail */ + } + + if (p->pListLast != NULL) { + p->pListLast->pListNext = p->pListNext; + } else { + /* Deleting the head of the list */ + ht->pListHead = p->pListNext; + } + if (p->pListNext != NULL) { + p->pListNext->pListLast = p->pListLast; + } else { + ht->pListTail = p->pListLast; + } + if (ht->pInternalPointer == p) { + ht->pInternalPointer = p->pListNext; + } + pefree(p, ht->persistent); + HANDLE_UNBLOCK_INTERRUPTIONS(); + ht->nNumOfElements--; + + return retval; +} + + +ZEND_API void zend_hash_graceful_destroy(HashTable *ht) +{ + Bucket *p; + + IS_CONSISTENT(ht); + + p = ht->pListHead; + while (p != NULL) { + p = zend_hash_apply_deleter(ht, p); + } + pefree(ht->arBuckets, ht->persistent); + + SET_INCONSISTENT(HT_DESTROYED); +} + +/* This is used to selectively delete certain entries from a hashtable. + * destruct() receives the data and decides if the entry should be deleted + * or not + */ + + +ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func) +{ + Bucket *p; + + IS_CONSISTENT(ht); + + HASH_APPLY_BEGIN(ht); + p = ht->pListHead; + while (p != NULL) { + if (apply_func(p->pData)) { + p = zend_hash_apply_deleter(ht, p); + } else { + p = p->pListNext; + } + } + HASH_APPLY_END(ht); +} + + +ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument) +{ + Bucket *p; + + IS_CONSISTENT(ht); + + HASH_APPLY_BEGIN(ht); + p = ht->pListHead; + while (p != NULL) { + if (apply_func(p->pData, argument)) { + p = zend_hash_apply_deleter(ht, p); + } else { + p = p->pListNext; + } + } + HASH_APPLY_END(ht); +} + + +ZEND_API void zend_hash_apply_with_arguments(HashTable *ht, int (*destruct)(void *, int, va_list, zend_hash_key *), int num_args, ...) +{ + Bucket *p; + va_list args; + zend_hash_key hash_key; + + IS_CONSISTENT(ht); + + HASH_APPLY_BEGIN(ht); + + va_start(args, num_args); + p = ht->pListHead; + while (p != NULL) { + hash_key.arKey = p->arKey; + hash_key.nKeyLength = p->nKeyLength; + hash_key.h = p->h; + if (destruct(p->pData, num_args, args, &hash_key)) { + p = zend_hash_apply_deleter(ht, p); + } else { + p = p->pListNext; + } + } + va_end(args); + + HASH_APPLY_END(ht); +} + + + +ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size) +{ + Bucket *p; + void *new_entry; + + IS_CONSISTENT(source); + IS_CONSISTENT(target); + + p = source->pListHead; + while (p) { + if (p->nKeyLength) { + zend_hash_update(target, p->arKey, p->nKeyLength, p->pData, size, &new_entry); + } else { + zend_hash_index_update(target, p->h, p->pData, size, &new_entry); + } + if (pCopyConstructor) { + pCopyConstructor(new_entry); + } + p = p->pListNext; + } + target->pInternalPointer = target->pListHead; +} + + +ZEND_API void zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size, int overwrite) +{ + Bucket *p; + void *t; + int mode = (overwrite?HASH_UPDATE:HASH_ADD); + + IS_CONSISTENT(source); + IS_CONSISTENT(target); + + p = source->pListHead; + while (p) { + if (p->nKeyLength>0) { + if (zend_hash_add_or_update(target, p->arKey, p->nKeyLength, p->pData, size, &t, mode)==SUCCESS && pCopyConstructor) { + pCopyConstructor(t); + } + } else { + if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h)) && zend_hash_index_update(target, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) { + pCopyConstructor(t); + } + } + p = p->pListNext; + } + target->pInternalPointer = target->pListHead; +} + + +ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, uint size, zend_bool (*pReplaceOrig)(void *orig, void *p_new)) +{ + Bucket *p; + void *t; + void *pOrig; + + IS_CONSISTENT(source); + IS_CONSISTENT(target); + + p = source->pListHead; + while (p) { + if (p->nKeyLength>0) { + if (zend_hash_find(target, p->arKey, p->nKeyLength, &pOrig)==FAILURE + || pReplaceOrig(pOrig, p->pData)) { + if (zend_hash_update(target, p->arKey, p->nKeyLength, p->pData, size, &t)==SUCCESS && pCopyConstructor) { + pCopyConstructor(t); + } + } + } else { + if (zend_hash_index_find(target, p->h, &pOrig)==FAILURE + || pReplaceOrig(pOrig, p->pData)) { + if (zend_hash_index_update(target, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) { + pCopyConstructor(t); + } + } + } + p = p->pListNext; + } + target->pInternalPointer = target->pListHead; +} + + +ZEND_API ulong zend_get_hash_value(HashTable *ht, char *arKey, uint nKeyLength) +{ + IS_CONSISTENT(ht); + + return ht->pHashFunction(arKey, nKeyLength); +} + + +/* Returns SUCCESS if found and FAILURE if not. The pointer to the + * data is returned in pData. The reason is that there's no reason + * someone using the hash table might not want to have NULL data + */ +ZEND_API int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength, void **pData) +{ + ulong h; + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_find(ht, idx, pData)); + + h = ht->pHashFunction(arKey, nKeyLength); + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == nKeyLength)) { + if (!memcmp(p->arKey, arKey, nKeyLength)) { + *pData = p->pData; + return SUCCESS; + } + } + p = p->pNext; + } + return FAILURE; +} + + +ZEND_API int zend_hash_quick_find(HashTable *ht, char *arKey, uint nKeyLength, ulong h, void **pData) +{ + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == nKeyLength)) { + if (!memcmp(p->arKey, arKey, nKeyLength)) { + *pData = p->pData; + return SUCCESS; + } + } + p = p->pNext; + } + return FAILURE; +} + + +ZEND_API int zend_hash_exists(HashTable *ht, char *arKey, uint nKeyLength) +{ + ulong h; + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_exists(ht, idx)); + + h = ht->pHashFunction(arKey, nKeyLength); + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == nKeyLength)) { + if (!memcmp(p->arKey, arKey, nKeyLength)) { + return 1; + } + } + p = p->pNext; + } + return 0; +} + + +ZEND_API int zend_hash_index_find(HashTable *ht, ulong h, void **pData) +{ + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == 0)) { + *pData = p->pData; + return SUCCESS; + } + p = p->pNext; + } + return FAILURE; +} + + +ZEND_API int zend_hash_index_exists(HashTable *ht, ulong h) +{ + uint nIndex; + Bucket *p; + + IS_CONSISTENT(ht); + + nIndex = h % ht->nTableSize; + + p = ht->arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == 0)) { + return 1; + } + p = p->pNext; + } + return 0; +} + + +ZEND_API int zend_hash_num_elements(HashTable *ht) +{ + IS_CONSISTENT(ht); + + return ht->nNumOfElements; +} + + +ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos) +{ + IS_CONSISTENT(ht); + + if (pos) + *pos = ht->pListHead; + else + ht->pInternalPointer = ht->pListHead; +} + + +/* This function will be extremely optimized by remembering + * the end of the list + */ +ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos) +{ + IS_CONSISTENT(ht); + + if (pos) + *pos = ht->pListTail; + else + ht->pInternalPointer = ht->pListTail; +} + + +ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos) +{ + HashPosition *current = pos ? pos : &ht->pInternalPointer; + + IS_CONSISTENT(ht); + + if (*current) { + *current = (*current)->pListNext; + return SUCCESS; + } else + return FAILURE; +} + +ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos) +{ + HashPosition *current = pos ? pos : &ht->pInternalPointer; + + IS_CONSISTENT(ht); + + if (*current) { + *current = (*current)->pListLast; + return SUCCESS; + } else + return FAILURE; +} + + +/* This function should be made binary safe */ +ZEND_API int zend_hash_get_current_key_ex(HashTable *ht, char **str_index, ulong *str_length, ulong *num_index, HashPosition *pos) +{ + Bucket *p; + + p = pos ? (*pos) : ht->pInternalPointer; + + IS_CONSISTENT(ht); + + if (p) { + if (p->nKeyLength) { + *str_index = (char *) pemalloc(p->nKeyLength, ht->persistent); + memcpy(*str_index, p->arKey, p->nKeyLength); + if (str_length) { + *str_length = p->nKeyLength; + } + return HASH_KEY_IS_STRING; + } else { + *num_index = p->h; + return HASH_KEY_IS_LONG; + } + } + return HASH_KEY_NON_EXISTANT; +} + + +ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos) +{ + Bucket *p; + + p = pos ? (*pos) : ht->pInternalPointer; + + IS_CONSISTENT(ht); + + if (p) { + if (p->nKeyLength) { + return HASH_KEY_IS_STRING; + } else { + return HASH_KEY_IS_LONG; + } + } + return HASH_KEY_NON_EXISTANT; +} + + +ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos) +{ + Bucket *p; + + p = pos ? (*pos) : ht->pInternalPointer; + + IS_CONSISTENT(ht); + + if (p) { + *pData = p->pData; + return SUCCESS; + } else { + return FAILURE; + } +} + + +ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func, + compare_func_t compar, int renumber) +{ + Bucket **arTmp; + Bucket *p; + int i, j; + + IS_CONSISTENT(ht); + + if (ht->nNumOfElements <= 1) { /* Doesn't require sorting */ + return SUCCESS; + } + arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent); + if (!arTmp) { + return FAILURE; + } + p = ht->pListHead; + i = 0; + while (p) { + arTmp[i] = p; + p = p->pListNext; + i++; + } + + (*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar); + + HANDLE_BLOCK_INTERRUPTIONS(); + ht->pListHead = arTmp[0]; + ht->pListTail = NULL; + ht->pInternalPointer = ht->pListHead; + + for (j = 0; j < i; j++) { + if (ht->pListTail) { + ht->pListTail->pListNext = arTmp[j]; + } + arTmp[j]->pListLast = ht->pListTail; + arTmp[j]->pListNext = NULL; + ht->pListTail = arTmp[j]; + } + pefree(arTmp, ht->persistent); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + if (renumber) { + p = ht->pListHead; + i=0; + while (p != NULL) { + p->nKeyLength = 0; + p->h = i++; + p = p->pListNext; + } + ht->nNextFreeElement = i; + zend_hash_rehash(ht); + } + return SUCCESS; +} + + +ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered) +{ + Bucket *p1, *p2; + int result; + void *pData2; + + IS_CONSISTENT(ht1); + IS_CONSISTENT(ht2); + + result = ht1->nNumOfElements - ht2->nNumOfElements; + if (result!=0) { + return result; + } + + p1 = ht1->pListHead; + if (ordered) { + p2 = ht2->pListHead; + } + + while (p1) { + if (ordered && !p2) { + return 1; /* That's not supposed to happen */ + } + if (ordered) { + if (p1->nKeyLength==0 && p2->nKeyLength==0) { /* numeric indices */ + result = p1->h - p2->h; + if (result!=0) { + return result; + } + } else { /* string indices */ + result = p1->nKeyLength - p2->nKeyLength; + if (result!=0) { + return result; + } + result = memcmp(p1->arKey, p2->arKey, p1->nKeyLength); + if (result!=0) { + return result; + } + } + pData2 = p2->pData; + } else { + if (p1->nKeyLength==0) { /* numeric index */ + if (zend_hash_index_find(ht2, p1->h, &pData2)==FAILURE) { + return 1; + } + } else { /* string index */ + if (zend_hash_find(ht2, p1->arKey, p1->nKeyLength, &pData2)==FAILURE) { + return 1; + } + } + } + result = compar(p1->pData, pData2); + if (result!=0) { + return result; + } + p1 = p1->pListNext; + if (ordered) { + p2 = p2->pListNext; + } + } + + return 0; +} + + +ZEND_API int zend_hash_minmax(HashTable *ht, int (*compar) (const void *, const void *), int flag, void **pData) +{ + Bucket *p,*res; + + IS_CONSISTENT(ht); + + if (ht->nNumOfElements == 0 ) { + *pData=NULL; + return FAILURE; + } + + res = p = ht->pListHead; + while ((p = p->pListNext)) { + if (flag) { + if (compar(&res,&p) < 0) { /* max */ + res = p; + } + } else { + if (compar(&res,&p) > 0) { /* min */ + res = p; + } + } + } + *pData = res->pData; + return SUCCESS; +} + +ZEND_API ulong zend_hash_next_free_element(HashTable *ht) +{ + IS_CONSISTENT(ht); + + return ht->nNextFreeElement; + +} + +#if ZEND_DEBUG +void zend_hash_display_pListTail(HashTable *ht) +{ + Bucket *p; + + p = ht->pListTail; + while (p != NULL) { + zend_output_debug_string(0, "pListTail has key %s\n", p->arKey); + p = p->pListLast; + } +} + +void zend_hash_display(HashTable *ht) +{ + Bucket *p; + uint i; + + for (i = 0; i < ht->nTableSize; i++) { + p = ht->arBuckets[i]; + while (p != NULL) { + zend_output_debug_string(0, "%s <==> 0x%X\n", p->arKey, p->h); + p = p->pNext; + } + } + + p = ht->pListTail; + while (p != NULL) { + zend_output_debug_string(0, "%s <==> 0x%X\n", p->arKey, p->h); + p = p->pListLast; + } +} +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y new file mode 100644 index 0000000000..720b83696d --- /dev/null +++ b/Zend/zend_language_parser.y @@ -0,0 +1,726 @@ +%{ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* + * LALR shift/reduce conflicts and how they are resolved: + * + * - 2 shift/reduce conflicts due to the dangeling elseif/else ambiguity. Solved by shift. + * - 1 shift/reduce conflict due to arrays within encapsulated strings. Solved by shift. + * - 1 shift/reduce conflict due to objects within encapsulated strings. Solved by shift. + * + */ + + +#include "zend_compile.h" +#include "zend.h" +#include "zend_list.h" +#include "zend_globals.h" +#include "zend_API.h" + +#define YYERROR_VERBOSE +#define YYSTYPE znode +#ifdef ZTS +# define YYPARSE_PARAM compiler_globals +# define YYLEX_PARAM compiler_globals +#endif + + +%} + +%pure_parser +%expect 4 + +%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE +%left ',' +%left T_LOGICAL_OR +%left T_LOGICAL_XOR +%left T_LOGICAL_AND +%right T_PRINT +%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL +%left '?' ':' +%left T_BOOLEAN_OR +%left T_BOOLEAN_AND +%left '|' +%left '^' +%left '&' +%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL +%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL +%left T_SL T_SR +%left '+' '-' '.' +%left '*' '/' '%' +%right '!' '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@' +%right '[' +%nonassoc T_NEW +%token T_EXIT +%token T_IF +%left T_ELSEIF +%left T_ELSE +%left T_ENDIF +%token T_LNUMBER +%token T_DNUMBER +%token T_STRING +%token T_STRING_VARNAME +%token T_VARIABLE +%token T_NUM_STRING +%token T_INLINE_HTML +%token T_CHARACTER +%token T_BAD_CHARACTER +%token T_ENCAPSED_AND_WHITESPACE +%token T_CONSTANT_ENCAPSED_STRING +%token T_ECHO +%token T_DO +%token T_WHILE +%token T_ENDWHILE +%token T_FOR +%token T_ENDFOR +%token T_FOREACH +%token T_ENDFOREACH +%token T_DECLARE +%token T_ENDDECLARE +%token T_AS +%token T_SWITCH +%token T_ENDSWITCH +%token T_CASE +%token T_DEFAULT +%token T_BREAK +%token T_CONTINUE +%token T_OLD_FUNCTION +%token T_FUNCTION +%token T_CONST +%token T_RETURN +%token T_USE +%token T_GLOBAL +%token T_STATIC +%token T_VAR +%token T_UNSET +%token T_ISSET +%token T_EMPTY +%token T_CLASS +%token T_EXTENDS +%token T_OBJECT_OPERATOR +%token T_DOUBLE_ARROW +%token T_LIST +%token T_ARRAY +%token T_LINE +%token T_FILE +%token T_COMMENT +%token T_ML_COMMENT +%token T_OPEN_TAG +%token T_OPEN_TAG_WITH_ECHO +%token T_CLOSE_TAG +%token T_WHITESPACE +%token T_START_HEREDOC +%token T_END_HEREDOC +%token T_DOLLAR_OPEN_CURLY_BRACES +%token T_CURLY_OPEN +%token T_PAAMAYIM_NEKUDOTAYIM + +%% /* Rules */ + +start: + top_statement_list +; + +top_statement_list: + top_statement_list { zend_do_extended_info(CLS_C); } top_statement { HANDLE_INTERACTIVE(); } + | /* empty */ +; + + +top_statement: + statement + | declaration_statement { zend_do_early_binding(CLS_C); } +; + + +inner_statement_list: + inner_statement_list { zend_do_extended_info(CLS_C); } inner_statement { HANDLE_INTERACTIVE(); } + | /* empty */ +; + + +inner_statement: + statement + | declaration_statement +; + + +statement: + unticked_statement { zend_do_ticks(CLS_C); } +; + + +unticked_statement: + '{' inner_statement_list '}' + | T_IF '(' expr ')' { zend_do_if_cond(&$3, &$4 CLS_CC); } statement { zend_do_if_after_statement(&$4, 1 CLS_CC); } elseif_list else_single { zend_do_if_end(CLS_C); } + | T_IF '(' expr ')' ':' { zend_do_if_cond(&$3, &$4 CLS_CC); } inner_statement_list { zend_do_if_after_statement(&$4, 1 CLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(CLS_C); } + | T_WHILE '(' { $1.u.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' { zend_do_while_cond(&$4, &$5 CLS_CC); } while_statement { zend_do_while_end(&$1, &$5 CLS_CC); } + | T_DO { $1.u.opline_num = get_next_op_number(CG(active_op_array)); zend_do_do_while_begin(CLS_C); } statement T_WHILE '(' { $5.u.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' ';' { zend_do_do_while_end(&$1, &$5, &$7 CLS_CC); } + | T_FOR + '(' + for_expr + ';' { zend_do_free(&$3 CLS_CC); $4.u.opline_num = get_next_op_number(CG(active_op_array)); } + for_expr + ';' { zend_do_extended_info(CLS_C); zend_do_for_cond(&$6, &$7 CLS_CC); } + for_expr + ')' { zend_do_free(&$9 CLS_CC); zend_do_for_before_statement(&$4, &$7 CLS_CC); } + for_statement { zend_do_for_end(&$7 CLS_CC); } + | T_SWITCH '(' expr ')' { zend_do_switch_cond(&$3 CLS_CC); } switch_case_list { zend_do_switch_end(&$6 CLS_CC); } + | T_BREAK ';' { zend_do_brk_cont(ZEND_BRK, NULL CLS_CC); } + | T_BREAK expr ';' { zend_do_brk_cont(ZEND_BRK, &$2 CLS_CC); } + | T_CONTINUE ';' { zend_do_brk_cont(ZEND_CONT, NULL CLS_CC); } + | T_CONTINUE expr ';' { zend_do_brk_cont(ZEND_CONT, &$2 CLS_CC); } + | T_RETURN ';' { zend_do_return(NULL, 0 CLS_CC); } + | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 CLS_CC); } + | T_RETURN cvar ';' { zend_do_return(&$2, 1 CLS_CC); } + | T_GLOBAL global_var_list ';' + | T_STATIC static_var_list ';' + | T_ECHO echo_expr_list ';' + | T_INLINE_HTML { zend_do_echo(&$1 CLS_CC); } + | expr ';' { zend_do_free(&$1 CLS_CC); } + | T_USE use_filename ';' { zend_error(E_COMPILE_ERROR,"use: Not yet supported. Please use include_once() or require_once()"); zval_dtor(&$2.u.constant); } + | T_UNSET '(' unset_variables ')' ';' + | T_FOREACH '(' w_cvar T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 1 CLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4 CLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 CLS_CC); } + | T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 0 CLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4 CLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 CLS_CC); } + | T_DECLARE { zend_do_declare_begin(CLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(CLS_C); } + | ';' /* empty statement */ +; + +unset_variables: + unset_variable + | unset_variables ',' unset_variable +; + +unset_variable: + cvar { zend_do_end_variable_parse(BP_VAR_UNSET, 0 CLS_CC); zend_do_unset(&$1 CLS_CC); } +; + +use_filename: + T_CONSTANT_ENCAPSED_STRING { $$ = $1; } + | '(' T_CONSTANT_ENCAPSED_STRING ')' { $$ = $2; } +; + + +declaration_statement: + unticked_declaration_statement { zend_do_ticks(CLS_C); } +; + + +unticked_declaration_statement: + T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type CLS_CC); } + '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 CLS_CC); } + | T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type CLS_CC); } + parameter_list '(' inner_statement_list ')' ';' { zend_do_end_function_declaration(&$1 CLS_CC); } + | T_CLASS T_STRING { zend_do_begin_class_declaration(&$2, NULL CLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(CLS_C); } + | T_CLASS T_STRING T_EXTENDS T_STRING { zend_do_begin_class_declaration(&$2, &$4 CLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(CLS_C); } +; + + +foreach_optional_arg: + /* empty */ { $$.op_type = IS_UNUSED; } + | T_DOUBLE_ARROW w_cvar { $$ = $2; } +; + + +for_statement: + statement + | ':' inner_statement_list T_ENDFOR ';' +; + + +foreach_statement: + statement + | ':' inner_statement_list T_ENDFOREACH ';' +; + + +declare_statement: + statement + | ':' inner_statement_list T_ENDDECLARE ';' +; + + +declare_list: + T_STRING '=' static_scalar { zend_do_declare_stmt(&$1, &$3 CLS_CC); } + | declare_list ',' T_STRING '=' static_scalar { zend_do_declare_stmt(&$3, &$5 CLS_CC); } +; + + +switch_case_list: + '{' case_list '}' { $$ = $2; } + | '{' ';' case_list '}' { $$ = $3; } + | ':' case_list T_ENDSWITCH ';' { $$ = $2; } + | ':' ';' case_list T_ENDSWITCH ';' { $$ = $3; } +; + + +case_list: + /* empty */ { $$.op_type = IS_UNUSED; } + | case_list T_CASE expr case_separator { zend_do_extended_info(CLS_C); zend_do_case_before_statement(&$1, &$2, &$3 CLS_CC); } inner_statement_list { zend_do_case_after_statement(&$$, &$2 CLS_CC); $$.op_type = IS_CONST } + | case_list T_DEFAULT case_separator { zend_do_extended_info(CLS_C); zend_do_default_before_statement(&$1, &$2 CLS_CC); } inner_statement_list { zend_do_case_after_statement(&$$, &$2 CLS_CC); $$.op_type = IS_CONST; } +; + + +case_separator: + ':' + | ';' +; + + +while_statement: + statement + | ':' inner_statement_list T_ENDWHILE ';' +; + + + +elseif_list: + /* empty */ + | elseif_list T_ELSEIF '(' expr ')' { zend_do_if_cond(&$4, &$5 CLS_CC); } statement { zend_do_if_after_statement(&$5, 0 CLS_CC); } +; + + +new_elseif_list: + /* empty */ + | new_elseif_list T_ELSEIF '(' expr ')' ':' { zend_do_if_cond(&$4, &$5 CLS_CC); } inner_statement_list { zend_do_if_after_statement(&$5, 0 CLS_CC); } +; + + +else_single: + /* empty */ + | T_ELSE statement +; + + +new_else_single: + /* empty */ + | T_ELSE ':' inner_statement_list +; + + +parameter_list: + non_empty_parameter_list + | /* empty */ +; + + +non_empty_parameter_list: + T_VARIABLE { znode tmp; fetch_simple_variable(&tmp, &$1, 0 CLS_CC); $$.op_type = IS_CONST; $$.u.constant.value.lval=1; $$.u.constant.type=IS_LONG; INIT_PZVAL(&$$.u.constant); zend_do_receive_arg(ZEND_RECV, &tmp, &$$, NULL, BYREF_NONE CLS_CC); } + | '&' T_VARIABLE { znode tmp; fetch_simple_variable(&tmp, &$2, 0 CLS_CC); $$.op_type = IS_CONST; $$.u.constant.value.lval=1; $$.u.constant.type=IS_LONG; INIT_PZVAL(&$$.u.constant); zend_do_receive_arg(ZEND_RECV, &tmp, &$$, NULL, BYREF_FORCE CLS_CC); } + | T_CONST T_VARIABLE { znode tmp; fetch_simple_variable(&tmp, &$2, 0 CLS_CC); $$.op_type = IS_CONST; $$.u.constant.value.lval=1; $$.u.constant.type=IS_LONG; INIT_PZVAL(&$$.u.constant); zend_do_receive_arg(ZEND_RECV, &tmp, &$$, NULL, BYREF_NONE CLS_CC); } + | T_VARIABLE '=' static_scalar { znode tmp; fetch_simple_variable(&tmp, &$1, 0 CLS_CC); $$.op_type = IS_CONST; $$.u.constant.value.lval=1; $$.u.constant.type=IS_LONG; INIT_PZVAL(&$$.u.constant); zend_do_receive_arg(ZEND_RECV_INIT, &tmp, &$$, &$3, BYREF_NONE CLS_CC); } + | non_empty_parameter_list ',' T_VARIABLE { znode tmp; fetch_simple_variable(&tmp, &$3, 0 CLS_CC); $$=$1; $$.u.constant.value.lval++; zend_do_receive_arg(ZEND_RECV, &tmp, &$$, NULL, BYREF_NONE CLS_CC); } + | non_empty_parameter_list ',' '&' T_VARIABLE { znode tmp; fetch_simple_variable(&tmp, &$4, 0 CLS_CC); $$=$1; $$.u.constant.value.lval++; zend_do_receive_arg(ZEND_RECV, &tmp, &$$, NULL, BYREF_FORCE CLS_CC); } + | non_empty_parameter_list ',' T_CONST T_VARIABLE { znode tmp; fetch_simple_variable(&tmp, &$4, 0 CLS_CC); $$=$1; $$.u.constant.value.lval++; zend_do_receive_arg(ZEND_RECV, &tmp, &$$, NULL, BYREF_NONE CLS_CC); } + | non_empty_parameter_list ',' T_VARIABLE '=' static_scalar { znode tmp; fetch_simple_variable(&tmp, &$3, 0 CLS_CC); $$=$1; $$.u.constant.value.lval++; zend_do_receive_arg(ZEND_RECV_INIT, &tmp, &$$, &$5, BYREF_NONE CLS_CC); } +; + + +function_call_parameter_list: + non_empty_function_call_parameter_list { $$ = $1; } + | /* empty */ { $$.u.constant.value.lval = 0; } +; + + +non_empty_function_call_parameter_list: + expr_without_variable { $$.u.constant.value.lval = 1; zend_do_pass_param(&$1, ZEND_SEND_VAL, $$.u.constant.value.lval CLS_CC); } + | cvar { $$.u.constant.value.lval = 1; zend_do_pass_param(&$1, ZEND_SEND_VAR, $$.u.constant.value.lval CLS_CC); } + | '&' w_cvar { $$.u.constant.value.lval = 1; zend_do_pass_param(&$2, ZEND_SEND_REF, $$.u.constant.value.lval CLS_CC); } + | non_empty_function_call_parameter_list ',' expr_without_variable { $$.u.constant.value.lval=$1.u.constant.value.lval+1; zend_do_pass_param(&$3, ZEND_SEND_VAL, $$.u.constant.value.lval CLS_CC); } + | non_empty_function_call_parameter_list ',' cvar { $$.u.constant.value.lval=$1.u.constant.value.lval+1; zend_do_pass_param(&$3, ZEND_SEND_VAR, $$.u.constant.value.lval CLS_CC); } + | non_empty_function_call_parameter_list ',' '&' w_cvar { $$.u.constant.value.lval=$1.u.constant.value.lval+1; zend_do_pass_param(&$4, ZEND_SEND_REF, $$.u.constant.value.lval CLS_CC); } +; + +global_var_list: + global_var_list ',' global_var { zend_do_fetch_global_or_static_variable(&$3, NULL, ZEND_FETCH_GLOBAL CLS_CC); } + | global_var { zend_do_fetch_global_or_static_variable(&$1, NULL, ZEND_FETCH_GLOBAL CLS_CC); } +; + + +global_var: + T_VARIABLE { $$ = $1; } + | '$' r_cvar { $$ = $2; } + | '$' '{' expr '}' { $$ = $3; } +; + + +static_var_list: + static_var_list ',' T_VARIABLE { zend_do_fetch_global_or_static_variable(&$3, NULL, ZEND_FETCH_STATIC CLS_CC); } + | static_var_list ',' T_VARIABLE '=' static_scalar { zend_do_fetch_global_or_static_variable(&$3, &$5, ZEND_FETCH_STATIC CLS_CC); } + | T_VARIABLE { zend_do_fetch_global_or_static_variable(&$1, NULL, ZEND_FETCH_STATIC CLS_CC); } + | T_VARIABLE '=' static_scalar { zend_do_fetch_global_or_static_variable(&$1, &$3, ZEND_FETCH_STATIC CLS_CC); } + +; + + +class_statement_list: + class_statement_list class_statement + | /* empty */ +; + + +class_statement: + T_VAR class_variable_decleration ';' + | T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 1, $3.op_type CLS_CC); } '(' + parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 CLS_CC); } + | T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 1, $3.op_type CLS_CC); } + parameter_list '(' inner_statement_list ')' ';' { zend_do_end_function_declaration(&$1 CLS_CC); } + +; + +is_reference: + /* empty */ { $$.op_type = ZEND_RETURN_VAL; } + | '&' { $$.op_type = ZEND_RETURN_REF; } + +class_variable_decleration: + class_variable_decleration ',' T_VARIABLE { zend_do_declare_property(&$3, NULL CLS_CC); } + | class_variable_decleration ',' T_VARIABLE '=' static_scalar { zend_do_declare_property(&$3, &$5 CLS_CC); } + | T_VARIABLE { zend_do_declare_property(&$1, NULL CLS_CC); } + | T_VARIABLE '=' static_scalar { zend_do_declare_property(&$1, &$3 CLS_CC); } +; + + +echo_expr_list: + | echo_expr_list ',' expr { zend_do_echo(&$3 CLS_CC); } + | expr { zend_do_echo(&$1 CLS_CC); } +; + + +for_expr: + /* empty */ { $$.op_type = IS_CONST; $$.u.constant.type = IS_BOOL; $$.u.constant.value.lval = 1; } + | non_empty_for_expr { $$ = $1; } +; + +non_empty_for_expr: + non_empty_for_expr ',' { zend_do_free(&$1 CLS_CC); } expr { $$ = $4; } + | expr { $$ = $1; } +; + + +expr_without_variable: + T_LIST '(' { zend_do_list_init(CLS_C); } assignment_list ')' '=' expr { zend_do_list_end(&$$, &$7 CLS_CC); } + | cvar '=' expr { zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); zend_do_assign(&$$, &$1, &$3 CLS_CC); } + | cvar '=' '&' w_cvar { zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); zend_do_assign_ref(&$$, &$1, &$4 CLS_CC); } + | cvar '=' '&' function_call { zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); zend_do_assign_ref(&$$, &$1, &$4 CLS_CC); } + | cvar '=' '&' T_NEW class_name { zend_do_extended_fcall_begin(CLS_C); zend_do_begin_new_object(&$4, &$5 CLS_CC); } ctor_arguments { zend_do_end_new_object(&$3, &$5, &$4, &$7 CLS_CC); zend_do_extended_fcall_end(CLS_C); zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); zend_do_assign_ref(&$$, &$1, &$3 CLS_CC); } + | T_NEW class_name { zend_do_extended_fcall_begin(CLS_C); zend_do_begin_new_object(&$1, &$2 CLS_CC); } ctor_arguments { zend_do_end_new_object(&$$, &$2, &$1, &$4 CLS_CC); zend_do_extended_fcall_end(CLS_C);} + | cvar T_PLUS_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_ADD, &$$, &$1, &$3 CLS_CC); } + | cvar T_MINUS_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SUB, &$$, &$1, &$3 CLS_CC); } + | cvar T_MUL_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_MUL, &$$, &$1, &$3 CLS_CC); } + | cvar T_DIV_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_DIV, &$$, &$1, &$3 CLS_CC); } + | cvar T_CONCAT_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_CONCAT, &$$, &$1, &$3 CLS_CC); } + | cvar T_MOD_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_MOD, &$$, &$1, &$3 CLS_CC); } + | cvar T_AND_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_AND, &$$, &$1, &$3 CLS_CC); } + | cvar T_OR_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_OR, &$$, &$1, &$3 CLS_CC); } + | cvar T_XOR_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_XOR, &$$, &$1, &$3 CLS_CC); } + | cvar T_SL_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SL, &$$, &$1, &$3 CLS_CC); } + | cvar T_SR_EQUAL expr { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SR, &$$, &$1, &$3 CLS_CC); } + | rw_cvar T_INC { zend_do_post_incdec(&$$, &$1, ZEND_POST_INC CLS_CC); } + | T_INC rw_cvar { zend_do_pre_incdec(&$$, &$2, ZEND_PRE_INC CLS_CC); } + | rw_cvar T_DEC { zend_do_post_incdec(&$$, &$1, ZEND_POST_DEC CLS_CC); } + | T_DEC rw_cvar { zend_do_pre_incdec(&$$, &$2, ZEND_PRE_DEC CLS_CC); } + | expr T_BOOLEAN_OR { zend_do_boolean_or_begin(&$1, &$2 CLS_CC); } expr { zend_do_boolean_or_end(&$$, &$1, &$4, &$2 CLS_CC); } + | expr T_BOOLEAN_AND { zend_do_boolean_and_begin(&$1, &$2 CLS_CC); } expr { zend_do_boolean_and_end(&$$, &$1, &$4, &$2 CLS_CC); } + | expr T_LOGICAL_OR { zend_do_boolean_or_begin(&$1, &$2 CLS_CC); } expr { zend_do_boolean_or_end(&$$, &$1, &$4, &$2 CLS_CC); } + | expr T_LOGICAL_AND { zend_do_boolean_and_begin(&$1, &$2 CLS_CC); } expr { zend_do_boolean_and_end(&$$, &$1, &$4, &$2 CLS_CC); } + | expr T_LOGICAL_XOR expr { zend_do_binary_op(ZEND_BOOL_XOR, &$$, &$1, &$3 CLS_CC); } + | expr '|' expr { zend_do_binary_op(ZEND_BW_OR, &$$, &$1, &$3 CLS_CC); } + | expr '&' expr { zend_do_binary_op(ZEND_BW_AND, &$$, &$1, &$3 CLS_CC); } + | expr '^' expr { zend_do_binary_op(ZEND_BW_XOR, &$$, &$1, &$3 CLS_CC); } + | expr '.' expr { zend_do_binary_op(ZEND_CONCAT,&$$,&$1,&$3 CLS_CC); } + | expr '+' expr { zend_do_binary_op(ZEND_ADD,&$$,&$1,&$3 CLS_CC); } + | expr '-' expr { zend_do_binary_op(ZEND_SUB,&$$,&$1,&$3 CLS_CC); } + | expr '*' expr { zend_do_binary_op(ZEND_MUL,&$$,&$1,&$3 CLS_CC); } + | expr '/' expr { zend_do_binary_op(ZEND_DIV,&$$,&$1,&$3 CLS_CC); } + | expr '%' expr { zend_do_binary_op(ZEND_MOD,&$$,&$1,&$3 CLS_CC); } + | expr T_SL expr { zend_do_binary_op(ZEND_SL, &$$, &$1, &$3 CLS_CC); } + | expr T_SR expr { zend_do_binary_op(ZEND_SR, &$$, &$1, &$3 CLS_CC); } + | '+' expr { $1.u.constant.value.lval=0; $1.u.constant.type=IS_LONG; $1.op_type = IS_CONST; INIT_PZVAL(&$1.u.constant); zend_do_binary_op(ZEND_ADD, &$$, &$1, &$2 CLS_CC); } + | '-' expr { $1.u.constant.value.lval=0; $1.u.constant.type=IS_LONG; $1.op_type = IS_CONST; INIT_PZVAL(&$1.u.constant); zend_do_binary_op(ZEND_SUB, &$$, &$1, &$2 CLS_CC); } + | '!' expr { zend_do_unary_op(ZEND_BOOL_NOT, &$$, &$2 CLS_CC); } + | '~' expr { zend_do_unary_op(ZEND_BW_NOT, &$$, &$2 CLS_CC); } + | expr T_IS_IDENTICAL expr { zend_do_binary_op(ZEND_IS_IDENTICAL, &$$, &$1, &$3 CLS_CC); } + | expr T_IS_NOT_IDENTICAL expr { zend_do_binary_op(ZEND_IS_NOT_IDENTICAL, &$$, &$1, &$3 CLS_CC); } + | expr T_IS_EQUAL expr { zend_do_binary_op(ZEND_IS_EQUAL, &$$, &$1, &$3 CLS_CC); } + | expr T_IS_NOT_EQUAL expr { zend_do_binary_op(ZEND_IS_NOT_EQUAL, &$$, &$1, &$3 CLS_CC); } + | expr '<' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$1, &$3 CLS_CC); } + | expr T_IS_SMALLER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$1, &$3 CLS_CC); } + | expr '>' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 CLS_CC); } + | expr T_IS_GREATER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1 CLS_CC); } + | '(' expr ')' { $$ = $2; } + | expr '?' { zend_do_begin_qm_op(&$1, &$2 CLS_CC); } + expr ':' { zend_do_qm_true(&$4, &$2, &$5 CLS_CC); } + expr { zend_do_qm_false(&$$, &$7, &$2, &$5 CLS_CC); } + | function_call { $$ = $1; } + | internal_functions_in_yacc { $$ = $1; } + | T_INT_CAST expr { zend_do_cast(&$$, &$2, IS_LONG CLS_CC); } + | T_DOUBLE_CAST expr { zend_do_cast(&$$, &$2, IS_DOUBLE CLS_CC); } + | T_STRING_CAST expr { zend_do_cast(&$$, &$2, IS_STRING CLS_CC); } + | T_ARRAY_CAST expr { zend_do_cast(&$$, &$2, IS_ARRAY CLS_CC); } + | T_OBJECT_CAST expr { zend_do_cast(&$$, &$2, IS_OBJECT CLS_CC); } + | T_BOOL_CAST expr { zend_do_cast(&$$, &$2, IS_BOOL CLS_CC); } + | T_UNSET_CAST expr { zend_do_cast(&$$, &$2, IS_NULL CLS_CC); } + | T_EXIT exit_expr { zend_do_exit(&$$, &$2 CLS_CC); } + | '@' { zend_do_begin_silence(&$1 CLS_CC); } expr { zend_do_end_silence(&$1 CLS_CC); $$ = $3; } + | scalar { $$ = $1; } + | T_ARRAY '(' array_pair_list ')' { $$ = $3; } + | '`' encaps_list '`' { zend_do_shell_exec(&$$, &$2 CLS_CC); } + | T_PRINT expr { zend_do_print(&$$, &$2 CLS_CC); } +; + +function_call: + T_STRING '(' { $2.u.opline_num = zend_do_begin_function_call(&$1 CLS_CC); } + function_call_parameter_list + ')' { zend_do_end_function_call(&$1, &$$, &$4, 0, $2.u.opline_num CLS_CC); zend_do_extended_fcall_end(CLS_C); } + | cvar '(' { zend_do_begin_dynamic_function_call(&$1 CLS_CC); } + function_call_parameter_list + ')' { zend_do_end_function_call(&$1, &$$, &$4, 0, 1 CLS_CC); zend_do_extended_fcall_end(CLS_C);} + | T_STRING T_PAAMAYIM_NEKUDOTAYIM T_STRING '(' { zend_do_extended_fcall_begin(CLS_C); zend_do_begin_class_member_function_call(&$1, &$3 CLS_CC); } + function_call_parameter_list + ')' { zend_do_end_function_call(&$3, &$$, &$6, 1, 1 CLS_CC); zend_do_extended_fcall_end(CLS_C);} +; + + +exit_expr: + /* empty */ { memset(&$$, 0, sizeof(znode)); $$.op_type = IS_UNUSED; } + | '(' ')' { memset(&$$, 0, sizeof(znode)); $$.op_type = IS_UNUSED; } + | '(' expr ')' { $$ = $2; } +; + + +ctor_arguments: + /* empty */ { $$.u.constant.value.lval=0; } + | '(' function_call_parameter_list ')' { $$ = $2; } +; + + +class_name: + T_STRING { $$ = $1; } + | r_cvar { $$ = $1; } +; + + + +common_scalar: + T_LNUMBER { $$ = $1; } + | T_DNUMBER { $$ = $1; } + | T_CONSTANT_ENCAPSED_STRING { $$ = $1; } + | T_LINE { $$ = $1; } + | T_FILE { $$ = $1; } +; + + +static_scalar: /* compile-time evaluated scalars */ + common_scalar { $$ = $1; } + | T_STRING { zend_do_fetch_constant(&$$, &$1, ZEND_CT CLS_CC); } + | '+' static_scalar { $$ = $1; } + | '-' static_scalar { zval minus_one; minus_one.type = IS_LONG; minus_one.value.lval = -1; mul_function(&$2.u.constant, &$2.u.constant, &minus_one); $$ = $2; } + | T_ARRAY '(' static_array_pair_list ')' { $$ = $3; $$.u.constant.type = IS_CONSTANT_ARRAY; } +; + + +scalar: + T_STRING { zend_do_fetch_constant(&$$, &$1, ZEND_RT CLS_CC); } + | T_STRING_VARNAME { $$ = $1; } + | common_scalar { $$ = $1; } + | '"' encaps_list '"' { $$ = $2; } + | '\'' encaps_list '\'' { $$ = $2; } + | T_START_HEREDOC encaps_list T_END_HEREDOC { $$ = $2; zend_do_end_heredoc(CLS_C); } +; + + +static_array_pair_list: + /* empty */ { $$.op_type = IS_CONST; INIT_PZVAL(&$$.u.constant); array_init(&$$.u.constant); } + | non_empty_static_array_pair_list possible_comma { $$ = $1; } +; + +possible_comma: + /* empty */ + | ',' +; + +non_empty_static_array_pair_list: + non_empty_static_array_pair_list ',' static_scalar T_DOUBLE_ARROW static_scalar { zend_do_add_static_array_element(&$$, &$3, &$5); } + | non_empty_static_array_pair_list ',' static_scalar { zend_do_add_static_array_element(&$$, NULL, &$3); } + | static_scalar T_DOUBLE_ARROW static_scalar { $$.op_type = IS_CONST; INIT_PZVAL(&$$.u.constant); array_init(&$$.u.constant); zend_do_add_static_array_element(&$$, &$1, &$3); } + | static_scalar { $$.op_type = IS_CONST; INIT_PZVAL(&$$.u.constant); array_init(&$$.u.constant); zend_do_add_static_array_element(&$$, NULL, &$1); } +; + +expr: + r_cvar { $$ = $1; } + | expr_without_variable { $$ = $1; } +; + +/* +w_expr: + w_cvar { $$ = $1; } + | expr_without_variable { $$ = $1; } +; +*/ + + +r_cvar: + cvar { zend_do_end_variable_parse(BP_VAR_R, 0 CLS_CC); $$ = $1; } +; + + +w_cvar: + cvar { zend_do_end_variable_parse(BP_VAR_W, 0 CLS_CC); $$ = $1; } +; + + +rw_cvar: + cvar { zend_do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); $$ = $1; } +; + + +cvar: + cvar_without_objects { $$ = $1; } + | cvar_without_objects T_OBJECT_OPERATOR { zend_do_push_object(&$1 CLS_CC); } ref_list { $$ = $4; } +; + + +cvar_without_objects: + reference_variable { $$ = $1; } + | simple_indirect_reference reference_variable { zend_do_indirect_references(&$$, &$1, &$2 CLS_CC); } +; + + +reference_variable: + reference_variable '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 CLS_CC); } + | reference_variable '{' expr '}' { fetch_string_offset(&$$, &$1, &$3 CLS_CC); } + | compound_variable { zend_do_fetch_globals(&$1 CLS_CC); zend_do_begin_variable_parse(CLS_C); fetch_simple_variable(&$$, &$1, 1 CLS_CC); } +; + + +compound_variable: + T_VARIABLE { $$ = $1; } + | '$' '{' expr '}' { $$ = $3; } +; + +dim_offset: + /* empty */ { $$.op_type = IS_UNUSED; } + | expr { $$ = $1; } +; + +ref_list: + object_property { $$ = $1; } + | ref_list T_OBJECT_OPERATOR { zend_do_push_object(&$1 CLS_CC); } object_property { $$ = $4; } +; + +object_property: + object_dim_list { $$ = $1; } + | cvar_without_objects { zend_do_end_variable_parse(BP_VAR_R, 0 CLS_CC); } { znode tmp_znode; zend_do_pop_object(&tmp_znode CLS_CC); zend_do_fetch_property(&$$, &tmp_znode, &$1 CLS_CC);} +; + +object_dim_list: + object_dim_list '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 CLS_CC); } + | object_dim_list '{' expr '}' { fetch_string_offset(&$$, &$1, &$3 CLS_CC); } + | variable_name { znode tmp_znode; zend_do_pop_object(&tmp_znode CLS_CC); zend_do_fetch_property(&$$, &tmp_znode, &$1 CLS_CC);} +; + +variable_name: + T_STRING { $$ = $1; } + | '{' expr '}' { $$ = $2; } +; + + +simple_indirect_reference: + '$' { $$.u.constant.value.lval = 1; } + | simple_indirect_reference '$' { $$.u.constant.value.lval++; } +; + +assignment_list: + assignment_list ',' assignment_list_element + | assignment_list_element +; + + +assignment_list_element: + cvar { zend_do_add_list_element(&$1 CLS_CC); } + | T_LIST '(' { zend_do_new_list_begin(CLS_C); } assignment_list ')' { zend_do_new_list_end(CLS_C); } + | /* empty */ { zend_do_add_list_element(NULL CLS_CC); } +; + + +array_pair_list: + /* empty */ { zend_do_init_array(&$$, NULL, NULL, 0 CLS_CC); } + | non_empty_array_pair_list possible_comma { $$ = $1; } +; + +non_empty_array_pair_list: + non_empty_array_pair_list ',' expr T_DOUBLE_ARROW expr { zend_do_add_array_element(&$$, &$5, &$3, 0 CLS_CC); } + | non_empty_array_pair_list ',' expr { zend_do_add_array_element(&$$, &$3, NULL, 0 CLS_CC); } + | expr T_DOUBLE_ARROW expr { zend_do_init_array(&$$, &$3, &$1, 0 CLS_CC); } + | expr { zend_do_init_array(&$$, &$1, NULL, 0 CLS_CC); } + | non_empty_array_pair_list ',' expr T_DOUBLE_ARROW '&' w_cvar { zend_do_add_array_element(&$$, &$6, &$3, 1 CLS_CC); } + | non_empty_array_pair_list ',' '&' w_cvar { zend_do_add_array_element(&$$, &$4, NULL, 1 CLS_CC); } + | expr T_DOUBLE_ARROW '&' w_cvar { zend_do_init_array(&$$, &$4, &$1, 1 CLS_CC); } + | '&' w_cvar { zend_do_init_array(&$$, &$2, NULL, 1 CLS_CC); } +; + +encaps_list: + encaps_list encaps_var { zend_do_end_variable_parse(BP_VAR_R, 0 CLS_CC); zend_do_add_variable(&$$, &$1, &$2 CLS_CC); } + | encaps_list T_STRING { zend_do_add_string(&$$, &$1, &$2 CLS_CC); } + | encaps_list T_NUM_STRING { zend_do_add_string(&$$, &$1, &$2 CLS_CC); } + | encaps_list T_ENCAPSED_AND_WHITESPACE { zend_do_add_string(&$$, &$1, &$2 CLS_CC); } + | encaps_list T_CHARACTER { zend_do_add_char(&$$, &$1, &$2 CLS_CC); } + | encaps_list T_BAD_CHARACTER { zend_do_add_string(&$$, &$1, &$2 CLS_CC); } + | encaps_list '[' { $2.u.constant.value.lval = (long) '['; zend_do_add_char(&$$, &$1, &$2 CLS_CC); } + | encaps_list ']' { $2.u.constant.value.lval = (long) ']'; zend_do_add_char(&$$, &$1, &$2 CLS_CC); } + | encaps_list '{' { $2.u.constant.value.lval = (long) '{'; zend_do_add_char(&$$, &$1, &$2 CLS_CC); } + | encaps_list '}' { $2.u.constant.value.lval = (long) '}'; zend_do_add_char(&$$, &$1, &$2 CLS_CC); } + | encaps_list T_OBJECT_OPERATOR { znode tmp; $2.u.constant.value.lval = (long) '-'; zend_do_add_char(&tmp, &$1, &$2 CLS_CC); $2.u.constant.value.lval = (long) '>'; zend_do_add_char(&$$, &tmp, &$2 CLS_CC); } + | /* empty */ { zend_do_init_string(&$$ CLS_CC); } + +; + + + +encaps_var: + T_VARIABLE { zend_do_fetch_globals(&$1 CLS_CC); zend_do_begin_variable_parse(CLS_C); fetch_simple_variable(&$$, &$1, 1 CLS_CC); } + | T_VARIABLE '[' { zend_do_begin_variable_parse(CLS_C); } encaps_var_offset ']' { zend_do_fetch_globals(&$1 CLS_CC); fetch_array_begin(&$$, &$1, &$4 CLS_CC); } + | T_VARIABLE T_OBJECT_OPERATOR T_STRING { zend_do_begin_variable_parse(CLS_C); fetch_simple_variable(&$2, &$1, 1 CLS_CC); zend_do_fetch_property(&$$, &$2, &$3 CLS_CC); } + | T_DOLLAR_OPEN_CURLY_BRACES expr '}' { zend_do_begin_variable_parse(CLS_C); fetch_simple_variable(&$$, &$2, 1 CLS_CC); } + | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}' { zend_do_begin_variable_parse(CLS_C); fetch_array_begin(&$$, &$2, &$4 CLS_CC); } + | T_CURLY_OPEN cvar '}' { $$ = $2; } +; + + +encaps_var_offset: + T_STRING { $$ = $1; } + | T_NUM_STRING { $$ = $1; } + | T_VARIABLE { fetch_simple_variable(&$$, &$1, 1 CLS_CC); } +; + + +internal_functions_in_yacc: + T_ISSET '(' cvar ')' { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$3 CLS_CC); } + | T_EMPTY '(' cvar ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 CLS_CC); } + | T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 CLS_CC); } + | T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 CLS_CC); } + | T_EVAL '(' expr ')' { zend_do_include_or_eval(ZEND_EVAL, &$$, &$3 CLS_CC); } + | T_REQUIRE expr { zend_do_include_or_eval(ZEND_REQUIRE, &$$, &$2 CLS_CC); } + | T_REQUIRE_ONCE expr { zend_do_include_or_eval(ZEND_REQUIRE_ONCE, &$$, &$2 CLS_CC); } +; + + +%% + diff --git a/Zend/zend_modules.h b/Zend/zend_modules.h new file mode 100644 index 0000000000..0dd01306c8 --- /dev/null +++ b/Zend/zend_modules.h @@ -0,0 +1,85 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#ifndef MODULES_H +#define MODULES_H + +#define INIT_FUNC_ARGS int type, int module_number ELS_DC +#define INIT_FUNC_ARGS_PASSTHRU type, module_number ELS_CC +#define SHUTDOWN_FUNC_ARGS int type, int module_number +#define SHUTDOWN_FUNC_ARGS_PASSTHRU type, module_number +#define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module +#define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module +#define GINIT_FUNC_ARGS void +#define GINIT_FUNC_ARGS_PASSTHRU + +extern unsigned char first_arg_force_ref[]; +extern unsigned char first_arg_allow_ref[]; +extern unsigned char second_arg_force_ref[]; +extern unsigned char second_arg_allow_ref[]; + +#include "zend.h" + +#define ZEND_MODULE_API_NO 20000809 +#ifdef ZTS +#define USING_ZTS 1 +#else +#define USING_ZTS 0 +#endif + +#define STANDARD_MODULE_PROPERTIES_EX 0, 0, 0, NULL, 0, ZEND_DEBUG, USING_ZTS, ZEND_MODULE_API_NO + +#define STANDARD_MODULE_PROPERTIES \ + NULL, NULL, STANDARD_MODULE_PROPERTIES_EX + +#define MODULE_PERSISTENT 1 +#define MODULE_TEMPORARY 2 + +typedef struct _zend_module_entry zend_module_entry; + +struct _zend_module_entry { + char *name; + zend_function_entry *functions; + int (*module_startup_func)(INIT_FUNC_ARGS); + int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); + int (*request_startup_func)(INIT_FUNC_ARGS); + int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); + void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); + int (*global_startup_func)(void); + int (*global_shutdown_func)(void); + int globals_id; + int module_started; + unsigned char type; + void *handle; + int module_number; + unsigned char zend_debug; + unsigned char zts; + unsigned int zend_api; +}; + + +extern ZEND_API HashTable module_registry; + +void module_destructor(zend_module_entry *module); +int module_registry_cleanup(zend_module_entry *module); +int module_registry_request_startup(zend_module_entry *module); + +#define ZEND_MODULE_DTOR (void (*)(void *)) module_destructor +#endif diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c new file mode 100644 index 0000000000..a232e56af5 --- /dev/null +++ b/Zend/zend_operators.c @@ -0,0 +1,1753 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2000 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 0.92 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.zend.com/license/0_92.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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + + +#include +#include +#include +#include +#include +#include + +#include "zend.h" +#include "zend_operators.h" +#include "zend_variables.h" +#include "zend_globals.h" +#include "zend_list.h" +#include "zend_fast_cache.h" + +#if 0&&WITH_BCMATH +#include "ext/bcmath/number.h" +#endif + +ZEND_API int zend_atoi(const char *str, int str_len) +{ + int retval; + + if (!str_len) { + str_len = strlen(str); + } + retval = atoi(str); + if (str_len>0) { + switch (str[str_len-1]) { + case 'k': + case 'K': + retval *= 1024; + break; + case 'm': + case 'M': + retval *= 1048576; + break; + } + } + return retval; +} + + +ZEND_API double zend_string_to_double(const char *number, zend_uint length) +{ + double divisor = 10.0; + double result = 0.0; + double exponent; + const char *end = number+length; + const char *digit = number; + + if (!length) { + return result; + } + + while (digit < end) { + if ((*digit <= '9' && *digit >= '0')) { + result *= 10; + result += *digit - '0'; + } else if (*digit == '.') { + digit++; + break; + } else if (toupper(*digit) == 'E') { + exponent = (double) atoi(digit+1); + result *= pow(10.0, exponent); + return result; + } else { + return result; + } + digit++; + } + + while (digit < end) { + if ((*digit <= '9' && *digit >= '0')) { + result += (*digit - '0') / divisor; + divisor *= 10; + } else if (toupper(*digit) == 'E') { + exponent = (double) atoi(digit+1); + result *= pow(10.0, exponent); + return result; + } else { + return result; + } + digit++; + } + return result; +} + + +ZEND_API void convert_scalar_to_number(zval *op) +{ + char *strval; + + if (op->type == IS_STRING) { + strval = op->value.str.val; + switch ((op->type=is_numeric_string(strval, op->value.str.len, &op->value.lval, &op->value.dval))) { + case IS_DOUBLE: + case IS_LONG: + break; +#if 0&&WITH_BCMATH + case FLAG_IS_BC: + op->type = IS_DOUBLE; /* may have lost significant digits */ + break; +#endif + default: + op->value.lval = strtol(op->value.str.val, NULL, 10); + op->type = IS_LONG; + break; + } + STR_FREE(strval); + } else if (op->type==IS_BOOL || op->type==IS_RESOURCE) { + op->type = IS_LONG; + } else if (op->type==IS_NULL) { + op->type = IS_LONG; + op->value.lval = 0; + } +} + +#define zendi_convert_scalar_to_number(op, holder, result) \ + if (op==result) { \ + convert_scalar_to_number(op); \ + } else if ((op)->type == IS_STRING) { \ + switch (((holder).type=is_numeric_string((op)->value.str.val, (op)->value.str.len, &(holder).value.lval, &(holder).value.dval))) { \ + case IS_DOUBLE: \ + case IS_LONG: \ + break; \ + case FLAG_IS_BC: \ + (holder).type = IS_DOUBLE; /* may have lost significant digits */ \ + break; \ + default: \ + (holder).value.lval = strtol((op)->value.str.val, NULL, 10); \ + (holder).type = IS_LONG; \ + break; \ + } \ + (op) = &(holder); \ + } else if ((op)->type==IS_BOOL || (op)->type==IS_RESOURCE) { \ + (holder).value.lval = (op)->value.lval; \ + (holder).type = IS_LONG; \ + (op) = &(holder); \ + } else if ((op)->type==IS_NULL) { \ + (holder).value.lval = 0; \ + (holder).type = IS_LONG; \ + (op) = &(holder); \ + } + + + +#define zendi_convert_to_long(op, holder, result) \ + if (op==result) { \ + convert_to_long(op); \ + } else if ((op)->type==IS_BOOL || (op)->type==IS_RESOURCE) { \ + (holder).value.lval = (op)->value.lval; \ + (holder).type = IS_LONG; \ + (op) = &(holder); \ + } else if ((op)->type != IS_LONG) { \ + switch ((op)->type) { \ + case IS_NULL: \ + (holder).value.lval = 0; \ + break; \ + case IS_DOUBLE: \ + (holder).value.lval = (long) (op)->value.dval; \ + break; \ + case IS_STRING: \ + (holder).value.lval = strtol((op)->value.str.val, NULL, 10); \ + break; \ + case IS_ARRAY: \ + (holder).value.lval = (zend_hash_num_elements((op)->value.ht)?1:0); \ + break; \ + case IS_OBJECT: \ + (holder).value.lval = (zend_hash_num_elements((op)->value.obj.properties)?1:0); \ + break; \ + default: \ + zend_error(E_WARNING, "Cannot convert to ordinal value"); \ + (holder).value.lval = 0; \ + break; \ + } \ + (holder).type = IS_LONG; \ + (op) = &(holder); \ + } + + +#define zendi_convert_to_boolean(op, holder, result) \ + if (op==result) { \ + convert_to_boolean(op); \ + } else if ((op)->type != IS_BOOL) { \ + switch ((op)->type) { \ + case IS_NULL: \ + (holder).value.lval = 0; \ + break; \ + case IS_RESOURCE: \ + case IS_LONG: \ + (holder).value.lval = ((op)->value.lval ? 1 : 0); \ + break; \ + case IS_DOUBLE: \ + (holder).value.lval = ((op)->value.dval ? 1 : 0); \ + break; \ + case IS_STRING: \ + if ((op)->value.str.len == 0 \ + || ((op)->value.str.len==1 && (op)->value.str.val[0]=='0')) { \ + (holder).value.lval = 0; \ + } else { \ + (holder).value.lval = 1; \ + } \ + break; \ + case IS_ARRAY: \ + (holder).value.lval = (zend_hash_num_elements((op)->value.ht)?1:0); \ + break; \ + case IS_OBJECT: \ + (holder).value.lval = (zend_hash_num_elements((op)->value.obj.properties)?1:0); \ + break; \ + default: \ + (holder).value.lval = 0; \ + break; \ + } \ + (holder).type = IS_BOOL; \ + (op) = &(holder); \ + } + + +ZEND_API void convert_to_long(zval *op) +{ + convert_to_long_base(op, 10); +} + + +ZEND_API void convert_to_long_base(zval *op, int base) +{ + char *strval; + long tmp; + + switch (op->type) { + case IS_NULL: + op->value.lval = 0; + break; + case IS_RESOURCE: + case IS_BOOL: + case IS_LONG: + break; + case IS_DOUBLE: + op->value.lval = (long) op->value.dval; + break; + case IS_STRING: + strval = op->value.str.val; + op->value.lval = strtol(strval, NULL, base); + STR_FREE(strval); + break; + case IS_ARRAY: + tmp = (zend_hash_num_elements(op->value.ht)?1:0); + zval_dtor(op); + op->value.lval = tmp; + break; + case IS_OBJECT: + tmp = (zend_hash_num_elements(op->value.obj.properties)?1:0); + zval_dtor(op); + op->value.lval = tmp; + break; + default: + zend_error(E_WARNING, "Cannot convert to ordinal value"); + zval_dtor(op); + op->value.lval = 0; + break; + } + + op->type = IS_LONG; +} + + +ZEND_API void convert_to_double(zval *op) +{ + char *strval; + double tmp; + + switch (op->type) { + case IS_NULL: + op->value.dval = 0.0; + break; + case IS_RESOURCE: + case IS_BOOL: + case IS_LONG: + op->value.dval = (double) op->value.lval; + break; + case IS_DOUBLE: + break; + case IS_STRING: + strval = op->value.str.val; + + op->value.dval = strtod(strval, NULL); + STR_FREE(strval); + break; + case IS_ARRAY: + tmp = (zend_hash_num_elements(op->value.ht)?1:0); + zval_dtor(op); + op->value.dval = tmp; + break; + case IS_OBJECT: + tmp = (zend_hash_num_elements(op->value.obj.properties)?1:0); + zval_dtor(op); + op->value.dval = tmp; + break; + default: + zend_error(E_WARNING, "Cannot convert to real value (type=%d)", op->type); + zval_dtor(op); + op->value.dval = 0; + break; + } + op->type = IS_DOUBLE; +} + + +ZEND_API void convert_to_null(zval *op) +{ + zval_dtor(op); + op->type = IS_NULL; +} + + +ZEND_API void convert_to_boolean(zval *op) +{ + char *strval; + int tmp; + + switch (op->type) { + case IS_BOOL: + break; + case IS_NULL: + op->value.lval = 0; + break; + case IS_RESOURCE: + case IS_LONG: + op->value.lval = (op->value.lval ? 1 : 0); + break; + case IS_DOUBLE: + op->value.lval = (op->value.dval ? 1 : 0); + break; + case IS_STRING: + strval = op->value.str.val; + + if (op->value.str.len == 0 + || (op->value.str.len==1 && op->value.str.val[0]=='0')) { + op->value.lval = 0; + } else { + op->value.lval = 1; + } + STR_FREE(strval); + break; + case IS_ARRAY: + tmp = (zend_hash_num_elements(op->value.ht)?1:0); + zval_dtor(op); + op->value.lval = tmp; + break; + case IS_OBJECT: + tmp = (zend_hash_num_elements(op->value.obj.properties)?1:0); + zval_dtor(op); + op->value.lval = tmp; + break; + default: + zval_dtor(op); + op->value.lval = 0; + break; + } + op->type = IS_BOOL; +} + + +ZEND_API void _convert_to_string(zval *op ZEND_FILE_LINE_DC) +{ + long lval; + double dval; + ELS_FETCH(); + + switch (op->type) { + case IS_NULL: + op->value.str.val = empty_string; + op->value.str.len = 0; + break; + case IS_STRING: + break; + case IS_BOOL: + if (op->value.lval) { + op->value.str.val = estrndup_rel("1", 1); + op->value.str.len = 1; + } else { + op->value.str.val = empty_string; + op->value.str.len = 0; + } + break; + case IS_RESOURCE: { + long tmp = op->value.lval; + op->value.str.val = (char *) emalloc(sizeof("Resource id #")-1 + MAX_LENGTH_OF_LONG); + op->value.str.len = sprintf(op->value.str.val, "Resource id #%ld", tmp); + break; + } + case IS_LONG: + lval = op->value.lval; + + op->value.str.val = (char *) emalloc_rel(MAX_LENGTH_OF_LONG + 1); + op->value.str.len = zend_sprintf(op->value.str.val, "%ld", lval); /* SAFE */ + break; + case IS_DOUBLE: { + dval = op->value.dval; + op->value.str.val = (char *) emalloc_rel(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); + op->value.str.len = zend_sprintf(op->value.str.val, "%.*G", (int) EG(precision), dval); /* SAFE */ + /* %G already handles removing trailing zeros from the fractional part, yay */ + break; + } + case IS_ARRAY: + zval_dtor(op); + op->value.str.val = estrndup_rel("Array",sizeof("Array")-1); + op->value.str.len = sizeof("Array")-1; + break; + case IS_OBJECT: + zval_dtor(op); + op->value.str.val = estrndup_rel("Object",sizeof("Object")-1); + op->value.str.len = sizeof("Object")-1; + break; + default: + zval_dtor(op); + var_reset(op); + break; + } + op->type = IS_STRING; +} + + +static void convert_scalar_to_array(zval *op, int type) +{ + zval *entry; + + ALLOC_ZVAL(entry); + *entry = *op; + INIT_PZVAL(entry); + + switch (type) { + case IS_ARRAY: + ALLOC_HASHTABLE(op->value.ht); + zend_hash_init(op->value.ht, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_index_update(op->value.ht, 0, (void *) &entry, sizeof(zval *), NULL); + op->type = IS_ARRAY; + break; + case IS_OBJECT: + ALLOC_HASHTABLE(op->value.obj.properties); + zend_hash_init(op->value.obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_update(op->value.obj.properties, "scalar", sizeof("scalar"), (void *) &entry, sizeof(zval *), NULL); + op->value.obj.ce = &zend_standard_class_def; + op->type = IS_OBJECT; + break; + } +} + + +ZEND_API void convert_to_array(zval *op) +{ + switch(op->type) { + case IS_ARRAY: + return; + break; + case IS_OBJECT: + op->type = IS_ARRAY; + op->value.ht = op->value.obj.properties; + return; + case IS_NULL: + ALLOC_HASHTABLE(op->value.ht); + zend_hash_init(op->value.ht, 0, NULL, ZVAL_PTR_DTOR, 0); + op->type = IS_ARRAY; + break; + default: + convert_scalar_to_array(op, IS_ARRAY); + break; + } +} + + +ZEND_API void convert_to_object(zval *op) +{ + switch(op->type) { + case IS_ARRAY: + op->type = IS_OBJECT; + op->value.obj.properties = op->value.ht; + op->value.obj.ce = &zend_standard_class_def; + return; + break; + case IS_OBJECT: + return; + case IS_NULL: + ALLOC_HASHTABLE(op->value.obj.properties); + zend_hash_init(op->value.obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0); + op->value.obj.ce = &zend_standard_class_def; + op->type = IS_OBJECT; + break; + default: + convert_scalar_to_array(op, IS_OBJECT); + break; + } +} + +ZEND_API void multi_convert_to_long_ex(int argc, ...) +{ + zval **arg; + va_list ap; + + va_start(ap, argc); + + while (argc--) { + arg = va_arg(ap, zval **); + convert_to_long_ex(arg); + } + + va_end(ap); +} + +ZEND_API void multi_convert_to_double_ex(int argc, ...) +{ + zval **arg; + va_list ap; + + va_start(ap, argc); + + while (argc--) { + arg = va_arg(ap, zval **); + convert_to_double_ex(arg); + } + + va_end(ap); +} + +ZEND_API void multi_convert_to_string_ex(int argc, ...) +{ + zval **arg; + va_list ap; + + va_start(ap, argc); + + while (argc--) { + arg = va_arg(ap, zval **); + convert_to_string_ex(arg); + } + + va_end(ap); +} + +ZEND_API int add_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + if (op1->type == IS_ARRAY && op2->type == IS_ARRAY) { + zval *tmp; + + if ((result == op1) && (result == op2)) { + /* $a += $a */ + return SUCCESS; + } + if (result != op1) { + /* $a += $b */ + *result = *op1; + zval_copy_ctor(result); + } + zend_hash_merge(result->value.ht, op2->value.ht, (void (*)(void *pData)) zval_add_ref, (void *) &tmp, sizeof(zval *), 0); + return SUCCESS; + } + zendi_convert_scalar_to_number(op1, op1_copy, result); + zendi_convert_scalar_to_number(op2, op2_copy, result); + + + if (op1->type == IS_LONG && op2->type == IS_LONG) { + double dval = (double) op1->value.lval + (double) op2->value.lval; + + if (dval > (double) LONG_MAX) { + result->value.dval = dval; + result->type = IS_DOUBLE; + } else { + result->value.lval = op1->value.lval + op2->value.lval; + result->type = IS_LONG; + } + return SUCCESS; + } + if ((op1->type == IS_DOUBLE && op2->type == IS_LONG) + || (op1->type == IS_LONG && op2->type == IS_DOUBLE)) { + result->value.dval = (op1->type == IS_LONG ? + (((double) op1->value.lval) + op2->value.dval) : + (op1->value.dval + ((double) op2->value.lval))); + result->type = IS_DOUBLE; + return SUCCESS; + } + if (op1->type == IS_DOUBLE && op2->type == IS_DOUBLE) { + result->type = IS_DOUBLE; + result->value.dval = op1->value.dval + op2->value.dval; + return SUCCESS; + } + zend_error(E_ERROR, "Unsupported operand types"); + return FAILURE; /* unknown datatype */ +} + + +ZEND_API int sub_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + zendi_convert_scalar_to_number(op1, op1_copy, result); + zendi_convert_scalar_to_number(op2, op2_copy, result); + + if (op1->type == IS_LONG && op2->type == IS_LONG) { + double dval = (double) op1->value.lval - (double) op2->value.lval; + + if (dval < (double) LONG_MIN) { + result->value.dval = dval; + result->type = IS_DOUBLE; + } else { + result->value.lval = op1->value.lval - op2->value.lval; + result->type = IS_LONG; + } + return SUCCESS; + } + if ((op1->type == IS_DOUBLE && op2->type == IS_LONG) + || (op1->type == IS_LONG && op2->type == IS_DOUBLE)) { + result->value.dval = (op1->type == IS_LONG ? + (((double) op1->value.lval) - op2->value.dval) : + (op1->value.dval - ((double) op2->value.lval))); + result->type = IS_DOUBLE; + return SUCCESS; + } + if (op1->type == IS_DOUBLE && op2->type == IS_DOUBLE) { + result->type = IS_DOUBLE; + result->value.dval = op1->value.dval - op2->value.dval; + return SUCCESS; + } + zend_error(E_ERROR, "Unsupported operand types"); + return FAILURE; /* unknown datatype */ +} + + +ZEND_API int mul_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + zendi_convert_scalar_to_number(op1, op1_copy, result); + zendi_convert_scalar_to_number(op2, op2_copy, result); + + if (op1->type == IS_LONG && op2->type == IS_LONG) { + double dval = (double) op1->value.lval * (double) op2->value.lval; + + if (dval > (double) LONG_MAX) { + result->value.dval = dval; + result->type = IS_DOUBLE; + } else { + result->value.lval = op1->value.lval * op2->value.lval; + result->type = IS_LONG; + } + return SUCCESS; + } + if ((op1->type == IS_DOUBLE && op2->type == IS_LONG) + || (op1->type == IS_LONG && op2->type == IS_DOUBLE)) { + result->value.dval = (op1->type == IS_LONG ? + (((double) op1->value.lval) * op2->value.dval) : + (op1->value.dval * ((double) op2->value.lval))); + result->type = IS_DOUBLE; + return SUCCESS; + } + if (op1->type == IS_DOUBLE && op2->type == IS_DOUBLE) { + result->type = IS_DOUBLE; + result->value.dval = op1->value.dval * op2->value.dval; + return SUCCESS; + } + zend_error(E_ERROR, "Unsupported operand types"); + return FAILURE; /* unknown datatype */ +} + +ZEND_API int div_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + zendi_convert_scalar_to_number(op1, op1_copy, result); + zendi_convert_scalar_to_number(op2, op2_copy, result); + + if ((op2->type == IS_LONG && op2->value.lval == 0) || (op2->type == IS_DOUBLE && op2->value.dval == 0.0)) { + zend_error(E_WARNING, "Division by zero"); + var_reset(result); + return FAILURE; /* division by zero */ + } + if (op1->type == IS_LONG && op2->type == IS_LONG) { + if (op1->value.lval % op2->value.lval == 0) { /* integer */ + result->type = IS_LONG; + result->value.lval = op1->value.lval / op2->value.lval; + } else { + result->type = IS_DOUBLE; + result->value.dval = ((double) op1->value.lval) / op2->value.lval; + } + return SUCCESS; + } + if ((op1->type == IS_DOUBLE && op2->type == IS_LONG) + || (op1->type == IS_LONG && op2->type == IS_DOUBLE)) { + result->value.dval = (op1->type == IS_LONG ? + (((double) op1->value.lval) / op2->value.dval) : + (op1->value.dval / ((double) op2->value.lval))); + result->type = IS_DOUBLE; + return SUCCESS; + } + if (op1->type == IS_DOUBLE && op2->type == IS_DOUBLE) { + result->type = IS_DOUBLE; + result->value.dval = op1->value.dval / op2->value.dval; + return SUCCESS; + } + zend_error(E_ERROR, "Unsupported operand types"); + return FAILURE; /* unknown datatype */ +} + + +ZEND_API int mod_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + zendi_convert_to_long(op1, op1_copy, result); + zendi_convert_to_long(op2, op2_copy, result); + + if (op2->value.lval == 0) { + var_reset(result); + return FAILURE; /* modulus by zero */ + } + + result->type = IS_LONG; + result->value.lval = op1->value.lval % op2->value.lval; + return SUCCESS; +} + + +ZEND_API int boolean_or_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + result->type = IS_BOOL; + + zendi_convert_to_boolean(op1, op1_copy, result); + if (op1->value.lval) { + result->value.lval = 1; + return SUCCESS; + } + zendi_convert_to_boolean(op2, op2_copy, result); + if (op2->value.lval) { + result->value.lval = 1; + return SUCCESS; + } + result->value.lval = 0; + return SUCCESS; +} + + +ZEND_API int boolean_and_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + result->type = IS_BOOL; + + zendi_convert_to_boolean(op1, op1_copy, result); + if (!op1->value.lval) { + result->value.lval = 0; + return SUCCESS; + } + zendi_convert_to_boolean(op2, op2_copy, result); + if (!op2->value.lval) { + result->value.lval = 0; + return SUCCESS; + } + result->value.lval = 1; + return SUCCESS; +} + + +ZEND_API int boolean_xor_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + result->type = IS_BOOL; + + zendi_convert_to_boolean(op1, op1_copy, result); + zendi_convert_to_boolean(op2, op2_copy, result); + result->value.lval = op1->value.lval ^ op2->value.lval; + return SUCCESS; +} + + +ZEND_API int boolean_not_function(zval *result, zval *op1) +{ + zval op1_copy; + + zendi_convert_to_boolean(op1, op1_copy, result); + + result->type = IS_BOOL; + result->value.lval = !op1->value.lval; + return SUCCESS; +} + + +ZEND_API int bitwise_not_function(zval *result, zval *op1) +{ + zval op1_copy = *op1; + + op1 = &op1_copy; + + if (op1->type == IS_DOUBLE) { + op1->value.lval = (long) op1->value.dval; + op1->type = IS_LONG; + } + if (op1->type == IS_LONG) { + result->value.lval = ~op1->value.lval; + result->type = IS_LONG; + return SUCCESS; + } + if (op1->type == IS_STRING) { + int i; + + result->type = IS_STRING; + result->value.str.val = estrndup(op1->value.str.val, op1->value.str.len); + result->value.str.len = op1->value.str.len; + for (i = 0; i < op1->value.str.len; i++) { + result->value.str.val[i] = ~op1->value.str.val[i]; + } + return SUCCESS; + } + zend_error(E_ERROR, "Unsupported operand types"); + return FAILURE; /* unknown datatype */ +} + + +ZEND_API int bitwise_or_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + if (op1->type == IS_STRING && op2->type == IS_STRING) { + zval *longer, *shorter; + char *result_str; + int i, result_len; + + if (op1->value.str.len >= op2->value.str.len) { + longer = op1; + shorter = op2; + } else { + longer = op2; + shorter = op1; + } + + result->type = IS_STRING; + result_len = longer->value.str.len; + result_str = estrndup(longer->value.str.val, longer->value.str.len); + for (i = 0; i < shorter->value.str.len; i++) { + result_str[i] |= shorter->value.str.val[i]; + } + if (result==op1) { + efree(result->value.str.val); + } + result->value.str.val = result_str; + result->value.str.len = result_len; + return SUCCESS; + } + zendi_convert_to_long(op1, op1_copy, result); + zendi_convert_to_long(op2, op2_copy, result); + + result->type = IS_LONG; + result->value.lval = op1->value.lval | op2->value.lval; + return SUCCESS; +} + + +ZEND_API int bitwise_and_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + if (op1->type == IS_STRING && op2->type == IS_STRING) { + zval *longer, *shorter; + char *result_str; + int i, result_len; + + if (op1->value.str.len >= op2->value.str.len) { + longer = op1; + shorter = op2; + } else { + longer = op2; + shorter = op1; + } + + result->type = IS_STRING; + result_len = shorter->value.str.len; + result_str = estrndup(shorter->value.str.val, shorter->value.str.len); + for (i = 0; i < shorter->value.str.len; i++) { + result_str[i] &= longer->value.str.val[i]; + } + if (result==op1) { + efree(result->value.str.val); + } + result->value.str.val = result_str; + result->value.str.len = result_len; + return SUCCESS; + } + + + zendi_convert_to_long(op1, op1_copy, result); + zendi_convert_to_long(op2, op2_copy, result); + + result->type = IS_LONG; + result->value.lval = op1->value.lval & op2->value.lval; + return SUCCESS; +} + + +ZEND_API int bitwise_xor_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + if (op1->type == IS_STRING && op2->type == IS_STRING) { + zval *longer, *shorter; + char *result_str; + int i, result_len; + + if (op1->value.str.len >= op2->value.str.len) { + longer = op1; + shorter = op2; + } else { + longer = op2; + shorter = op1; + } + + result->type = IS_STRING; + result_len = shorter->value.str.len; + result_str = estrndup(shorter->value.str.val, shorter->value.str.len); + for (i = 0; i < shorter->value.str.len; i++) { + result_str[i] ^= longer->value.str.val[i]; + } + if (result==op1) { + efree(result->value.str.val); + } + result->value.str.val = result_str; + result->value.str.len = result_len; + return SUCCESS; + } + + zendi_convert_to_long(op1, op1_copy, result); + zendi_convert_to_long(op2, op2_copy, result); + + result->type = IS_LONG; + result->value.lval = op1->value.lval ^ op2->value.lval; + return SUCCESS; +} + + +ZEND_API int shift_left_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + zendi_convert_to_long(op1, op1_copy, result); + zendi_convert_to_long(op2, op2_copy, result); + result->value.lval = op1->value.lval << op2->value.lval; + result->type = IS_LONG; + return SUCCESS; +} + + +ZEND_API int shift_right_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + zendi_convert_to_long(op1, op1_copy, result); + zendi_convert_to_long(op2, op2_copy, result); + result->value.lval = op1->value.lval >> op2->value.lval; + result->type = IS_LONG; + return SUCCESS; +} + + + +/* must support result==op1 */ +ZEND_API int add_char_to_string(zval *result, zval *op1, zval *op2) +{ + result->value.str.len = op1->value.str.len + 1; + result->value.str.val = (char *) erealloc(op1->value.str.val, result->value.str.len+1); + result->value.str.val[result->value.str.len - 1] = (char) op2->value.lval; + result->value.str.val[result->value.str.len] = 0; + result->type = IS_STRING; + return SUCCESS; +} + + +/* must support result==op1 */ +ZEND_API int add_string_to_string(zval *result, zval *op1, zval *op2) +{ + int length = op1->value.str.len + op2->value.str.len; + result->value.str.val = (char *) erealloc(op1->value.str.val, length+1); + memcpy(result->value.str.val+op1->value.str.len, op2->value.str.val, op2->value.str.len); + result->value.str.val[length] = 0; + result->value.str.len = length; + result->type = IS_STRING; + return SUCCESS; +} + + +ZEND_API int concat_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + int use_copy1, use_copy2; + + + zend_make_printable_zval(op1, &op1_copy, &use_copy1); + zend_make_printable_zval(op2, &op2_copy, &use_copy2); + + if (use_copy1) { + op1 = &op1_copy; + } + if (use_copy2) { + op2 = &op2_copy; + } + if (result==op1) { /* special case, perform operations on result */ + uint res_len = op1->value.str.len + op2->value.str.len; + + if (result->value.str.len == 0) { /* handle empty_string */ + STR_FREE(result->value.str.val); + result->value.str.val = emalloc(res_len+1); + } else { + result->value.str.val = erealloc(result->value.str.val, res_len+1); + } + memcpy(result->value.str.val+result->value.str.len, op2->value.str.val, op2->value.str.len); + result->value.str.val[res_len]=0; + result->value.str.len = res_len; + } else { + result->value.str.len = op1->value.str.len + op2->value.str.len; + result->value.str.val = (char *) emalloc(result->value.str.len + 1); + memcpy(result->value.str.val, op1->value.str.val, op1->value.str.len); + memcpy(result->value.str.val+op1->value.str.len, op2->value.str.val,op2->value.str.len); + result->value.str.val[result->value.str.len] = 0; + result->type = IS_STRING; + } + if (use_copy1) { + zval_dtor(op1); + } + if (use_copy2) { + zval_dtor(op2); + } + return SUCCESS; +} + + +ZEND_API int string_compare_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + int use_copy1, use_copy2; + + zend_make_printable_zval(op1, &op1_copy, &use_copy1); + zend_make_printable_zval(op2, &op2_copy, &use_copy2); + + if (use_copy1) { + op1 = &op1_copy; + } + if (use_copy2) { + op2 = &op2_copy; + } + + result->value.lval = zend_binary_zval_strcmp(op1, op2); + result->type = IS_LONG; + + if (use_copy1) { + zval_dtor(op1); + } + if (use_copy2) { + zval_dtor(op2); + } + return SUCCESS; +} + +ZEND_API int numeric_compare_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + op1_copy = *op1; + zval_copy_ctor(&op1_copy); + + op2_copy = *op2; + zval_copy_ctor(&op2_copy); + + convert_to_double(&op1_copy); + convert_to_double(&op2_copy); + + result->value.lval = ZEND_NORMALIZE_BOOL(op1_copy.value.dval-op2_copy.value.dval); + result->type = IS_LONG; + + return SUCCESS; +} + + +ZEND_API int compare_function(zval *result, zval *op1, zval *op2) +{ + zval op1_copy, op2_copy; + + if ((op1->type == IS_NULL && op2->type == IS_STRING) + || (op2->type == IS_NULL && op1->type == IS_STRING)) { + if (op1->type == IS_NULL) { + result->type = IS_LONG; + result->value.lval = zend_binary_strcmp("", 0, op2->value.str.val, op2->value.str.len); + return SUCCESS; + } else { + result->type = IS_LONG; + result->value.lval = zend_binary_strcmp(op1->value.str.val, op1->value.str.len, "", 0); + return SUCCESS; + } + } + + if (op1->type == IS_STRING && op2->type == IS_STRING) { + zendi_smart_strcmp(result, op1, op2); + return SUCCESS; + } + + if (op1->type == IS_BOOL || op2->type == IS_BOOL + || op1->type == IS_NULL || op2->type == IS_NULL) { + zendi_convert_to_boolean(op1, op1_copy, result); + zendi_convert_to_boolean(op2, op2_copy, result); + result->type = IS_LONG; + result->value.lval = ZEND_NORMALIZE_BOOL(op1->value.lval-op2->value.lval); + return SUCCESS; + } + + zendi_convert_scalar_to_number(op1, op1_copy, result); + zendi_convert_scalar_to_number(op2, op2_copy, result); + + if (op1->type == IS_LONG && op2->type == IS_LONG) { + result->type = IS_LONG; + result->value.lval = ZEND_NORMALIZE_BOOL(op1->value.lval-op2->value.lval); + return SUCCESS; + } + if ((op1->type == IS_DOUBLE || op1->type == IS_LONG) + && (op2->type == IS_DOUBLE || op2->type == IS_LONG)) { + result->value.dval = (op1->type == IS_LONG ? (double) op1->value.lval : op1->value.dval) - (op2->type == IS_LONG ? (double) op2->value.lval : op2->value.dval); + result->value.lval = ZEND_NORMALIZE_BOOL(result->value.dval); + result->type = IS_LONG; + return SUCCESS; + } + if (op1->type==IS_ARRAY && op2->type==IS_ARRAY) { + zend_compare_arrays(result, op1, op2); + return SUCCESS; + } + + if (op1->type==IS_OBJECT && op2->type==IS_OBJECT) { + zend_compare_objects(result, op1, op2); + return SUCCESS; + } + + if (op1->type==IS_ARRAY) { + result->value.lval = 1; + result->type = IS_LONG; + return SUCCESS; + } + if (op2->type==IS_ARRAY) { + result->value.lval = -1; + result->type = IS_LONG; + return SUCCESS; + } + if (op1->type==IS_OBJECT) { + result->value.lval = 1; + result->type = IS_LONG; + return SUCCESS; + } + if (op2->type==IS_OBJECT) { + result->value.lval = -1; + result->type = IS_LONG; + return SUCCESS; + } + + var_reset(result); + return FAILURE; +} + + +static int hash_zval_identical_function(const zval **z1, const zval **z2) +{ + zval result; + + /* is_identical_function() returns 1 in case of identity and 0 in case + * of a difference; + * whereas this comparison function is expected to return 0 on identity, + * and non zero otherwise. + */ + if (is_identical_function(&result, (zval *) *z1, (zval *) *z2)==FAILURE) { + return 1; + } + return !result.value.lval; +} + + +ZEND_API int is_identical_function(zval *result, zval *op1, zval *op2) +{ + result->type = IS_BOOL; + if (op1->type != op2->type) { + result->value.lval = 0; + return SUCCESS; + } + switch (op1->type) { + case IS_NULL: + result->value.lval = (op2->type==IS_NULL); + break; + case IS_BOOL: + case IS_LONG: + case IS_RESOURCE: + result->value.lval = (op1->value.lval == op2->value.lval); + break; + case IS_DOUBLE: + result->value.lval = (op1->value.dval == op2->value.dval); + break; + case IS_STRING: + if ((op1->value.str.len == op2->value.str.len) + && (!memcmp(op1->value.str.val, op2->value.str.val, op1->value.str.len))) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + break; + case IS_ARRAY: + if (zend_hash_compare(op1->value.ht, op2->value.ht, (compare_func_t) hash_zval_identical_function, 1)==0) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + break; + case IS_OBJECT: + if (op1->value.obj.ce != op2->value.obj.ce) { + result->value.lval = 0; + } else { + if (zend_hash_compare(op1->value.obj.properties, op2->value.obj.properties, (compare_func_t) hash_zval_identical_function, 1)==0) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + } + break; + default: + var_reset(result); + return FAILURE; + } + return SUCCESS; +} + + +ZEND_API int is_not_identical_function(zval *result, zval *op1, zval *op2) +{ + result->type = IS_BOOL; + if (is_identical_function( result, op1, op2 ) == FAILURE) { + return FAILURE; + } + result->value.lval = !result->value.lval; + return SUCCESS; +} + + +ZEND_API int is_equal_function(zval *result, zval *op1, zval *op2) +{ + if (compare_function(result, op1, op2) == FAILURE) { + return FAILURE; + } + convert_to_boolean(result); + if (result->value.lval == 0) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + return SUCCESS; +} + + +ZEND_API int is_not_equal_function(zval *result, zval *op1, zval *op2) +{ + if (compare_function(result, op1, op2) == FAILURE) { + return FAILURE; + } + convert_to_boolean(result); + if (result->value.lval) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + return SUCCESS; +} + + +ZEND_API int is_smaller_function(zval *result, zval *op1, zval *op2) +{ + if (compare_function(result, op1, op2) == FAILURE) { + return FAILURE; + } + if (result->type == IS_LONG) { + result->type = IS_BOOL; + if (result->value.lval < 0) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + return SUCCESS; + } + if (result->type == IS_DOUBLE) { + result->type = IS_BOOL; + if (result->value.dval < 0) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + return SUCCESS; + } + zend_error(E_ERROR, "Unsupported operand types"); + return FAILURE; +} + + +ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2) +{ + if (compare_function(result, op1, op2) == FAILURE) { + return FAILURE; + } + if (result->type == IS_LONG) { + result->type = IS_BOOL; + if (result->value.lval <= 0) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + return SUCCESS; + } + if (result->type == IS_DOUBLE) { + result->type = IS_BOOL; + if (result->value.dval <= 0) { + result->value.lval = 1; + } else { + result->value.lval = 0; + } + return SUCCESS; + } + zend_error(E_ERROR, "Unsupported operand types"); + return FAILURE; +} + + +#define LOWER_CASE 1 +#define UPPER_CASE 2 +#define NUMERIC 3 + + +static void increment_string(zval *str) +{ + int carry=0; + int pos=str->value.str.len-1; + char *s=str->value.str.val; + char *t; + int last=0; /* Shut up the compiler warning */ + int ch; + + if (str->value.str.len == 0) { + STR_FREE(str->value.str.val); + str->value.str.val = estrndup("1", sizeof("1")-1); + str->value.str.len = 1; + return; + } + + while(pos >= 0) { + ch = s[pos]; + if (ch >= 'a' && ch <= 'z') { + if (ch == 'z') { + s[pos] = 'a'; + carry=1; + } else { + s[pos]++; + carry=0; + } + last=LOWER_CASE; + } else if (ch >= 'A' && ch <= 'Z') { + if (ch == 'Z') { + s[pos] = 'A'; + carry=1; + } else { + s[pos]++; + carry=0; + } + last=UPPER_CASE; + } else if (ch >= '0' && ch <= '9') { + if (ch == '9') { + s[pos] = '0'; + carry=1; + } else { + s[pos]++; + carry=0; + } + last = NUMERIC; + } else { + carry=0; + break; + } + if (carry == 0) { + break; + } + pos--; + } + + if (carry) { + t = (char *) emalloc(str->value.str.len+1+1); + memcpy(t+1,str->value.str.val, str->value.str.len); + str->value.str.len++; + t[str->value.str.len] = '\0'; + switch (last) { + case NUMERIC: + t[0] = '1'; + break; + case UPPER_CASE: + t[0] = 'A'; + break; + case LOWER_CASE: + t[0] = 'a'; + break; + } + STR_FREE(str->value.str.val); + str->value.str.val = t; + } +} + + +ZEND_API int increment_function(zval *op1) +{ + switch (op1->type) { + case IS_LONG: + op1->value.lval++; + break; + case IS_DOUBLE: + op1->value.dval = op1->value.dval + 1; + break; + case IS_NULL: + op1->value.lval = 1; + op1->type = IS_LONG; + break; + case IS_STRING: /* Perl style string increment */ + increment_string(op1); + break; + default: + return FAILURE; + } + return SUCCESS; +} + + +ZEND_API int decrement_function(zval *op1) +{ + long lval; + + switch (op1->type) { + case IS_LONG: + op1->value.lval--; + break; + case IS_DOUBLE: + op1->value.dval = op1->value.dval - 1; + break; + case IS_STRING: /* Like perl we only support string increment */ + if (op1->value.str.len==0) { /* consider as 0 */ + STR_FREE(op1->value.str.val); + op1->value.lval = -1; + op1->type = IS_LONG; + break; + } else if (is_numeric_string(op1->value.str.val, op1->value.str.len, &lval, NULL)==IS_LONG) { /* long */ + STR_FREE(op1->value.str.val); + op1->value.lval = lval-1; + op1->type = IS_LONG; + break; + } + break; + default: + return FAILURE; + } + + return SUCCESS; +} + + +ZEND_API int zval_is_true(zval *op) +{ + convert_to_boolean(op); + return (op->value.lval ? 1 : 0); +} + + +ZEND_API void zend_str_tolower(char *str, unsigned int length) +{ + register char *p=str, *end=p+length; + + while (pvalue.str.val, s1->value.str.len, s2->value.str.val, s2->value.str.len); +} + +ZEND_API int zend_binary_zval_strncmp(zval *s1, zval *s2, zval *s3) +{ + return zend_binary_strncmp(s1->value.str.val, s1->value.str.len, s2->value.str.val, s2->value.str.len, s3->value.lval); +} + + +ZEND_API int zend_binary_zval_strcasecmp(zval *s1, zval *s2) +{ + return zend_binary_strcasecmp(s1->value.str.val, s1->value.str.len, s2->value.str.val, s2->value.str.len); +} + + +ZEND_API int zend_binary_zval_strncasecmp(zval *s1, zval *s2, zval *s3) +{ + return zend_binary_strncasecmp(s1->value.str.val, s1->value.str.len, s2->value.str.val, s2->value.str.len, s3->value.lval); +} + + +ZEND_API void zendi_smart_strcmp(zval *result, zval *s1, zval *s2) +{ + int ret1,ret2; + long lval1, lval2; + double dval1, dval2; + + if ((ret1=is_numeric_string(s1->value.str.val, s1->value.str.len, &lval1, &dval1)) && + (ret2=is_numeric_string(s2->value.str.val, s2->value.str.len, &lval2, &dval2))) { +#if 0&&WITH_BCMATH + if ((ret1==FLAG_IS_BC) || (ret2==FLAG_IS_BC)) { + bc_num first, second; + + /* use the BC math library to compare the numbers */ + init_num(&first); + init_num(&second); + str2num(&first,s1->value.str.val,100); /* this scale should do */ + str2num(&second,s2->value.str.val,100); /* ditto */ + result->value.lval = bc_compare(first,second); + result->value.lval = ZEND_NORMALIZE_BOOL(result->value.lval); + result->type = IS_LONG; + free_num(&first); + free_num(&second); + } else +#endif + if ((ret1==IS_DOUBLE) || (ret2==IS_DOUBLE)) { + if (ret1!=IS_DOUBLE) { + dval1 = strtod(s1->value.str.val, NULL); + } else if (ret2!=IS_DOUBLE) { + dval2 = strtod(s2->value.str.val, NULL); + } + result->value.dval = dval1 - dval2; + result->value.lval = ZEND_NORMALIZE_BOOL(result->value.dval); + result->type = IS_LONG; + } else { /* they both have to be long's */ + result->value.lval = lval1 - lval2; + result->value.lval = ZEND_NORMALIZE_BOOL(result->value.lval); + result->type = IS_LONG; + } + } else { + result->value.lval = zend_binary_zval_strcmp(s1, s2); + result->value.lval = ZEND_NORMALIZE_BOOL(result->value.lval); + result->type = IS_LONG; + } + return; +} + + +static int hash_zval_compare_function(const zval **z1, const zval **z2) +{ + zval result; + + if (compare_function(&result, (zval *) *z1, (zval *) *z2)==FAILURE) { + return 1; + } + return result.value.lval; +} + + + +ZEND_API void zend_compare_symbol_tables(zval *result, HashTable *ht1, HashTable *ht2) +{ + result->type = IS_LONG; + result->value.lval = zend_hash_compare(ht1, ht2, (compare_func_t) hash_zval_compare_function, 0); +} + + +ZEND_API void zend_compare_arrays(zval *result, zval *a1, zval *a2) +{ + zend_compare_symbol_tables(result, a1->value.ht, a2->value.ht); +} + + +ZEND_API void zend_compare_objects(zval *result, zval *o1, zval *o2) +{ + if (o1->value.obj.ce != o2->value.obj.ce) { + result->value.lval = 1; /* Comparing objects of different types is pretty much meaningless */ + result->type = IS_LONG; + return; + } + zend_compare_symbol_tables(result, o1->value.obj.properties, o2->value.obj.properties); +} + + +/* returns 0 for non-numeric string + * returns IS_DOUBLE for floating point string, and assigns the value to *dval (if it's not NULL) + * returns IS_LONG for integer strings, and assigns the value to *lval (if it's not NULL) + * returns FLAG_IS_BC if the number might lose accuracy when converted to a double + */ + +#if 0 + +static inline int is_numeric_string(char *str, int length, long *lval, double *dval) +{ + register char *p=str, *end=str+length; + unsigned char had_period=0,had_exponent=0; + char *end_ptr; + + if (!length) { + return 0; + } + switch (*p) { + case '-': + case '+': + while (*++p==' '); /* ignore spaces after the sign */ + break; + default: + break; + } + while (p