# End Source File\r
# Begin Source File\r
\r
+SOURCE=.\zend_objects.c\r
+# End Source File\r
+# Begin Source File\r
+\r
SOURCE=.\zend_opcode.c\r
# End Source File\r
# Begin Source File\r
# End Source File\r
# Begin Source File\r
\r
+SOURCE=.\zend_objects.h\r
+# End Source File\r
+# Begin Source File\r
+\r
SOURCE=.\zend_operators.h\r
# End Source File\r
# Begin Source File\r
HashTable *properties;
} zend_object;
+typedef unsigned int zend_object_handle;
+
+typedef zend_object *(*get_address_t)(zend_object_handle handle); /* Don't return zval ** so that we can't change it */
+typedef zval **(*get_property_address_t)(zend_object_handle handle, zval *offset, int type);
+typedef void (*add_ref_t)(zend_object_handle handle);
+typedef void (*del_ref_t)(zend_object_handle handle);
+typedef void (*delete_obj_t)(zend_object_handle handle);
+
+typedef struct _zend_object_handlers {
+ get_address_t get_address;
+ get_property_address_t get_property_address;
+ add_ref_t add_ref;
+ del_ref_t del_ref;
+ delete_obj_t delete_obj;
+} zend_object_handlers;
+
+typedef struct _zend_object_value {
+ zend_object_handle handle;
+ zend_object_handlers handlers;
+} zend_object_value;
+
+#include "zend_objects.h"
+
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
int len;
} str;
HashTable *ht; /* hash table value */
- zend_object obj;
+/* struct {
+ zend_class_entry *ce;
+ HashTable *properties;
+ } obj;
+*/
+ zend_object_value obj;
} zvalue_value;
return 1;
}
}
-
return 0;
}
ZEND_API int _object_init_ex(zval *arg, zend_class_entry *class_type ZEND_FILE_LINE_DC TSRMLS_DC)
{
zval *tmp;
+ zend_object *object;
if (!class_type->constants_updated) {
zend_hash_apply_with_argument(&class_type->default_properties, (apply_func_arg_t) zval_update_constant, (void *) 1 TSRMLS_CC);
class_type->constants_updated = 1;
}
- ALLOC_HASHTABLE_REL(arg->value.obj.properties);
- zend_hash_init(arg->value.obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
- zend_hash_copy(arg->value.obj.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
+ arg->value.obj = zend_objects_new(&object);
+
+ ALLOC_HASHTABLE_REL(object->properties);
+ zend_hash_init(object->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
+ zend_hash_copy(object->properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
+ object->ce = class_type;
+
arg->type = IS_OBJECT;
- arg->value.obj.ce = class_type;
return SUCCESS;
}
MAKE_STD_ZVAL(tmp);
ZVAL_RESOURCE(tmp, n);
-
+
return zend_hash_update(Z_OBJPROP_P(arg), key, key_len, (void *) &tmp, sizeof(zval *), NULL);
}
MAKE_STD_ZVAL(tmp);
ZVAL_DOUBLE(tmp, d);
-
+
return zend_hash_update(Z_OBJPROP_P(arg), key, key_len, (void *) &tmp, sizeof(zval *), NULL);
}
}
}
-
void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference TSRMLS_DC)
{
zend_op_array op_array;
}
-void zend_do_unset(znode *variable TSRMLS_DC)
+void zend_do_unset(znode *variable, int type TSRMLS_DC)
{
zend_op *last_op;
break;
}
+ last_op->extended_value = type;
}
void zend_do_cast(znode *result, znode *expr, int type TSRMLS_DC);
void zend_do_include_or_eval(int type, znode *result, znode *op1 TSRMLS_DC);
-void zend_do_unset(znode *variable TSRMLS_DC);
+void zend_do_unset(znode *variable, int type TSRMLS_DC);
void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC);
void zend_do_foreach_begin(znode *foreach_token, znode *array, znode *open_brackets_token, znode *as_token, int variable TSRMLS_DC);
#define ZEND_FETCH_LOCAL 1
#define ZEND_FETCH_STATIC 2
+/* unset types */
+#define ZEND_UNSET_REG 0
+#define ZEND_UNSET_OBJ 1
+
/* var status for backpatching */
#define BP_VAR_R 0
#define BP_VAR_W 1
object.ptr = _get_object_zval_ptr(&opline->op1, Ts, &EG(free_op1) TSRMLS_CC);
if ((!object.ptr && Ts[opline->op1.u.var].EA.type==IS_OVERLOADED_OBJECT)
- || ((object.ptr && object.ptr->type==IS_OBJECT) && (object.ptr->value.obj.ce->handle_function_call))) { /* overloaded function call */
+ || ((object.ptr && object.ptr->type==IS_OBJECT) && Z_OBJCE_P(object.ptr)->handle_function_call)) { /* overloaded function call */
zend_overloaded_element overloaded_element;
overloaded_element.element = *function_name;
goto overloaded_function_call_cont;
}
- if (!object.ptr || object.ptr->type != IS_OBJECT) {
+ if (object.ptr && object.ptr->type == IS_OBJECT) {
+ active_function_table = &Z_OBJCE_P(object.ptr)->function_table;
+ } else {
zend_error(E_ERROR, "Call to a member function on a non-object");
}
- if (!object.ptr->is_ref && object.ptr->refcount > 1) {
- zend_error(E_ERROR, "Bug: Problem in method call\n");
+ if (!PZVAL_IS_REF(object.ptr)) {
+ object.ptr->refcount++; /* For $this pointer */
+ } else {
+ zval *this_ptr;
+ ALLOC_ZVAL(this_ptr);
+ *this_ptr = *object.ptr;
+ INIT_PZVAL(this_ptr);
+ zval_copy_ctor(this_ptr);
+ object.ptr = this_ptr;
}
- object.ptr->is_ref=1;
- object.ptr->refcount++; /* For $this pointer */
- active_function_table = &(object.ptr->value.obj.ce->function_table);
+ active_function_table = &Z_OBJCE_P(object.ptr)->function_table;
}
} else { /* function pointer */
object.ptr = NULL;
INIT_ZVAL(*(Ts[opline->result.u.var].var.ptr));
((zend_internal_function *) function_state.function)->handler(opline->extended_value, Ts[opline->result.u.var].var.ptr, object.ptr, return_value_used TSRMLS_CC);
if (object.ptr) {
- object.ptr->refcount--;
+ zval_ptr_dtor(&object.ptr);
}
Ts[opline->result.u.var].var.ptr->is_ref = 0;
Ts[opline->result.u.var].var.ptr->refcount = 1;
zval *null_ptr = NULL;
zend_hash_update(function_state.function_symbol_table, "this", sizeof("this"), &null_ptr, sizeof(zval *), (void **) &this_ptr);
- if (!PZVAL_IS_REF(object.ptr)) {
- zend_error(E_WARNING, "Problem with method call - please report this bug");
- }
*this_ptr = object.ptr;
object.ptr = NULL;
}
NEXT_OPCODE();
case ZEND_UNSET_VAR: {
zval tmp, *variable = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R);
+ zval **object;
+ zend_bool unset_object;
if (variable->type != IS_STRING) {
tmp = *variable;
variable = &tmp;
}
+ unset_object = (opline->extended_value == ZEND_UNSET_OBJ);
+
+ if (unset_object) {
+ if (zend_hash_find(EG(active_symbol_table), variable->value.str.val, variable->value.str.len+1, (void **)&object) == FAILURE) {
+ zend_error(E_ERROR, "Cannot delete non-existing object");
+ }
+ if (Z_TYPE_PP(object) != IS_OBJECT) {
+ zend_error(E_ERROR, "Cannot call delete on non-object type");
+ }
+ (*object)->value.obj.handlers.delete_obj((*object)->value.obj.handle);
+ }
+
zend_hash_del(EG(active_symbol_table), variable->value.str.val, variable->value.str.len+1);
if (variable == &tmp) {
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, &EG(free_op2), BP_VAR_R);
+ zend_bool unset_object;
+ zval **object;
+ unset_object = (opline->extended_value == ZEND_UNSET_OBJ);
+
if (container) {
HashTable *ht;
ht = (*container)->value.ht;
break;
case IS_OBJECT:
- ht = (*container)->value.obj.properties;
+ ht = Z_OBJPROP_PP(container);
break;
default:
ht = NULL;
} else {
index = offset->value.lval;
}
+
+ if (unset_object) {
+ if (zend_hash_index_find(ht, index, (void **)&object) == FAILURE) {
+ zend_error(E_ERROR, "Cannot delete non-existing object");
+ }
+ if (Z_TYPE_PP(object) != IS_OBJECT) {
+ zend_error(E_ERROR, "Cannot call delete on non-object type");
+ }
+ (*object)->value.obj.handlers.delete_obj((*object)->value.obj.handle);
+ }
+
zend_hash_index_del(ht, index);
break;
}
case IS_STRING:
+ if (unset_object) {
+ if (zend_hash_find(ht, offset->value.str.val, offset->value.str.len+1, (void **)&object) == FAILURE) {
+ zend_error(E_ERROR, "Cannot delete non-existing object");
+ }
+ if (Z_TYPE_PP(object) != IS_OBJECT) {
+ zend_error(E_ERROR, "Cannot call delete on non-object type");
+ }
+ (*object)->value.obj.handlers.delete_obj((*object)->value.obj.handle);
+ }
+
zend_hash_del(ht, offset->value.str.val, offset->value.str.len+1);
break;
case IS_NULL:
+ if (unset_object) {
+ if (zend_hash_find(ht, "", sizeof(""), (void **)&object) == FAILURE) {
+ zend_error(E_ERROR, "Cannot delete non-existing object");
+ }
+ if (Z_TYPE_PP(object) != IS_OBJECT) {
+ zend_error(E_ERROR, "Cannot call delete on non-object type");
+ }
+ (*object)->value.obj.handlers.delete_obj((*object)->value.obj.handle);
+ }
+
zend_hash_del(ht, "", sizeof(""));
break;
default:
PZVAL_LOCK(array);
fe_ht = HASH_OF(array);
-
if (!fe_ht) {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
opline = op_array->opcodes+opline->op2.u.opline_num;
}
NEXT_OPCODE();
case ZEND_JMP_NO_CTOR: {
- zval *object;
+ zval *object_zval;
+ zend_object *object;
if (opline->op1.op_type == IS_VAR) {
PZVAL_LOCK(*Ts[opline->op1.u.var].var.ptr_ptr);
}
- object = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R);
- 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)) {
+ object_zval = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R);
+ object = object_zval->value.obj.handlers.get_address(object_zval->value.obj.handle);
+
+ if (!object->ce->handle_function_call
+ && !zend_hash_exists(&object->ce->function_table, object->ce->name, object->ce->name_length+1)) {
opline = op_array->opcodes + opline->op2.u.opline_num;
continue;
}
zend_ptr_stack_init(&EG(user_error_handlers));
EG(orig_error_reporting) = EG(error_reporting);
-
+ zend_objects_init(&EG(objects), 1024);
#ifdef ZEND_WIN32
EG(timed_out) = 0;
#endif
zend_ptr_stack_destroy(&EG(user_error_handlers));
EG(error_reporting) = EG(orig_error_reporting);
+ zend_objects_destroy(&EG(objects));
} zend_end_try();
}
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)) {
+ } else if ((*zval_ptr)->refcount == 1) {
(*zval_ptr)->is_ref = 0;
}
}
}
if (object_pp) {
if (Z_TYPE_PP(object_pp) == IS_OBJECT) {
- function_table = &(*object_pp)->value.obj.ce->function_table;
+ function_table = &Z_OBJCE_PP(object_pp)->function_table;
} else if (Z_TYPE_PP(object_pp) == IS_STRING) {
zend_class_entry *ce;
char *lc_class;
int lambda_count;
HashTable ini_directives;
+ zend_objects objects;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
%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
+%nonassoc T_NEW T_DELETE
%token T_EXIT
%token T_IF
%left T_ELSEIF
| T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 0 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); }
| T_DECLARE { zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(TSRMLS_C); }
| ';' /* empty statement */
+ | T_DELETE cvar ';' { zend_do_end_variable_parse(BP_VAR_UNSET, 0 TSRMLS_CC); zend_do_unset(&$1, ZEND_UNSET_OBJ TSRMLS_CC); }
;
unset_variables:
;
unset_variable:
- cvar { zend_do_end_variable_parse(BP_VAR_UNSET, 0 TSRMLS_CC); zend_do_unset(&$1 TSRMLS_CC); }
+ cvar { zend_do_end_variable_parse(BP_VAR_UNSET, 0 TSRMLS_CC); zend_do_unset(&$1, ZEND_UNSET_REG TSRMLS_CC); }
;
use_filename:
return T_NEW;
}
+<ST_IN_SCRIPTING>"delete" {
+ return T_DELETE;
+}
+
<ST_IN_SCRIPTING>"var" {
return T_VAR;
}
--- /dev/null
+#include "zend.h"
+#include "zend_globals.h"
+
+#define ZEND_DEBUG_OBJECTS 0
+
+static zend_object_handlers zoh = {
+ zend_objects_get_address,
+ NULL,
+ zend_objects_add_ref,
+ zend_objects_del_ref,
+ zend_objects_delete_obj
+};
+
+void zend_objects_init(zend_objects *objects, zend_uint init_size)
+{
+ objects->object_buckets = (zend_object_bucket *) emalloc(init_size * sizeof(zend_object_bucket));
+ objects->top = 1; /* Skip 0 so that handles are true */
+ objects->size = init_size;
+ objects->free_list_head = -1;
+}
+
+void zend_objects_destroy(zend_objects *objects)
+{
+ efree(objects->object_buckets);
+}
+
+zend_object_value zend_objects_new(zend_object **object)
+{
+ TSRMLS_FETCH();
+
+ zend_object_handle handle;
+ zend_object_value retval;
+
+ if (EG(objects).free_list_head != -1) {
+ handle = EG(objects).free_list_head;
+ EG(objects).free_list_head = EG(objects).object_buckets[handle].bucket.free_list.next;
+ } else {
+ if (EG(objects).top == EG(objects).size) {
+ EG(objects).size <<= 1;
+ EG(objects).object_buckets = (zend_object_bucket *) erealloc(EG(objects).object_buckets, EG(objects).size * sizeof(zend_object_bucket));
+ }
+ handle = EG(objects).top++;
+ }
+ EG(objects).object_buckets[handle].valid = 1;
+ EG(objects).object_buckets[handle].bucket.obj.refcount = 1;
+ *object = &EG(objects).object_buckets[handle].bucket.obj.object;
+ retval.handle = handle;
+ retval.handlers = zoh;
+#if ZEND_DEBUG_OBJECTS
+ fprintf(stderr, "Allocated object id #%d\n", handle);
+#endif
+ return retval;
+}
+
+zend_object *zend_objects_get_address(zend_object_handle handle)
+{
+ TSRMLS_FETCH();
+
+ if (!EG(objects).object_buckets[handle].valid) {
+ zend_error(E_ERROR, "Trying to access invalid object");
+ }
+ return &EG(objects).object_buckets[handle].bucket.obj.object;
+}
+
+void zend_objects_add_ref(zend_object_handle handle)
+{
+ TSRMLS_FETCH();
+
+ if (!EG(objects).object_buckets[handle].valid) {
+ zend_error(E_ERROR, "Trying to add reference to invalid object");
+ }
+
+ EG(objects).object_buckets[handle].bucket.obj.refcount++;
+#if ZEND_DEBUG_OBJECTS
+ fprintf(stderr, "Increased refcount of object id #%d\n", handle);
+#endif
+}
+
+void zend_objects_delete_obj(zend_object_handle handle)
+{
+ zend_object *object;
+ TSRMLS_FETCH();
+
+ if (!EG(objects).object_buckets[handle].valid) {
+ zend_error(E_ERROR, "Trying to delete invalid object");
+ }
+
+ object = &EG(objects).object_buckets[handle].bucket.obj.object;
+ zend_hash_destroy(object->properties);
+ efree(object->properties);
+ EG(objects).object_buckets[handle].valid = 0;
+#if ZEND_DEBUG_OBJECTS
+ fprintf(stderr, "Deleted object id #%d\n", handle);
+#endif
+
+}
+
+void zend_objects_del_ref(zend_object_handle handle)
+{
+ TSRMLS_FETCH();
+
+ if (--EG(objects).object_buckets[handle].bucket.obj.refcount == 0) {
+ zend_object *object;
+
+ if (EG(objects).object_buckets[handle].valid) {
+ object = &EG(objects).object_buckets[handle].bucket.obj.object;
+ zend_hash_destroy(object->properties);
+ efree(object->properties);
+ }
+ EG(objects).object_buckets[handle].bucket.free_list.next = EG(objects).free_list_head;
+ EG(objects).free_list_head = handle;
+ EG(objects).object_buckets[handle].valid = 0;
+#if ZEND_DEBUG_OBJECTS
+ fprintf(stderr, "Deallocated object id #%d\n", handle);
+#endif
+ }
+#if ZEND_DEBUG_OBJECTS
+ else {
+ fprintf(stderr, "Decreased refcount of object id #%d\n", handle);
+ }
+#endif
+}
--- /dev/null
+#ifndef ZEND_OBJECTS_H
+#define ZEND_OBJECTS_H
+
+#include "zend.h"
+
+typedef struct _zend_object_bucket {
+ zend_bool valid;
+ union _bucket {
+ struct {
+ zend_object object;
+ zend_uint refcount;
+ } obj;
+ struct {
+ int next;
+ } free_list;
+ } bucket;
+} zend_object_bucket;
+
+typedef struct _zend_objects {
+ zend_object_bucket *object_buckets;
+ zend_uint top;
+ zend_uint size;
+ int free_list_head;
+} zend_objects;
+
+void zend_objects_init(zend_objects *objects, zend_uint init_size);
+void zend_objects_destroy(zend_objects *objects);
+zend_object_value zend_objects_new(zend_object **object);
+zend_object *zend_objects_get_address(zend_object_handle handle);
+void zend_objects_add_ref(zend_object_handle handle);
+void zend_objects_del_ref(zend_object_handle handle);
+void zend_objects_delete_obj(zend_object_handle handle);
+
+#endif /* ZEND_OBJECTS_H */
\ No newline at end of file
}
-
#define Z_LVAL(zval) (zval).value.lval
#define Z_BVAL(zval) ((zend_bool)(zval).value.lval)
#define Z_DVAL(zval) (zval).value.dval
#define Z_STRVAL(zval) (zval).value.str.val
#define Z_STRLEN(zval) (zval).value.str.len
#define Z_ARRVAL(zval) (zval).value.ht
-#define Z_OBJ(zval) &(zval).value.obj
-#define Z_OBJPROP(zval) (zval).value.obj.properties
-#define Z_OBJCE(zval) (zval).value.obj.ce
+#define Z_OBJ(zval) (zval).value.obj.handlers.get_address((zval).value.obj.handle)
+#define Z_OBJPROP(zval) Z_OBJ(zval)->properties
+#define Z_OBJCE(zval) Z_OBJ(zval)->ce
#define Z_RESVAL(zval) (zval).value.lval
#define Z_LVAL_P(zval_p) Z_LVAL(*zval_p)
}
break;
case IS_OBJECT:
- zend_hash_destroy(zvalue->value.obj.properties);
- FREE_HASHTABLE(zvalue->value.obj.properties);
+ zvalue->value.obj.handlers.del_ref(zvalue->value.obj.handle);
break;
case IS_RESOURCE: {
TSRMLS_FETCH();
zend_hash_copy(zvalue->value.ht, original_ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
}
break;
- case IS_OBJECT: {
- zval *tmp;
- HashTable *original_ht = zvalue->value.obj.properties;
-
- ALLOC_HASHTABLE_REL(zvalue->value.obj.properties);
- zend_hash_init(zvalue->value.obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
- zend_hash_copy(zvalue->value.obj.properties, original_ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
- }
+ case IS_OBJECT:
+ zvalue->value.obj.handlers.add_ref(zvalue->value.obj.handle);
break;
}
return SUCCESS;