diff options
Diffstat (limited to 'Zend/zend_execute.c')
-rw-r--r-- | Zend/zend_execute.c | 1885 |
1 files changed, 1885 insertions, 0 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c new file mode 100644 index 0000000000..346b217fe0 --- /dev/null +++ b/Zend/zend_execute.c @@ -0,0 +1,1885 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998, 1999 Andi Gutmans, Zeev Suraski | + +----------------------------------------------------------------------+ + | This source file is subject to the Zend license, that is bundled | + | with this package in the file LICENSE. If you did not receive a | + | copy of the Zend license, please mail us at zend@zend.com so we can | + | send you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include <stdio.h> +#include <signal.h> + +#include "zend.h" +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_API.h" +#include "zend_ptr_stack.h" +#include "zend_variables.h" +#include "zend_operators.h" +#include "zend_constants.h" +#include "zend_extensions.h" + + +static void destroy_garbage(HashTable *ht) +{ + zend_hash_destroy(ht); + efree(ht); +} + + +#define INC_AI_COUNT(znode_ptr) if (!((znode_ptr)->u.EA.type & EXT_TYPE_UNUSED)) { EG(AiCount)++; } +#define DEC_AI_COUNT() if (--EG(AiCount)==0) { zend_ptr_stack_clean(&EG(garbage), (void (*)(void *)) destroy_garbage); } + +#define get_zval_ptr(node, Ts, should_free, type) _get_zval_ptr(node, Ts, should_free ELS_CC) +#define get_zval_ptr_ptr(node, Ts, type) _get_zval_ptr_ptr(node, Ts ELS_CC) + +#define get_incdec_op(op, opcode) \ + switch (opcode) { \ + case ZEND_PRE_INC: \ + case ZEND_POST_INC: \ + (op) = increment_function; \ + break; \ + case ZEND_PRE_DEC: \ + case ZEND_POST_DEC: \ + (op) = decrement_function; \ + break; \ + default: \ + (op) = NULL; \ + break; \ + } \ + +/* These globals don't have to be thread safe since they're never modified */ + + +/* Prototypes */ +static zval get_overloaded_property(ELS_D); +static void set_overloaded_property(zval *value ELS_DC); +static void call_overloaded_function(HashTable *ht, zval *return_value, HashTable *list, HashTable *plist ELS_DC); +static inline void zend_fetch_var_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type ELS_DC); +static inline void zend_fetch_dimension_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type ELS_DC); +static inline void zend_fetch_property_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type ELS_DC); +static inline void zend_fetch_dimension_address_from_tmp_var(znode *result, znode *op1, znode *op2, temp_variable *Ts ELS_DC); +static void zend_extension_statement_handler(zend_extension *extension, zend_op_array *op_array); +static void zend_extension_fcall_begin_handler(zend_extension *extension, zend_op_array *op_array); +static void zend_extension_fcall_end_handler(zend_extension *extension, zend_op_array *op_array); + + +static inline zval *_get_zval_ptr(znode *node, temp_variable *Ts, int *should_free ELS_DC) +{ + switch(node->op_type) { + case IS_CONST: + *should_free = 0; + return &node->u.constant; + break; + case IS_TMP_VAR: + *should_free = 1; + return &Ts[node->u.var].tmp_var; + break; + case IS_VAR: + if (Ts[node->u.var].var) { + DEC_AI_COUNT(); + *should_free = 0; + return *Ts[node->u.var].var; + } else { + *should_free = 1; + + switch (Ts[node->u.var].EA.type) { + case IS_OVERLOADED_OBJECT: + Ts[node->u.var].tmp_var = get_overloaded_property(ELS_C); + Ts[node->u.var].tmp_var.refcount=1; + Ts[node->u.var].tmp_var.is_ref=1; + return &Ts[node->u.var].tmp_var; + break; + case IS_STRING_OFFSET: { + temp_variable *T = &Ts[node->u.var]; + zval *str = T->EA.str; + + if (T->EA.str->type != IS_STRING + || (T->EA.str->value.str.len <= T->EA.offset)) { + T->tmp_var.value.str.val = empty_string; + T->tmp_var.value.str.len = 0; + } else { + char c = str->value.str.val[T->EA.offset]; + + T->tmp_var.value.str.val = estrndup(&c, 1); + T->tmp_var.value.str.len = 1; + } + zval_ptr_dtor(&str); + T->tmp_var.refcount=1; + T->tmp_var.is_ref=1; + T->tmp_var.type = IS_STRING; + return &T->tmp_var; + } + break; + } + } + break; + case IS_UNUSED: + return NULL; + break; +#if DEBUG_ZEND + default: + zend_error(E_ERROR, "Unknown temporary variable type"); + break; +#endif + } + return NULL; +} + + +static inline zval **_get_zval_ptr_ptr(znode *node, temp_variable *Ts ELS_DC) +{ + switch(node->op_type) { + case IS_VAR: + if (Ts[node->u.var].var) { + DEC_AI_COUNT(); + } + return Ts[node->u.var].var; + break; + default: + return NULL; + break; + } +} + + +static inline zval **zend_fetch_property_address_inner(HashTable *ht, znode *op2, temp_variable *Ts, int type ELS_DC) +{ + int free_op2; + zval *prop_ptr = get_zval_ptr(op2, Ts, &free_op2, BP_VAR_R); + zval **retval; + zval tmp; + + + switch (op2->op_type) { + case IS_CONST: + /* already a constant string */ + break; + case IS_VAR: + tmp = *prop_ptr; + zval_copy_ctor(&tmp); + convert_to_string(&tmp); + prop_ptr = &tmp; + break; + case IS_TMP_VAR: + convert_to_string(prop_ptr); + break; + } + + if (zend_hash_find(ht, prop_ptr->value.str.val, prop_ptr->value.str.len+1, (void **) &retval) == FAILURE) { + switch (type) { + case BP_VAR_R: + zend_error(E_NOTICE,"Undefined property: %s", prop_ptr->value.str.val); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval_ptr); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined property: %s", prop_ptr->value.str.val); + /* break missing intentionally */ + case BP_VAR_W: { + zval *new_zval = (zval *) emalloc(sizeof(zval)); + + var_uninit(new_zval); + new_zval->refcount=1; + new_zval->is_ref=0; + //zend_hash_update(ht, prop_ptr->value.str.val, prop_ptr->value.str.len+1, &new_zval, sizeof(zval *), (void **) &retval); + zend_hash_update_ptr(ht, prop_ptr->value.str.val, prop_ptr->value.str.len+1, new_zval, sizeof(zval *), (void **) &retval); + } + break; + } + } + + if (prop_ptr == &tmp) { + zval_dtor(prop_ptr); + } + FREE_OP(op2, free_op2); + return retval; +} + + +static inline void zend_assign_to_variable(znode *result, znode *op1, zval *value, int type, temp_variable *Ts ELS_DC) +{ + zval **variable_ptr_ptr = get_zval_ptr_ptr(op1, Ts, BP_VAR_W); + zval *variable_ptr; + + if (!variable_ptr_ptr) { + switch (Ts[op1->u.var].EA.type) { + case IS_OVERLOADED_OBJECT: + set_overloaded_property(value ELS_CC); + if (type == IS_TMP_VAR) { + zval_dtor(value); + } + break; + case IS_STRING_OFFSET: { + temp_variable *T = &Ts[op1->u.var]; + + if (T->EA.str->type == IS_STRING + && (T->EA.offset < T->EA.str->value.str.len)) { + zval tmp; + + if (value->type!=IS_STRING) { + tmp = *value; + zval_copy_ctor(&tmp); + convert_to_string(&tmp); + value = &tmp; + } + + T->EA.str->value.str.val[T->EA.offset] = value->value.str.val[0]; + if (value == &tmp) { + zval_dtor(value); + } + /* + * the value of an assignment to a string offset is undefined + Ts[result->u.var].var = &T->EA.str; + */ + } + zval_ptr_dtor(&T->EA.str); + T->tmp_var.type = IS_STRING; + } + break; + } + Ts[result->u.var].var = &EG(uninitialized_zval_ptr); + INC_AI_COUNT(result); + return; + } + + variable_ptr = *variable_ptr_ptr; + + if (variable_ptr == EG(error_zval_ptr)) { + if (result) { + Ts[result->u.var].var = &EG(uninitialized_zval_ptr); + INC_AI_COUNT(result); + } + return; + } + + if (variable_ptr->is_ref) { + if (variable_ptr!=value) { + short refcount=variable_ptr->refcount; + + zendi_zval_dtor(*variable_ptr); + *variable_ptr = *value; + variable_ptr->refcount = refcount; + variable_ptr->is_ref=1; + if (type!=IS_TMP_VAR) { + zendi_zval_copy_ctor(*variable_ptr); + } + } + } else { + variable_ptr->refcount--; + variable_ptr = *variable_ptr_ptr; + if (variable_ptr->refcount==0) { + switch (type) { + case IS_VAR: + case IS_CONST: + if (variable_ptr==value) { + variable_ptr->refcount++; + } else if (value->is_ref) { + *variable_ptr = *value; + zval_copy_ctor(variable_ptr); + variable_ptr->refcount=1; + } else { + zendi_zval_dtor(*variable_ptr); + safe_free_zval_ptr(variable_ptr); + *variable_ptr_ptr = value; + value->refcount++; + } + break; + case IS_TMP_VAR: + zendi_zval_dtor(*variable_ptr); + value->refcount=1; + *variable_ptr = *value; + break; + } + } else { /* we need to split */ + switch (type) { + case IS_VAR: + case IS_CONST: + if (value->is_ref) { + variable_ptr = *variable_ptr_ptr = (zval *) emalloc(sizeof(zval)); + *variable_ptr = *value; + zval_copy_ctor(variable_ptr); + variable_ptr->refcount=1; + break; + } + *variable_ptr_ptr = value; + value->refcount++; + break; + case IS_TMP_VAR: + (*variable_ptr_ptr) = (zval *) emalloc(sizeof(zval)); + value->refcount=1; + **variable_ptr_ptr = *value; + break; + } + } + (*variable_ptr_ptr)->is_ref=0; + } + if (result) { + Ts[result->u.var].var = variable_ptr_ptr; + INC_AI_COUNT(result); + } +} + + +/* Utility Functions for Extensions */ +static void zend_extension_statement_handler(zend_extension *extension, zend_op_array *op_array) +{ + if (extension->statement_handler) { + extension->statement_handler(op_array); + } +} + + +static void zend_extension_fcall_begin_handler(zend_extension *extension, zend_op_array *op_array) +{ + if (extension->fcall_begin_handler) { + extension->fcall_begin_handler(op_array); + } +} + + +static void zend_extension_fcall_end_handler(zend_extension *extension, zend_op_array *op_array) +{ + if (extension->fcall_end_handler) { + extension->fcall_end_handler(op_array); + } +} + + +static void print_refcount(zval *p, char *str) +{ + //printf("*** refcount for %x - '%s': %d\n", p, str, p->refcount); + print_refcount(NULL, NULL); +} + + +static inline void zend_fetch_var_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type ELS_DC) +{ + int free_op1; + zval *varname = get_zval_ptr(op1, Ts, &free_op1, BP_VAR_R); + zval **retval; + zval tmp_varname; + HashTable *target_symbol_table; + + switch (op2->u.constant.value.lval) { + case ZEND_FETCH_LOCAL: + default: /* just to shut gcc up */ + target_symbol_table = EG(active_symbol_table); + break; + case ZEND_FETCH_GLOBAL: + target_symbol_table = &EG(symbol_table); + break; + case ZEND_FETCH_STATIC: + target_symbol_table = EG(active_op_array)->static_variables; + break; + } + + if (varname->type != IS_STRING) { + tmp_varname = *varname; + zval_copy_ctor(&tmp_varname); + convert_to_string(&tmp_varname); + varname = &tmp_varname; + } + if (zend_hash_find(target_symbol_table, varname->value.str.val, varname->value.str.len+1, (void **) &retval) == FAILURE) { + switch (type) { + case BP_VAR_R: + zend_error(E_NOTICE,"Undefined variable: %s", varname->value.str.val); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval_ptr); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined variable: %s", varname->value.str.val); + /* break missing intentionally */ + case BP_VAR_W: { + zval *new_zval = (zval *) emalloc(sizeof(zval)); + + var_uninit(new_zval); + new_zval->refcount=1; + new_zval->is_ref=0; + //zend_hash_update(target_symbol_table, varname->value.str.val, varname->value.str.len+1, &new_zval, sizeof(zval *), (void **) &retval); + zend_hash_update_ptr(target_symbol_table, varname->value.str.val, varname->value.str.len+1, new_zval, sizeof(zval *), (void **) &retval); + } + break; + } + } + if (op2->u.constant.value.lval == ZEND_FETCH_LOCAL) { + FREE_OP(op1, free_op1); + } else if (op2->u.constant.value.lval == ZEND_FETCH_STATIC) { + zval_update_constant(*retval); + } + + if (varname == &tmp_varname) { + zval_dtor(varname); + } + Ts[result->u.var].var = retval; + INC_AI_COUNT(result); +} + + +static inline zval **zend_fetch_dimension_address_inner(HashTable *ht, znode *op2, temp_variable *Ts, int type ELS_DC) +{ + int free_op2; + zval *dim = get_zval_ptr(op2, Ts, &free_op2, BP_VAR_R); + zval **retval; + + switch (dim->type) { + case IS_STRING: { + if (zend_hash_find(ht, dim->value.str.val, dim->value.str.len+1, (void **) &retval) == FAILURE) { + switch (type) { + case BP_VAR_R: + zend_error(E_NOTICE,"Undefined index: %s", dim->value.str.val); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval_ptr); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined index: %s", dim->value.str.val); + /* break missing intentionally */ + case BP_VAR_W: { + zval *new_zval = (zval *) emalloc(sizeof(zval)); + + var_uninit(new_zval); + new_zval->refcount=1; + new_zval->is_ref=0; + //zend_hash_update(ht, dim->value.str.val, dim->value.str.len+1, &new_zval, sizeof(zval *), (void **) &retval); + zend_hash_update_ptr(ht, dim->value.str.val, dim->value.str.len+1, new_zval, sizeof(zval *), (void **) &retval); + } + break; + } + } + } + break; + case IS_LONG: { + if (zend_hash_index_find(ht, dim->value.lval, (void **) &retval) == FAILURE) { + switch (type) { + case BP_VAR_R: + zend_error(E_NOTICE,"Undefined offset: %d", dim->value.lval); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval_ptr); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined offset: %d", dim->value.lval); + /* break missing intentionally */ + case BP_VAR_W: { + zval *new_zval = (zval *) emalloc(sizeof(zval)); + + var_uninit(new_zval); + new_zval->refcount=1; + new_zval->is_ref=0; + zend_hash_index_update(ht, dim->value.lval, &new_zval, sizeof(zval *), (void **) &retval); + } + break; + } + } + } + break; + /* we need to do implement this nicely somehow ZA + case IS_DOUBLE: + break; + */ + default: + zend_error(E_WARNING, "Illegal offset type"); + if (type == BP_VAR_R || type == BP_VAR_IS) { + retval = &EG(uninitialized_zval_ptr); + } else { + retval = &EG(error_zval_ptr); + } + break; + } + FREE_OP(op2, free_op2); + return retval; +} + + +static inline void zend_fetch_dimension_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type ELS_DC) +{ + int free_op2; + zval **container_ptr = get_zval_ptr_ptr(op1, Ts, type); + zval *container; + zval ***retval = &Ts[result->u.var].var; + + + if (container_ptr == NULL) { + zend_property_reference *property_reference; + zend_overloaded_element overloaded_element; + + if (Ts[op1->u.var].EA.type == IS_STRING_OFFSET) { + zval_ptr_dtor(&Ts[op1->u.var].EA.str); + switch (type) { + case BP_VAR_R: + case BP_VAR_IS: + *retval = &EG(uninitialized_zval_ptr); + return; + case BP_VAR_W: + case BP_VAR_RW: + *retval = &EG(error_zval_ptr); + return; + } + } + + /* prepare the new element */ + overloaded_element.element = *get_zval_ptr(op2, Ts, &free_op2, type); + overloaded_element.type = IS_ARRAY; + if (!free_op2) { + zval_copy_ctor(&overloaded_element.element); + } + + zend_stack_top(&EG(overloaded_objects_stack), (void **) &property_reference); + + zend_llist_add_element(&property_reference->elements_list, &overloaded_element); + + Ts[result->u.var].EA.type = IS_OVERLOADED_OBJECT; + *retval = NULL; + return; + } + container = *container_ptr; + + if (container == EG(error_zval_ptr)) { + INC_AI_COUNT(result); + *retval = &EG(error_zval_ptr); + return; + } + + if (container->type == IS_STRING && container->value.str.val == undefined_variable_string) { + switch (type) { + case BP_VAR_RW: + case BP_VAR_W: + if (!container->is_ref) { + container->refcount--; + if (container->refcount>0) { + container = *container_ptr = (zval *) emalloc(sizeof(zval)); + container->is_ref=0; + } + container->refcount=1; + } + array_init(container); + break; + } + } + + switch (container->type) { + case IS_ARRAY: + if ((type==BP_VAR_W || type==BP_VAR_RW) && container->refcount>1 && !container->is_ref) { + container->refcount--; + *container_ptr = (zval *) emalloc(sizeof(zval)); + **container_ptr = *container; + container = *container_ptr; + container->refcount=1; + container->is_ref=0; + zendi_zval_copy_ctor(*container); + } + if (op2->op_type == IS_UNUSED) { + zval *new_zval = (zval *) emalloc(sizeof(zval)); + + var_uninit(new_zval); + new_zval->refcount = 1; + new_zval->is_ref = 0; + zend_hash_next_index_insert_ptr(container->value.ht, new_zval, sizeof(zval *), (void **) retval); + } else { + *retval = zend_fetch_dimension_address_inner(container->value.ht, op2, Ts, type ELS_CC); + } + break; + case IS_STRING: { + zval *offset; + offset = get_zval_ptr(op2, Ts, &free_op2, BP_VAR_R); + + if (container->value.str.val == undefined_variable_string + || type==BP_VAR_IS) { + /* for read-mode only */ + *retval = &EG(uninitialized_zval_ptr); + FREE_OP(op2, free_op2); + } else { + zval tmp; + + if (offset->type != IS_LONG) { + tmp = *offset; + zval_copy_ctor(&tmp); + convert_to_long(&tmp); + offset = &tmp; + } + Ts[result->u.var].EA.str = container; + container->refcount++; + Ts[result->u.var].EA.offset = offset->value.lval; + Ts[result->u.var].EA.type = IS_STRING_OFFSET; + FREE_OP(op2, free_op2); + *retval = NULL; + return; + } + } + break; + default: { + zval *offset; + + offset = get_zval_ptr(op2, Ts, &free_op2, BP_VAR_R); + if (type==BP_VAR_R || type==BP_VAR_IS) { + *retval = &EG(uninitialized_zval_ptr); + } else { + *retval = &EG(error_zval_ptr); + } + FREE_OP(op2, free_op2); + } + break; + } + INC_AI_COUNT(result); +} + + +static inline void zend_fetch_dimension_address_from_tmp_var(znode *result, znode *op1, znode *op2, temp_variable *Ts ELS_DC) +{ + int free_op1; + zval *container = get_zval_ptr(op1, Ts, &free_op1, BP_VAR_R); + + if (container->type != IS_ARRAY) { + INC_AI_COUNT(result); + Ts[result->u.var].var = &EG(uninitialized_zval_ptr); + return; + } + + INC_AI_COUNT(result); + Ts[result->u.var].var = zend_fetch_dimension_address_inner(container->value.ht, op2, Ts, BP_VAR_R ELS_CC); +} + + +static inline void zend_fetch_property_address(znode *result, znode *op1, znode *op2, temp_variable *Ts, int type ELS_DC) +{ + int free_op2; + zval **container_ptr = get_zval_ptr_ptr(op1, Ts, type); + zval *container; + zval ***retval = &Ts[result->u.var].var; + + + if (container_ptr == NULL) { + zend_property_reference *property_reference; + zend_overloaded_element overloaded_element; + + if (Ts[op1->u.var].EA.type == IS_STRING_OFFSET) { + zval_ptr_dtor(&Ts[op1->u.var].EA.str); + switch (type) { + case BP_VAR_R: + case BP_VAR_IS: + *retval = &EG(uninitialized_zval_ptr); + return; + case BP_VAR_W: + case BP_VAR_RW: + *retval = &EG(error_zval_ptr); + return; + } + } + + overloaded_element.element = *get_zval_ptr(op2, Ts, &free_op2, type); + overloaded_element.type = IS_OBJECT; + if (!free_op2) { + zval_copy_ctor(&overloaded_element.element); + } + + zend_stack_top(&EG(overloaded_objects_stack), (void **) &property_reference); + + zend_llist_add_element(&property_reference->elements_list, &overloaded_element); + + Ts[result->u.var].EA.type = IS_OVERLOADED_OBJECT; + *retval = NULL; + return; + } + + container = *container_ptr; + if (container == EG(error_zval_ptr)) { + INC_AI_COUNT(result); + *retval = &EG(error_zval_ptr); + return; + } + + if (container->type == IS_OBJECT + && container->value.obj.ce->handle_property_get) { + zend_property_reference property_reference; + zend_overloaded_element overloaded_element; + + property_reference.object = container_ptr; + property_reference.type = type; + zend_llist_init(&property_reference.elements_list, sizeof(zend_overloaded_element), NULL, 0); + overloaded_element.element = *get_zval_ptr(op2, Ts, &free_op2, type); + overloaded_element.type = IS_OBJECT; + if (!free_op2) { + zval_copy_ctor(&overloaded_element.element); + } + zend_llist_add_element(&property_reference.elements_list, &overloaded_element); + zend_stack_push(&EG(overloaded_objects_stack), &property_reference, sizeof(zend_property_reference)); + Ts[result->u.var].EA.type = IS_OVERLOADED_OBJECT; + *retval = NULL; + return; + } + + + + if (container->type == IS_STRING && container->value.str.val == undefined_variable_string) { + switch (type) { + case BP_VAR_RW: + case BP_VAR_W: + if (!container->is_ref) { + container->refcount--; + if (container->refcount>0) { + container = *container_ptr = (zval *) emalloc(sizeof(zval)); + container->is_ref=0; + } + container->refcount=1; + } + object_init(container); + break; + } + } + + if (container->type != IS_OBJECT) { + zval *offset; + + offset = get_zval_ptr(op2, Ts, &free_op2, BP_VAR_R); + FREE_OP(op2, free_op2); + INC_AI_COUNT(result); + if (type==BP_VAR_R || type==BP_VAR_IS) { + *retval = &EG(uninitialized_zval_ptr); + return; + } else { + *retval = &EG(error_zval_ptr); + return; + } + } + + + if ((type==BP_VAR_W || type==BP_VAR_RW) && container->refcount>1 && !container->is_ref) { + container->refcount--; + *container_ptr = (zval *) emalloc(sizeof(zval)); + **container_ptr = *container; + container = *container_ptr; + container->refcount=1; + container->is_ref=0; + zendi_zval_copy_ctor(*container); + } + INC_AI_COUNT(result); + *retval = zend_fetch_property_address_inner(container->value.obj.properties, op2, Ts, type ELS_CC); +} + + +static zval get_overloaded_property(ELS_D) +{ + zend_property_reference *property_reference; + zval result; + + zend_stack_top(&EG(overloaded_objects_stack), (void **) &property_reference); + result = (*(property_reference->object))->value.obj.ce->handle_property_get(property_reference); + + zend_llist_destroy(&property_reference->elements_list); + + zend_stack_del_top(&EG(overloaded_objects_stack)); + return result; +} + + +static void set_overloaded_property(zval *value ELS_DC) +{ + zend_property_reference *property_reference; + + zend_stack_top(&EG(overloaded_objects_stack), (void **) &property_reference); + (*(property_reference->object))->value.obj.ce->handle_property_set(property_reference, value); + + zend_llist_destroy(&property_reference->elements_list); + + zend_stack_del_top(&EG(overloaded_objects_stack)); +} + + +static void call_overloaded_function(HashTable *ht, zval *return_value, HashTable *list, HashTable *plist ELS_DC) +{ + zend_property_reference *property_reference; + + zend_stack_top(&EG(overloaded_objects_stack), (void **) &property_reference); + (*(property_reference->object))->value.obj.ce->handle_function_call(ht, return_value, list, plist, property_reference); + //(*(property_reference->object))->value.obj.ce->handle_function_call(NULL, NULL, NULL, NULL, NULL); + zend_llist_destroy(&property_reference->elements_list); + + zend_stack_del_top(&EG(overloaded_objects_stack)); +} + + +#if HAVE_ALLOCA || (defined (__GNUC__) && __GNUC__ >= 2) +# define free_alloca(p) +#else +# define alloca(p) emalloc(p) +# define free_alloca(p) efree(p) +#endif + + +void execute(zend_op_array *op_array ELS_DC) +{ + zend_op *opline = op_array->opcodes; + int free_op1, free_op2; + int (*unary_op)(zval *result, zval *op1); + int (*binary_op)(zval *result, zval *op1, zval *op2); + zend_op *end = op_array->opcodes + op_array->last; + zend_function_state function_state; + HashTable *calling_symbol_table; + zend_function *function_being_called=NULL; + zval **object_ptr=NULL; +#if !defined (__GNUC__) || __GNUC__ < 2 + temp_variable *Ts = (temp_variable *) alloca(sizeof(temp_variable)*op_array->T); +#else + temp_variable Ts[op_array->T]; +#endif + +#if SUPPORT_INTERACTIVE + if (EG(interactive)) { + opline = op_array->opcodes + op_array->start_op_number; + end = op_array->opcodes + op_array->end_op_number; + } +#endif + + EG(opline_ptr) = &opline; + + function_state.function = (zend_function *) op_array; + EG(function_state_ptr) = &function_state; +#if ZEND_DEBUG + /* function_state.function_symbol_table is saved as-is to a stack, + * which is an intentional UMR. Shut it up if we're in DEBUG. + */ + function_state.function_symbol_table = NULL; +#endif + + while (opline<end) { + switch(opline->opcode) { + case ZEND_ADD: + binary_op = add_function; + goto binary_op_addr; + case ZEND_SUB: + binary_op = sub_function; + goto binary_op_addr; + case ZEND_MUL: + binary_op = mul_function; + goto binary_op_addr; + case ZEND_DIV: + binary_op = div_function; + goto binary_op_addr; + case ZEND_MOD: + binary_op = mod_function; + goto binary_op_addr; + case ZEND_SL: + binary_op = shift_left_function; + goto binary_op_addr; + case ZEND_SR: + binary_op = shift_right_function; + goto binary_op_addr; + case ZEND_CONCAT: + binary_op = concat_function; + goto binary_op_addr; + case ZEND_IS_EQUAL: + binary_op = is_equal_function; + goto binary_op_addr; + case ZEND_IS_NOT_EQUAL: + binary_op = is_not_equal_function; + goto binary_op_addr; + case ZEND_IS_SMALLER: + binary_op = is_smaller_function; + goto binary_op_addr; + case ZEND_IS_SMALLER_OR_EQUAL: + binary_op = is_smaller_or_equal_function; + goto binary_op_addr; + case ZEND_BW_OR: + binary_op = bitwise_or_function; + goto binary_op_addr; + case ZEND_BW_AND: + binary_op = bitwise_and_function; + goto binary_op_addr; + case ZEND_BW_XOR: + binary_op = bitwise_xor_function; + goto binary_op_addr; + case ZEND_BOOL_XOR: + binary_op = boolean_xor_function; + /* Fall through */ +binary_op_addr: + binary_op(&Ts[opline->result.u.var].tmp_var, + get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R), + get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R) ); + FREE_OP(&opline->op1, free_op1); + FREE_OP(&opline->op2, free_op2); + break; + case ZEND_BW_NOT: + case ZEND_BOOL_NOT: + unary_op = get_unary_op(opline->opcode); + unary_op(&Ts[opline->result.u.var].tmp_var, + get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R) ); + FREE_OP(&opline->op1, free_op1); + break; + + case ZEND_ASSIGN_ADD: + binary_op = add_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_SUB: + binary_op = sub_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_MUL: + binary_op = mul_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_DIV: + binary_op = div_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_MOD: + binary_op = mod_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_SL: + binary_op = shift_left_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_SR: + binary_op = shift_right_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_CONCAT: + binary_op = concat_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_BW_OR: + binary_op = bitwise_or_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_BW_AND: + binary_op = bitwise_and_function; + goto binary_assign_op_addr; + case ZEND_ASSIGN_BW_XOR: + binary_op = bitwise_xor_function; + /* Fall through */ +binary_assign_op_addr: { + zval **var_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_RW); + + if (!var_ptr) { + zend_error(E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); + } + if (*var_ptr == EG(error_zval_ptr)) { + Ts[opline->result.u.var].var = &EG(uninitialized_zval_ptr); + INC_AI_COUNT(&opline->result); + opline++; + continue; + } + if (!(*var_ptr)->is_ref) { + if ((*var_ptr)->refcount>1) { + zval *orig_var=*var_ptr; + + (*var_ptr)->refcount--; + *var_ptr = (zval *) emalloc(sizeof(zval)); + **var_ptr = *orig_var; + zendi_zval_copy_ctor(**var_ptr); + (*var_ptr)->refcount=1; + } + } + binary_op(*var_ptr, *var_ptr, get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R)); + Ts[opline->result.u.var].var = var_ptr; + INC_AI_COUNT(&opline->result); + FREE_OP(&opline->op2, free_op2); + } + break; + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_POST_INC: + case ZEND_POST_DEC: { + int (*incdec_op)(zval *op); + zval **var_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_RW); + + if (!var_ptr) { + zend_error(E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); + } + if (*var_ptr == EG(error_zval_ptr)) { + Ts[opline->result.u.var].var = &EG(uninitialized_zval_ptr); + INC_AI_COUNT(&opline->result); + opline++; + continue; + } + + get_incdec_op(incdec_op, opline->opcode); + + switch (opline->opcode) { + case ZEND_POST_INC: + case ZEND_POST_DEC: + Ts[opline->result.u.var].tmp_var = **var_ptr; + zendi_zval_copy_ctor(Ts[opline->result.u.var].tmp_var); + break; + } + if (!(*var_ptr)->is_ref) { + if ((*var_ptr)->refcount>1) { + zval *orig_var = *var_ptr; + + (*var_ptr)->refcount--; + *var_ptr = (zval *) emalloc(sizeof(zval)); + **var_ptr = *orig_var; + zendi_zval_copy_ctor(**var_ptr); + (*var_ptr)->refcount=1; + } + } + incdec_op(*var_ptr); + switch (opline->opcode) { + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + Ts[opline->result.u.var].var = var_ptr; + INC_AI_COUNT(&opline->result); + break; + } + } + break; + case ZEND_PRINT_OP: + zend_print_variable(get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R)); + Ts[opline->result.u.var].tmp_var.value.lval = 1; + Ts[opline->result.u.var].tmp_var.type = IS_LONG; + FREE_OP(&opline->op1, free_op1); + break; + case ZEND_ECHO_OP: + zend_print_variable(get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R)); + FREE_OP(&opline->op1, free_op1); + break; + case ZEND_FETCH_R: + zend_fetch_var_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_R ELS_CC); + break; + case ZEND_FETCH_W: + zend_fetch_var_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_W ELS_CC); + break; + case ZEND_FETCH_RW: + zend_fetch_var_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_RW ELS_CC); + break; + case ZEND_FETCH_IS: + zend_fetch_var_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_IS ELS_CC); + break; + case ZEND_FETCH_DIM_R: + zend_fetch_dimension_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_R ELS_CC); + break; + case ZEND_FETCH_DIM_W: + zend_fetch_dimension_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_W ELS_CC); + break; + case ZEND_FETCH_DIM_RW: + zend_fetch_dimension_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_RW ELS_CC); + break; + case ZEND_FETCH_DIM_IS: + zend_fetch_dimension_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_IS ELS_CC); + break; + case ZEND_FETCH_OBJ_R: + zend_fetch_property_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_R ELS_CC); + break; + case ZEND_FETCH_OBJ_W: + zend_fetch_property_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_W ELS_CC); + break; + case ZEND_FETCH_OBJ_RW: + zend_fetch_property_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_RW ELS_CC); + break; + case ZEND_FETCH_OBJ_IS: + zend_fetch_property_address(&opline->result, &opline->op1, &opline->op2, Ts, BP_VAR_IS ELS_CC); + break; + case ZEND_FETCH_DIM_TMP_VAR: + zend_fetch_dimension_address_from_tmp_var(&opline->result, &opline->op1, &opline->op2, Ts ELS_CC); + break; + case ZEND_ASSIGN: { + zval *value = get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R); + + zend_assign_to_variable(&opline->result, &opline->op1, value, (free_op2?IS_TMP_VAR:opline->op2.op_type), Ts ELS_CC); + /* zend_assign_to_variable() always takes care of op2, never free it! */ + } + break; + case ZEND_ASSIGN_REF: + zend_assign_to_variable_reference(&opline->result, get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_W), get_zval_ptr_ptr(&opline->op2, Ts, BP_VAR_W), Ts ELS_CC); + INC_AI_COUNT(&opline->result); + break; + case ZEND_JMP: +#if DEBUG_ZEND>=2 + printf("Jumping to %d\n", opline->op1.u.opline_num); +#endif + opline = &op_array->opcodes[opline->op1.u.opline_num]; + continue; + break; + case ZEND_JMPZ: { + znode *op1 = &opline->op1; + + if (!i_zend_is_true(get_zval_ptr(op1, Ts, &free_op1, BP_VAR_R))) { +#if DEBUG_ZEND>=2 + printf("Conditional jmp to %d\n", opline->op2.u.opline_num); +#endif + opline = &op_array->opcodes[opline->op2.u.opline_num]; + FREE_OP(op1, free_op1); + continue; + } + FREE_OP(op1, free_op1); + } + break; + case ZEND_JMPNZ: { + znode *op1 = &opline->op1; + + if (zend_is_true(get_zval_ptr(op1, Ts, &free_op1, BP_VAR_R))) { +#if DEBUG_ZEND>=2 + printf("Conditional jmp to %d\n", opline->op2.u.opline_num); +#endif + opline = &op_array->opcodes[opline->op2.u.opline_num]; + FREE_OP(op1, free_op1); + continue; + } + FREE_OP(op1, free_op1); + } + break; + case ZEND_JMPZNZ: { + znode *res = &opline->result; + + if (!zend_is_true(get_zval_ptr(res, Ts, &free_op1, BP_VAR_R))) { +#if DEBUG_ZEND>=2 + printf("Conditional jmp on false to %d\n", opline->op2.u.opline_num); +#endif + opline = &op_array->opcodes[opline->op2.u.opline_num]; + } else { +#if DEBUG_ZEND>=2 + printf("Conditional jmp on true to %d\n", opline->op1.u.opline_num); +#endif + opline = &op_array->opcodes[opline->op1.u.opline_num]; + } + FREE_OP(res, free_op1); + } + continue; + break; + case ZEND_JMPZ_EX: { + zend_op *original_opline = opline; + int retval = zend_is_true(get_zval_ptr(&original_opline->op1, Ts, &free_op1, BP_VAR_R)); + + if (!retval) { +#if DEBUG_ZEND>=2 + printf("Conditional jmp to %d\n", opline->op2.u.opline_num); +#endif + opline = &op_array->opcodes[opline->op2.u.opline_num]; + FREE_OP(&original_opline->op1, free_op1); + Ts[original_opline->result.u.var].tmp_var.value.lval = retval; + Ts[original_opline->result.u.var].tmp_var.type = IS_LONG; + continue; + } + FREE_OP(&original_opline->op1, free_op1); + Ts[original_opline->result.u.var].tmp_var.value.lval = retval; + Ts[original_opline->result.u.var].tmp_var.type = IS_LONG; + } + break; + case ZEND_JMPNZ_EX: { + zend_op *original_opline = opline; + int retval = zend_is_true(get_zval_ptr(&original_opline->op1, Ts, &free_op1, BP_VAR_R)); + + if (retval) { +#if DEBUG_ZEND>=2 + printf("Conditional jmp to %d\n", opline->op2.u.opline_num); +#endif + opline = &op_array->opcodes[opline->op2.u.opline_num]; + FREE_OP(&original_opline->op1, free_op1); + Ts[original_opline->result.u.var].tmp_var.value.lval = retval; + Ts[original_opline->result.u.var].tmp_var.type = IS_LONG; + continue; + } + FREE_OP(&original_opline->op1, free_op1); + Ts[original_opline->result.u.var].tmp_var.value.lval = retval; + Ts[original_opline->result.u.var].tmp_var.type = IS_LONG; + } + break; + case ZEND_FREE: + zendi_zval_dtor(Ts[opline->op1.u.var].tmp_var); + break; + case ZEND_INIT_STRING: + Ts[opline->result.u.var].tmp_var.value.str.val = emalloc(1); + Ts[opline->result.u.var].tmp_var.value.str.val[0] = 0; + Ts[opline->result.u.var].tmp_var.value.str.len = 0; + Ts[opline->result.u.var].tmp_var.refcount = 1; + break; + case ZEND_ADD_CHAR: + add_char_to_string( &Ts[opline->result.u.var].tmp_var, + get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_NA), + &opline->op2.u.constant); + /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ + break; + case ZEND_ADD_STRING: + add_string_to_string( &Ts[opline->result.u.var].tmp_var, + get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_NA), + &opline->op2.u.constant); + /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ + break; + case ZEND_ADD_VAR: { + zval *var = get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R); + zval var_copy; + int destroy=0; + + if (var->type != IS_STRING) { + var_copy = *var; + zval_copy_ctor(&var_copy); + var = &var_copy; + convert_to_string(var); + destroy=1; + } + add_string_to_string( &Ts[opline->result.u.var].tmp_var, + get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_NA), + var); + if (destroy) { + zval_dtor(var); + } + /* original comment, possibly problematic: + * FREE_OP is missing intentionally here - we're always working on the same temporary variable + * (Zeev): I don't think it's problematic, we only use variables + * which aren't affected by FREE_OP()'s anyway, unless they're + * string offsets or overloaded objects + */ + FREE_OP(&opline->op2, free_op2); + } + break; + case ZEND_INIT_FCALL_BY_NAME: { + zval *function_name = get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R); + zend_function *function; + HashTable *active_function_table; + zval tmp; + + tmp = *function_name; + zval_copy_ctor(&tmp); + convert_to_string(&tmp); + function_name = &tmp; + zend_str_tolower(tmp.value.str.val, tmp.value.str.len); + + if (opline->op1.op_type != IS_UNUSED) { + if (opline->op1.op_type==IS_CONST) { /* used for class_name::function() */ + zend_class_entry *ce; + + if (zend_hash_find(EG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len+1, (void **) &ce)==FAILURE) { + zend_error(E_ERROR, "Undefined class name '%s'", opline->op1.u.constant.value.str.val); + } + active_function_table = &ce->function_table; + if (zend_hash_find(EG(active_symbol_table), "this", sizeof("this"), (void **) &object_ptr)==FAILURE) { + object_ptr=NULL; + } + } else { /* used for member function calls */ + object_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_R); + + if (opline->op1.op_type==IS_VAR + && opline>EG(active_op_array)->opcodes + && (opline-1)->opcode == ZEND_JMP_NO_CTOR) { + /* constructor call */ + EG(AiCount)++; + } + + if (!object_ptr || (*object_ptr)->value.obj.ce->handle_function_call) { /* overloaded function call */ + zend_overloaded_element overloaded_element; + zend_property_reference *property_reference; + + overloaded_element.element = *function_name; + overloaded_element.type = IS_METHOD; + + if (object_ptr) { + zend_property_reference property_reference; + + if ((*object_ptr)->type != IS_OBJECT) { + zend_error(E_ERROR, "Call to a member function on a non-object"); + } + + property_reference.object = object_ptr; + property_reference.type = BP_VAR_NA; + zend_llist_init(&property_reference.elements_list, sizeof(zend_overloaded_element), NULL, 0); + zend_stack_push(&EG(overloaded_objects_stack), &property_reference, sizeof(zend_property_reference)); + } + zend_stack_top(&EG(overloaded_objects_stack), (void **) &property_reference); + zend_llist_add_element(&property_reference->elements_list, &overloaded_element); + function_being_called = (zend_function *) emalloc(sizeof(zend_function)); + function_being_called->type = ZEND_OVERLOADED_FUNCTION; + function_being_called->common.arg_types = NULL; + goto overloaded_function_call_cont; + } + + if ((*object_ptr)->type != IS_OBJECT) { + zend_error(E_ERROR, "Call to a member function on a non-object"); + } + active_function_table = &(*object_ptr)->value.obj.ce->function_table; + } + } else { /* function pointer */ + object_ptr = NULL; + active_function_table = EG(function_table); + } + if (zend_hash_find(active_function_table, function_name->value.str.val, function_name->value.str.len+1, (void **) &function)==FAILURE) { + zend_error(E_ERROR, "Call to undefined function: %s()\n", function_name->value.str.val); + } + zval_dtor(&tmp); + function_being_called = function; +overloaded_function_call_cont: + zend_ptr_stack_push(&EG(arg_types_stack), function_being_called); + FREE_OP(&opline->op2, free_op2); + } + goto init_by_name_cont; + break; + case ZEND_INIT_FCALL: + object_ptr=NULL; + function_being_called=NULL; +init_by_name_cont: + zend_ptr_stack_push(&EG(function_symbol_table_stack), function_state.function_symbol_table); + if (EG(symtable_cache_ptr)>=EG(symtable_cache)) { + //printf("Cache hit! Reusing %x\n", symtable_cache[symtable_cache_ptr]); + function_state.function_symbol_table = *(EG(symtable_cache_ptr)--); + } else { + function_state.function_symbol_table = (HashTable *) emalloc(sizeof(HashTable)); + zend_hash_init(function_state.function_symbol_table, 0, NULL, PVAL_PTR_DTOR, 0); + //printf("Cache miss! Initialized %x\n", function_state.function_symbol_table); + } + if (object_ptr && function_being_called && function_being_called->type!=ZEND_OVERLOADED_FUNCTION) { + zval *dummy = (zval *) emalloc(sizeof(zval)), **this_ptr; + + var_uninit(dummy); + dummy->refcount=1; + dummy->is_ref=0; + zend_hash_update_ptr(function_state.function_symbol_table, "this", sizeof("this"), dummy, sizeof(zval *), (void **) &this_ptr); + zend_assign_to_variable_reference(NULL, this_ptr, object_ptr, NULL ELS_CC); + object_ptr = NULL; + } + break; + case ZEND_DO_FCALL: + if (function_being_called) { + function_state.function = function_being_called; + } else { + zval *fname = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + + if (zend_hash_find(EG(function_table), fname->value.str.val, fname->value.str.len+1, (void **) &function_state.function)==FAILURE) { + zend_error(E_ERROR, "Unknown function: %s()\n", fname->value.str.val); + } + FREE_OP(&opline->op1, free_op1); + } + { + zval *original_return_value; + + if (function_being_called) { + function_being_called = zend_ptr_stack_pop(&EG(arg_types_stack)); + } + calling_symbol_table = EG(active_symbol_table); + EG(active_symbol_table) = function_state.function_symbol_table; + if (function_state.function->type==ZEND_INTERNAL_FUNCTION) { + var_uninit(&Ts[opline->result.u.var].tmp_var); + ((zend_internal_function *) function_state.function)->handler(EG(active_symbol_table), &Ts[opline->result.u.var].tmp_var, &EG(regular_list), &EG(persistent_list)); + } else if (function_state.function->type==ZEND_USER_FUNCTION) { + original_return_value = EG(return_value); + EG(return_value) = &Ts[opline->result.u.var].tmp_var; + var_uninit(EG(return_value)); + EG(active_op_array) = (zend_op_array *) function_state.function; + zend_execute(EG(active_op_array) ELS_CC); + EG(opline_ptr) = &opline; + EG(active_op_array) = op_array; + EG(return_value)=original_return_value; + } else { /* ZEND_OVERLOADED_FUNCTION */ + call_overloaded_function(EG(active_symbol_table), &Ts[opline->result.u.var].tmp_var, &EG(regular_list), &EG(persistent_list)); + efree(function_being_called); + } + function_state.function = (zend_function *) op_array; + EG(active_symbol_table) = calling_symbol_table; + if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { + zend_hash_destroy(function_state.function_symbol_table); + efree(function_state.function_symbol_table); + } else { + *(++EG(symtable_cache_ptr)) = function_state.function_symbol_table; + zend_hash_clean(*EG(symtable_cache_ptr)); + } + EG(function_state_ptr) = &function_state; + function_state.function_symbol_table = zend_ptr_stack_pop(&EG(function_symbol_table_stack)); + } + break; + case ZEND_RETURN: { + zval *retval = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + + *EG(return_value) = *retval; + if (!free_op1) { + zendi_zval_copy_ctor(*EG(return_value)); + } + //return_value->refcount=1; + //return_value->is_ref=0; +#if SUPPORT_INTERACTIVE + op_array->last_executed_op_number = opline-op_array->opcodes; +#endif + free_alloca(Ts); + return; + } + break; + case ZEND_SEND_VAL: + if (function_being_called + && function_being_called->common.arg_types + && opline->op2.u.opline_num<function_being_called->common.arg_types[0] + && function_being_called->common.arg_types[1+opline->op2.u.opline_num]==BYREF_FORCE) { + zend_error(E_ERROR, "Cannot pass parameter %d by reference", opline->op2.u.opline_num); + } + { + zval *valptr = (zval *) emalloc(sizeof(zval)); + + *valptr = Ts[opline->op1.u.var].tmp_var; + valptr->refcount=1; + valptr->is_ref=0; + zend_hash_next_index_insert_ptr(function_state.function_symbol_table, valptr, sizeof(zval *), NULL); + } + break; + case ZEND_SEND_VAR: + if (function_being_called + && function_being_called->common.arg_types + && opline->op2.u.opline_num<function_being_called->common.arg_types[0] + && function_being_called->common.arg_types[1+opline->op2.u.opline_num]==BYREF_FORCE) { + goto send_by_ref; + } + { + zval *varptr = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + + if (varptr == &EG(uninitialized_zval)) { + varptr = (zval *) emalloc(sizeof(zval)); + var_uninit(varptr); + varptr->refcount=0; + varptr->is_ref=0; + } else if (varptr->is_ref) { + zval *original_var = varptr; + + varptr = (zval *) emalloc(sizeof(zval)); + *varptr = *original_var; + varptr->is_ref = 0; + varptr->refcount = 0; + zval_copy_ctor(varptr); + } + varptr->refcount++; + zend_hash_next_index_insert_ptr(function_state.function_symbol_table, varptr, sizeof(zval *), NULL); + } + break; +send_by_ref: + case ZEND_SEND_REF: { + zval **varptr_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_W); + zval *varptr = *varptr_ptr; + + if (!varptr->is_ref) { + /* code to break away this variable */ + if (varptr->refcount>1) { + *varptr_ptr = (zval *) emalloc(sizeof(zval)); + **varptr_ptr = *varptr; + varptr = *varptr_ptr; + varptr->refcount = 1; + } + varptr->is_ref = 1; + /* at the end of this code refcount is always 1 */ + } + varptr->refcount++; + zend_hash_next_index_insert_ptr(function_state.function_symbol_table, varptr, sizeof(zval *), NULL); + } + break; + case ZEND_RECV: { + zval **param; + + if (zend_hash_index_find(EG(active_symbol_table), opline->op1.u.constant.value.lval, (void **) ¶m)==FAILURE) { + zend_error(E_NOTICE, "Missing argument %d for %s()\n", opline->op1.u.constant.value.lval, get_active_function_name()); + } else if ((*param)->is_ref) { + zend_assign_to_variable_reference(NULL, get_zval_ptr_ptr(&opline->result, Ts, BP_VAR_W), param, NULL ELS_CC); + } else { + zend_assign_to_variable(NULL, &opline->result, *param, IS_VAR, Ts ELS_CC); + } + } + break; + case ZEND_RECV_INIT: { + zval **param, *assignment_value; + + if (zend_hash_index_find(EG(active_symbol_table), opline->op1.u.constant.value.lval, (void **) ¶m)==FAILURE) { + if (opline->op2.u.constant.type == IS_CONSTANT) { + zval *default_value = (zval *) emalloc(sizeof(zval)); + zval tmp; + + *default_value = opline->op2.u.constant; + if (!zend_get_constant(default_value->value.str.val, default_value->value.str.len, &tmp)) { + default_value->type = IS_STRING; + zval_copy_ctor(default_value); + } else { + *default_value = tmp; + } + default_value->refcount=0; + default_value->is_ref=0; + param = &default_value; + assignment_value = default_value; + } else { + param = NULL; + assignment_value = &opline->op2.u.constant; + } + } else { + assignment_value = *param; + } + + if (assignment_value->is_ref) { + zend_assign_to_variable_reference(NULL, get_zval_ptr_ptr(&opline->result, Ts, BP_VAR_W), param, NULL ELS_CC); + } else { + zend_assign_to_variable(NULL, &opline->result, assignment_value, IS_VAR, Ts ELS_CC); + } + } + break; + case ZEND_BOOL: + /* PHP 3.0 returned "" for false and 1 for true, here we use 0 and 1 for now */ + Ts[opline->result.u.var].tmp_var.value.lval = zend_is_true(get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R)); + Ts[opline->result.u.var].tmp_var.type = IS_LONG; + FREE_OP(&opline->op1, free_op1); + break; + case ZEND_BRK: + case ZEND_CONT: { + zval *nest_levels_zval = get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R); + zval tmp; + int array_offset, nest_levels, original_nest_levels; + zend_brk_cont_element *jmp_to; + + if (nest_levels_zval->type != IS_LONG) { + tmp = *nest_levels_zval; + zval_copy_ctor(&tmp); + convert_to_long(&tmp); + nest_levels = tmp.value.lval; + } else { + nest_levels = nest_levels_zval->value.lval; + } + original_nest_levels = nest_levels; + array_offset = opline->op1.u.opline_num; + do { + if (array_offset==-1) { + zend_error(E_ERROR, "Cannot break/continue %d levels\n", original_nest_levels); + } + jmp_to = &op_array->brk_cont_array[array_offset]; + array_offset = jmp_to->parent; + } while (--nest_levels > 0); + + if (opline->opcode == ZEND_BRK) { + opline = op_array->opcodes+jmp_to->brk; + // printf("Jumping to %d\n", jmp_to->brk); + } else { + opline = op_array->opcodes+jmp_to->cont; + // printf("Jumping to %d\n", jmp_to->cont); + } + FREE_OP(&opline->op2, free_op2); + continue; + } + break; + case ZEND_CASE: + is_equal_function(&Ts[opline->result.u.var].tmp_var, + get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R), + get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R) ); + FREE_OP(&opline->op2, free_op2); + break; + case ZEND_NEW: { + zval *tmp = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + zval class_name; + zend_class_entry *ce; + + class_name = *tmp; + zval_copy_ctor(&class_name); + convert_to_string(&class_name); + zend_str_tolower(class_name.value.str.val, class_name.value.str.len); + + if (zend_hash_find(EG(class_table), class_name.value.str.val, class_name.value.str.len+1, (void **) &ce)==FAILURE) { + zend_error(E_ERROR, "Cannot instanciate non-existant class: %s", class_name.value.str.val); + } + object_init_ex(&Ts[opline->result.u.var].tmp_var, ce); + + zval_dtor(&class_name); + FREE_OP(&opline->op1, free_op1); + } + break; + case ZEND_FETCH_CONSTANT: + if (!zend_get_constant(opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &Ts[opline->result.u.var].tmp_var)) { + zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'", + opline->op1.u.constant.value.str.val, + opline->op1.u.constant.value.str.val); + Ts[opline->result.u.var].tmp_var = opline->op1.u.constant; + zval_copy_ctor(&Ts[opline->result.u.var].tmp_var); + } + break; + case ZEND_INIT_ARRAY: + case ZEND_ADD_ARRAY_ELEMENT: { + zval *array_ptr = &Ts[opline->result.u.var].tmp_var; + zval *expr=get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + zval *offset=get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R); + + if (opline->opcode==ZEND_INIT_ARRAY) { + array_init(array_ptr); + if (!expr) { + break; + } + } + if (free_op1) { /* temporary variable */ + zval *new_expr = (zval *) emalloc(sizeof(zval)); + + *new_expr = *expr; + expr = new_expr; + expr->refcount=1; + expr->is_ref=0; + } else { + if (expr->is_ref) { + zval *new_expr = (zval *) emalloc(sizeof(zval)); + + *new_expr = *expr; + expr = new_expr; + zendi_zval_copy_ctor(*expr); + expr->refcount=1; + expr->is_ref=0; + } else { + expr->refcount++; + } + } + if (offset) { + switch(offset->type) { + case IS_DOUBLE: + zend_hash_index_update(array_ptr->value.ht, (long) offset->value.lval, &expr, sizeof(zval *), NULL); + break; + case IS_LONG: + zend_hash_index_update(array_ptr->value.ht, offset->value.lval, &expr, sizeof(zval *), NULL); + break; + case IS_STRING: + zend_hash_update(array_ptr->value.ht, offset->value.str.val, offset->value.str.len+1, &expr, sizeof(zval *), NULL); + break; + default: + /* do nothing */ + break; + } + FREE_OP(&opline->op2, free_op2); + } else { + zend_hash_next_index_insert(array_ptr->value.ht, &expr, sizeof(zval *), NULL); + } + } + break; + case ZEND_CAST: { + zval *expr = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + zval *result = &Ts[opline->result.u.var].tmp_var; + + *result = *expr; + if (!free_op1) { + zendi_zval_copy_ctor(*result); + } + switch (opline->op2.u.constant.type) { + case IS_LONG: + convert_to_long(result); + break; + case IS_DOUBLE: + convert_to_double(result); + break; + case IS_STRING: + convert_to_string(result); + break; + case IS_ARRAY: + convert_to_array(result); + break; + case IS_OBJECT: + convert_to_object(result); + break; + } + } + break; + case ZEND_INCLUDE_OR_EVAL: { + zend_op_array *new_op_array=NULL; + zval *original_return_value = EG(return_value); + CLS_FETCH(); + + switch (opline->op2.u.constant.value.lval) { + case ZEND_INCLUDE: + new_op_array = compile_filename(get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R) CLS_CC); + break; + case ZEND_EVAL: + new_op_array = compile_string(get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R) CLS_CC); + break; + } + if (new_op_array) { + Ts[opline->result.u.var].tmp_var.value.lval = 1; + Ts[opline->result.u.var].tmp_var.type = IS_LONG; + EG(return_value) = &Ts[opline->result.u.var].tmp_var; + EG(active_op_array) = new_op_array; + zend_execute(new_op_array ELS_CC); + + EG(opline_ptr) = &opline; + EG(active_op_array) = op_array; + EG(function_state_ptr) = &function_state; + destroy_op_array(new_op_array); + efree(new_op_array); + } else { + var_uninit(&Ts[opline->result.u.var].tmp_var); + } + EG(return_value) = original_return_value; + FREE_OP(&opline->op1, free_op1); + } + break; + case ZEND_UNSET_VAR: { + zval tmp, *variable = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + + if (variable->type != IS_STRING) { + tmp = *variable; + zval_copy_ctor(&tmp); + convert_to_string(&tmp); + variable = &tmp; + } + + zend_hash_del(EG(active_symbol_table), variable->value.str.val, variable->value.str.len+1); + + if (variable == &tmp) { + zval_dtor(&tmp); + } + FREE_OP(&opline->op1, free_op1); + } + break; + case ZEND_UNSET_DIM_OBJ: { + zval **container = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_R); + zval *offset = get_zval_ptr(&opline->op2, Ts, &free_op2, BP_VAR_R); + + if (container) { + HashTable *ht; + + switch ((*container)->type) { + case IS_ARRAY: + ht = (*container)->value.ht; + break; + case IS_OBJECT: + ht = (*container)->value.obj.properties; + break; + default: + ht = NULL; + break; + } + if (ht) { + switch (offset->type) { + case IS_LONG: + zend_hash_index_del(ht, offset->value.lval); + break; + case IS_STRING: + zend_hash_del(ht, offset->value.str.val, offset->value.str.len+1); + break; + } + } + } else { + /* overloaded element */ + } + FREE_OP(&opline->op2, free_op2); + } + break; + case ZEND_FE_RESET: { + zval *array = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + + Ts[opline->result.u.var].tmp_var = *array; + array = &Ts[opline->result.u.var].tmp_var; + if (!free_op1) { + zval_copy_ctor(array); + } + if (array->type == IS_ARRAY) { + /* probably redundant */ + zend_hash_internal_pointer_reset(array->value.ht); + } else { + /* JMP to the end of foreach - TBD */ + } + } + break; + case ZEND_FE_FETCH: { + zval *array = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + zval *result = &Ts[opline->result.u.var].tmp_var; + zval **value, *key; + char *str_key; + ulong int_key; + + if (zend_hash_get_current_data(array->value.ht, (void **) &value)==FAILURE) { + opline = op_array->opcodes+opline->op2.u.opline_num; + continue; + } + array_init(result); + + + (*value)->refcount++; + zend_hash_index_update(result->value.ht, 0, value, sizeof(zval *), NULL); + + key = (zval *) emalloc(sizeof(zval)); + key->refcount=1; + key->is_ref=0; + switch (zend_hash_get_current_key(array->value.ht, &str_key, &int_key)) { + case HASH_KEY_IS_STRING: + key->value.str.val = str_key; + key->value.str.len = strlen(str_key); + key->type = IS_STRING; + break; + case HASH_KEY_IS_LONG: + key->value.lval = int_key; + key->type = IS_LONG; + break; + } + zend_hash_index_update(result->value.ht, 1, &key, sizeof(zval *), NULL); + zend_hash_move_forward(array->value.ht); + } + break; + case ZEND_JMP_NO_CTOR: { + zval *object = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + + EG(AiCount)++; + if (!object->value.obj.ce->handle_function_call + && !zend_hash_exists(&object->value.obj.ce->function_table, object->value.obj.ce->name, object->value.obj.ce->name_length+1)) { + opline = op_array->opcodes + opline->op2.u.opline_num; + continue; + } + } + break; + case ZEND_ISSET_ISEMPTY: { + zval **var = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_IS); + int isset; + + if (var==&EG(uninitialized_zval_ptr) + || ((*var)->type == IS_STRING && (*var)->value.str.val == undefined_variable_string)) { + isset = 0; + } else { + isset = 1; + } + + switch (opline->op2.u.constant.value.lval) { + case ZEND_ISSET: + Ts[opline->result.u.var].tmp_var.value.lval = isset; + break; + case ZEND_ISEMPTY: + if (!isset || !zend_is_true(*var)) { + Ts[opline->result.u.var].tmp_var.value.lval = 1; + } else { + Ts[opline->result.u.var].tmp_var.value.lval = 0; + } + break; + } + Ts[opline->result.u.var].tmp_var.type = IS_BOOL; + } + break; + case ZEND_EXIT: + if (opline->op1.op_type != IS_UNUSED) { + zend_print_variable(get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R)); + FREE_OP(&opline->op1, free_op1); + } + zend_bailout(); + break; + case ZEND_BEGIN_SILENCE: + Ts[opline->result.u.var].tmp_var.value.lval = EG(error_reporting); + Ts[opline->result.u.var].tmp_var.type = IS_LONG; /* shouldn't be necessary */ + EG(error_reporting) = 0; + break; + case ZEND_END_SILENCE: + EG(error_reporting) = Ts[opline->op1.u.var].tmp_var.value.lval; + break; + case ZEND_QM_ASSIGN: { + zval *value = get_zval_ptr(&opline->op1, Ts, &free_op1, BP_VAR_R); + + Ts[opline->result.u.var].tmp_var = *value; + if (!free_op1) { + zval_copy_ctor(&Ts[opline->result.u.var].tmp_var); + } + } + break; + case ZEND_EXT_STMT: + if (!EG(no_extensions)) { + zend_llist_apply_with_argument(&zend_extensions, (void (*)(void *, void *)) zend_extension_statement_handler, op_array); + } + break; + case ZEND_EXT_FCALL_BEGIN: + if (!EG(no_extensions)) { + zend_llist_apply_with_argument(&zend_extensions, (void (*)(void *, void *)) zend_extension_fcall_begin_handler, op_array); + } + break; + case ZEND_EXT_FCALL_END: + if (!EG(no_extensions)) { + zend_llist_apply_with_argument(&zend_extensions, (void (*)(void *, void *)) zend_extension_fcall_end_handler, op_array); + } + break; + case ZEND_EXT_NOP: + case ZEND_NOP: + break; + default: + break; + } + opline++; + } +#if SUPPORT_INTERACTIVE + op_array->last_executed_op_number = opline-op_array->opcodes; +#endif + free_alloca(Ts); +} |