Skip to content

Introduce get_properties_for #3579

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,6 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
case IS_OBJECT:
{
HashTable *properties;
int is_temp;

zend_string *class_name = Z_OBJ_HANDLER_P(expr, get_class_name)(Z_OBJ_P(expr));
smart_str_appends(buf, ZSTR_VAL(class_name));
Expand All @@ -449,18 +448,16 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
smart_str_appends(buf, " *RECURSION*");
return;
}
if ((properties = Z_OBJDEBUG_P(expr, is_temp)) == NULL) {

if ((properties = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_DEBUG)) == NULL) {
break;
}

Z_PROTECT_RECURSION_P(expr);
print_hash(buf, properties, indent, 1);
Z_UNPROTECT_RECURSION_P(expr);

if (is_temp) {
zend_hash_destroy(properties);
FREE_HASHTABLE(properties);
}
zend_release_properties(properties);
break;
}
case IS_LONG:
Expand Down
40 changes: 40 additions & 0 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1737,6 +1737,45 @@ ZEND_API int zend_std_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_fun
}
/* }}} */

ZEND_API HashTable *zend_std_get_properties_for(zval *obj, zend_prop_purpose purpose) {
HashTable *ht;
switch (purpose) {
case ZEND_PROP_PURPOSE_DEBUG:
if (Z_OBJ_HT_P(obj)->get_debug_info) {
int is_temp;
ht = Z_OBJ_HT_P(obj)->get_debug_info(obj, &is_temp);
if (ht && !is_temp && !(GC_FLAGS(ht) & GC_IMMUTABLE)) {
GC_ADDREF(ht);
}
return ht;
}
/* break missing intentionally */
case ZEND_PROP_PURPOSE_ARRAY_CAST:
case ZEND_PROP_PURPOSE_SERIALIZE:
case ZEND_PROP_PURPOSE_VAR_EXPORT:
case ZEND_PROP_PURPOSE_JSON:
ht = Z_OBJ_HT_P(obj)->get_properties(obj);
if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) {
GC_ADDREF(ht);
}
return ht;
default:
return NULL;
}
}

ZEND_API HashTable *zend_get_properties_for(zval *obj, zend_prop_purpose purpose) {
HashTable *ht;
if (Z_OBJ_HT_P(obj)->get_properties_for) {
ht = Z_OBJ_HT_P(obj)->get_properties_for(obj, purpose);
if (ht) {
return ht;
}
}

return zend_std_get_properties_for(obj, purpose);
}

ZEND_API const zend_object_handlers std_object_handlers = {
0, /* offset */

Expand Down Expand Up @@ -1768,6 +1807,7 @@ ZEND_API const zend_object_handlers std_object_handlers = {
zend_std_get_gc, /* get_gc */
NULL, /* do_operation */
NULL, /* compare */
NULL, /* get_properties_for */
};

/*
Expand Down
36 changes: 36 additions & 0 deletions Zend/zend_object_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,27 @@ typedef HashTable *(*zend_object_get_properties_t)(zval *object);

typedef HashTable *(*zend_object_get_debug_info_t)(zval *object, int *is_temp);

typedef enum _zend_prop_purpose {
/* Used for debugging. Supersedes get_debug_info handler. */
ZEND_PROP_PURPOSE_DEBUG,
/* Used for (array) casts. */
ZEND_PROP_PURPOSE_ARRAY_CAST,
/* Used for serialization using the "O" scheme.
* Unserialization will use __wakeup(). */
ZEND_PROP_PURPOSE_SERIALIZE,
/* Used for var_export().
* The data will be passed to __set_state() when evaluated. */
ZEND_PROP_PURPOSE_VAR_EXPORT,
/* Used for json_encode(). */
ZEND_PROP_PURPOSE_JSON,
/* Dummy member to ensure that "default" is specified. */
_ZEND_PROP_PURPOSE_NON_EXHAUSTIVE_ENUM
} zend_prop_purpose;

/* The return value may be NULL. A non-NULL return value must be released using
* zend_release_properties(). Releasing NULL is also safe. */
typedef zend_array *(*zend_object_get_properties_for_t)(zval *object, zend_prop_purpose purpose);

/* Used to call methods */
/* args on stack! */
/* Andi - EX(fbc) (function being called) needs to be initialized already in the INIT fcall opcode so that the parameters can be parsed the right way. We need to add another callback for this.
Expand Down Expand Up @@ -160,6 +181,7 @@ struct _zend_object_handlers {
zend_object_get_gc_t get_gc;
zend_object_do_operation_t do_operation;
zend_object_compare_zvals_t compare;
zend_object_get_properties_for_t get_properties_for; /* optional */
};

BEGIN_EXTERN_C()
Expand Down Expand Up @@ -207,6 +229,20 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend

ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member);

/* Default behavior for get_properties_for. For use as a fallback in custom
* get_properties_for implementations. */
ZEND_API HashTable *zend_std_get_properties_for(zval *obj, zend_prop_purpose purpose);

/* Will call get_properties_for handler or use default behavior. For use by
* consumers of the get_properties_for API. */
ZEND_API HashTable *zend_get_properties_for(zval *obj, zend_prop_purpose purpose);

#define zend_release_properties(ht) do { \
if ((ht) && !(GC_FLAGS(ht) & GC_IMMUTABLE) && !GC_DELREF(ht)) { \
zend_array_destroy(ht); \
} \
} while (0)

#define zend_free_trampoline(func) do { \
if ((func) == &EG(trampoline)) { \
EG(trampoline).common.function_name = NULL; \
Expand Down
36 changes: 12 additions & 24 deletions Zend/zend_operators.c
Original file line number Diff line number Diff line change
Expand Up @@ -610,32 +610,20 @@ ZEND_API void ZEND_FASTCALL convert_to_array(zval *op) /* {{{ */
if (Z_OBJCE_P(op) == zend_ce_closure) {
convert_scalar_to_array(op);
} else {
if (Z_OBJ_HT_P(op)->get_properties) {
HashTable *obj_ht = Z_OBJ_HT_P(op)->get_properties(op);
if (obj_ht) {
/* fast copy */
obj_ht = zend_proptable_to_symtable(obj_ht,
(Z_OBJCE_P(op)->default_properties_count ||
Z_OBJ_P(op)->handlers != &std_object_handlers ||
GC_IS_RECURSIVE(obj_ht)));
zval_ptr_dtor(op);
ZVAL_ARR(op, obj_ht);
return;
}
HashTable *obj_ht = zend_get_properties_for(op, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
HashTable *new_obj_ht = zend_proptable_to_symtable(obj_ht,
(Z_OBJCE_P(op)->default_properties_count ||
Z_OBJ_P(op)->handlers != &std_object_handlers ||
GC_IS_RECURSIVE(obj_ht)));
zval_ptr_dtor(op);
ZVAL_ARR(op, new_obj_ht);
zend_release_properties(obj_ht);
} else {
zval dst;
convert_object_to_type(op, &dst, IS_ARRAY, convert_to_array);

if (Z_TYPE(dst) == IS_ARRAY) {
zval_ptr_dtor(op);
ZVAL_COPY_VALUE(op, &dst);
return;
}
zval_ptr_dtor(op);
/*ZVAL_EMPTY_ARRAY(op);*/
array_init(op);
}

zval_ptr_dtor(op);
/*ZVAL_EMPTY_ARRAY(op);*/
array_init(op);
}
break;
case IS_NULL:
Expand Down
3 changes: 0 additions & 3 deletions Zend/zend_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
#define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(&(zval))
#define Z_OBJPROP_P(zval_p) Z_OBJPROP(*(zval_p))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add additional "for" argument to existing get_properties() handler, and change this into Z_OBJ_HT((zval))->get_properties(&(zval), ZEND_PROP_PURPOSE_DEFAULT)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we might need additional ZEND_PROP_PURPOSE_FOREACH_READ and ZEND_PROP_PURPOSE_FOREACH_WRITE to avoid useless duplications in SPL ArrayObject.


#define Z_OBJDEBUG(zval,tmp) (Z_OBJ_HANDLER((zval),get_debug_info)?Z_OBJ_HANDLER((zval),get_debug_info)(&(zval),&tmp):(tmp=0,Z_OBJPROP(zval)))
#define Z_OBJDEBUG_P(zval_p,tmp) Z_OBJDEBUG(*(zval_p), tmp)

#define Z_RES(zval) (zval).value.res
#define Z_RES_P(zval_p) Z_RES(*zval_p)

Expand Down
14 changes: 5 additions & 9 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -5307,22 +5307,18 @@ ZEND_VM_COLD_CONST_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY, TYPE)
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else if (Z_OBJ_HT_P(expr)->get_properties) {
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
/* fast copy */
obj_ht = zend_proptable_to_symtable(obj_ht,
ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
(Z_OBJCE_P(expr)->default_properties_count ||
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
GC_IS_RECURSIVE(obj_ht)));
ZVAL_ARR(result, obj_ht);
GC_IS_RECURSIVE(obj_ht))));
zend_release_properties(obj_ht);
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else {
ZVAL_COPY_VALUE(result, expr);
Z_ADDREF_P(result);
convert_to_array(result);
}
} else {
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
Expand Down
56 changes: 20 additions & 36 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -3121,22 +3121,18 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_H
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else if (Z_OBJ_HT_P(expr)->get_properties) {
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
/* fast copy */
obj_ht = zend_proptable_to_symtable(obj_ht,
ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
(Z_OBJCE_P(expr)->default_properties_count ||
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
GC_IS_RECURSIVE(obj_ht)));
ZVAL_ARR(result, obj_ht);
GC_IS_RECURSIVE(obj_ht))));
zend_release_properties(obj_ht);
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else {
ZVAL_COPY_VALUE(result, expr);
Z_ADDREF_P(result);
convert_to_array(result);
}
} else {
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
Expand Down Expand Up @@ -18092,22 +18088,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else if (Z_OBJ_HT_P(expr)->get_properties) {
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
/* fast copy */
obj_ht = zend_proptable_to_symtable(obj_ht,
ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
(Z_OBJCE_P(expr)->default_properties_count ||
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
GC_IS_RECURSIVE(obj_ht)));
ZVAL_ARR(result, obj_ht);
GC_IS_RECURSIVE(obj_ht))));
zend_release_properties(obj_ht);
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else {
ZVAL_COPY_VALUE(result, expr);
Z_ADDREF_P(result);
convert_to_array(result);
}
} else {
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
Expand Down Expand Up @@ -21100,22 +21092,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else if (Z_OBJ_HT_P(expr)->get_properties) {
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
/* fast copy */
obj_ht = zend_proptable_to_symtable(obj_ht,
ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
(Z_OBJCE_P(expr)->default_properties_count ||
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
GC_IS_RECURSIVE(obj_ht)));
ZVAL_ARR(result, obj_ht);
GC_IS_RECURSIVE(obj_ht))));
zend_release_properties(obj_ht);
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else {
ZVAL_COPY_VALUE(result, expr);
Z_ADDREF_P(result);
convert_to_array(result);
}
} else {
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
Expand Down Expand Up @@ -37467,22 +37455,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else if (Z_OBJ_HT_P(expr)->get_properties) {
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
} else {
HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
if (obj_ht) {
/* fast copy */
obj_ht = zend_proptable_to_symtable(obj_ht,
ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
(Z_OBJCE_P(expr)->default_properties_count ||
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
GC_IS_RECURSIVE(obj_ht)));
ZVAL_ARR(result, obj_ht);
GC_IS_RECURSIVE(obj_ht))));
zend_release_properties(obj_ht);
} else {
ZVAL_EMPTY_ARRAY(result);
}
} else {
ZVAL_COPY_VALUE(result, expr);
Z_ADDREF_P(result);
convert_to_array(result);
}
} else {
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
Expand Down
Loading