/* {{{ php_autoglobal_merge */ static void php_autoglobal_merge(HashTable *dest, HashTable *src) { zval *src_entry, *dest_entry; zend_string *string_key; zend_ulong num_key; int globals_check = (dest == (&EG(symbol_table))); ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) { if (Z_TYPE_P(src_entry) != IS_ARRAY || (string_key && (dest_entry = zend_hash_find(dest, string_key)) == NULL) || (string_key == NULL && (dest_entry = zend_hash_index_find(dest, num_key)) == NULL) || Z_TYPE_P(dest_entry) != IS_ARRAY) { if (Z_REFCOUNTED_P(src_entry)) { Z_ADDREF_P(src_entry); } if (string_key) { if (!globals_check || string_key->len != sizeof("GLOBALS") - 1 || memcmp(string_key->val, "GLOBALS", sizeof("GLOBALS") - 1)) { zend_hash_update(dest, string_key, src_entry); } else if (Z_REFCOUNTED_P(src_entry)) { Z_DELREF_P(src_entry); } } else { zend_hash_index_update(dest, num_key, src_entry); } } else { SEPARATE_ZVAL(dest_entry); php_autoglobal_merge(Z_ARRVAL_P(dest_entry), Z_ARRVAL_P(src_entry)); } } ZEND_HASH_FOREACH_END(); }
/* {{{ proto bool SplPriorityQueue::insert(mixed $value, mixed $priority) Push $value with the priority $priodiry on the priorityqueue */ SPL_METHOD(SplPriorityQueue, insert) { zval *data, *priority, elem; spl_heap_object *intern; if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &data, &priority) == FAILURE) { return; } intern = Z_SPLHEAP_P(getThis()); if (intern->heap->flags & SPL_HEAP_CORRUPTED) { zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0); return; } if (Z_REFCOUNTED_P(data)) Z_ADDREF_P(data); if (Z_REFCOUNTED_P(priority)) Z_ADDREF_P(priority); array_init(&elem); add_assoc_zval_ex(&elem, "data", sizeof("data") - 1, data); add_assoc_zval_ex(&elem, "priority", sizeof("priority") - 1, priority); spl_ptr_heap_insert(intern->heap, &elem, getThis()); RETURN_TRUE; }
static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /* {{{ */ { switch (Z_TYPE_P(expr)) { case IS_ARRAY: smart_str_appends(buf, "Array\n"); if (Z_REFCOUNTED_P(expr)) { if (Z_IS_RECURSIVE_P(expr)) { smart_str_appends(buf, " *RECURSION*"); return; } Z_PROTECT_RECURSION_P(expr); } print_hash(buf, Z_ARRVAL_P(expr), indent, 0); if (Z_REFCOUNTED_P(expr)) { Z_UNPROTECT_RECURSION_P(expr); } break; 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)); zend_string_release(class_name); smart_str_appends(buf, " Object\n"); if (Z_IS_RECURSIVE_P(expr)) { smart_str_appends(buf, " *RECURSION*"); return; } if ((properties = Z_OBJDEBUG_P(expr, is_temp)) == 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); } break; } case IS_LONG: smart_str_append_long(buf, Z_LVAL_P(expr)); break; case IS_REFERENCE: zend_print_zval_r_to_buf(buf, Z_REFVAL_P(expr), indent); break; default: { zend_string *str = zval_get_string(expr); smart_str_append(buf, str); zend_string_release(str); } break; } }
ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */ { switch (Z_TYPE_P(expr)) { case IS_ARRAY: ZEND_PUTS("Array ("); if (Z_REFCOUNTED_P(expr)) { if (Z_IS_RECURSIVE_P(expr)) { ZEND_PUTS(" *RECURSION*"); return; } Z_PROTECT_RECURSION_P(expr); } print_flat_hash(Z_ARRVAL_P(expr)); ZEND_PUTS(")"); if (Z_REFCOUNTED_P(expr)) { Z_UNPROTECT_RECURSION_P(expr); } break; case IS_OBJECT: { HashTable *properties = NULL; zend_string *class_name = Z_OBJ_HANDLER_P(expr, get_class_name)(Z_OBJ_P(expr)); zend_printf("%s Object (", ZSTR_VAL(class_name)); zend_string_release(class_name); if (Z_IS_RECURSIVE_P(expr)) { ZEND_PUTS(" *RECURSION*"); return; } if (Z_OBJ_HANDLER_P(expr, get_properties)) { properties = Z_OBJPROP_P(expr); } if (properties) { Z_PROTECT_RECURSION_P(expr); print_flat_hash(properties); Z_UNPROTECT_RECURSION_P(expr); } ZEND_PUTS(")"); break; } case IS_REFERENCE: zend_print_flat_zval_r(Z_REFVAL_P(expr)); break; default: zend_print_variable(expr); break; } }
static int php_get_if_index_from_zval(zval *val, unsigned *out) { int ret; if (Z_TYPE_P(val) == IS_LONG) { if (Z_LVAL_P(val) < 0 || (zend_ulong)Z_LVAL_P(val) > UINT_MAX) { php_error_docref(NULL, E_WARNING, "the interface index cannot be negative or larger than %u;" " given " ZEND_LONG_FMT, UINT_MAX, Z_LVAL_P(val)); ret = FAILURE; } else { *out = Z_LVAL_P(val); ret = SUCCESS; } } else { if (Z_REFCOUNTED_P(val)) { Z_ADDREF_P(val); } convert_to_string_ex(val); ret = php_string_to_if_index(Z_STRVAL_P(val), out); zval_ptr_dtor(val); } return ret; }
/* {{{ proto void SplFixedArray::__wakeup() */ SPL_METHOD(SplFixedArray, __wakeup) { spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(getThis()); HashTable *intern_ht = zend_std_get_properties(getThis()); zval *data; if (zend_parse_parameters_none() == FAILURE) { return; } if (!intern->array) { int index = 0; int size = zend_hash_num_elements(intern_ht); intern->array = emalloc(sizeof(spl_fixedarray)); spl_fixedarray_init(intern->array, size); ZEND_HASH_FOREACH_VAL(intern_ht, data) { if (Z_REFCOUNTED_P(data)) { Z_ADDREF_P(data); } ZVAL_COPY_VALUE(&intern->array->elements[index], data); index++; } ZEND_HASH_FOREACH_END(); /* Remove the unserialised properties, since we now have the elements * within the spl_fixedarray_object structure. */ zend_hash_clean(intern_ht); }
/* This function should only be used as a copy constructor, i.e. it * should only be called AFTER a zval has been copied to another * location using ZVAL_COPY_VALUE. Do not call it before copying, * otherwise a reference may be leaked. */ ZEND_API void zval_add_ref(zval *p) { if (Z_REFCOUNTED_P(p)) { if (Z_ISREF_P(p) && Z_REFCOUNT_P(p) == 1) { ZVAL_COPY(p, Z_REFVAL_P(p)); } else { Z_ADDREF_P(p); } } }
/* {{{ proto void SplDoublyLinkedList::add(mixed $index, mixed $newval) Inserts a new entry before the specified $index consisting of $newval. */ SPL_METHOD(SplDoublyLinkedList, add) { zval *zindex, *value; spl_dllist_object *intern; spl_ptr_llist_element *element; zend_long index; if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &zindex, &value) == FAILURE) { return; } intern = Z_SPLDLLIST_P(getThis()); index = spl_offset_convert_to_long(zindex); if (index < 0 || index > intern->llist->count) { zend_throw_exception(spl_ce_OutOfRangeException, "Offset invalid or out of range", 0); return; } if (Z_REFCOUNTED_P(value)) { Z_ADDREF_P(value); } if (index == intern->llist->count) { /* If index is the last entry+1 then we do a push because we're not inserting before any entry */ spl_ptr_llist_push(intern->llist, value); } else { /* Create the new element we want to insert */ spl_ptr_llist_element *elem = emalloc(sizeof(spl_ptr_llist_element)); /* Get the element we want to insert before */ element = spl_ptr_llist_offset(intern->llist, index, intern->flags & SPL_DLLIST_IT_LIFO); ZVAL_COPY_VALUE(&elem->data, value); elem->rc = 1; /* connect to the neighbours */ elem->next = element; elem->prev = element->prev; /* connect the neighbours to this new element */ if (elem->prev == NULL) { intern->llist->head = elem; } else { element->prev->next = elem; } element->prev = elem; intern->llist->count++; if (intern->llist->ctor) { intern->llist->ctor(elem); } } } /* }}} */
static void zend_generator_cleanup_unfinished_execution(zend_generator *generator) /* {{{ */ { zend_execute_data *execute_data = generator->execute_data; zend_op_array *op_array = &execute_data->func->op_array; if (generator->send_target) { if (Z_REFCOUNTED_P(generator->send_target)) Z_DELREF_P(generator->send_target); generator->send_target = NULL; } /* Manually free loop variables, as execution couldn't reach their * SWITCH_FREE / FREE opcodes. */ { /* -1 required because we want the last run opcode, not the * next to-be-run one. */ uint32_t op_num = execute_data->opline - op_array->opcodes - 1; int i; for (i = 0; i < op_array->last_brk_cont; ++i) { zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i; if (brk_cont->start < 0) { continue; } else if ((uint32_t)brk_cont->start > op_num) { break; } else if (brk_cont->brk >= 0 && (uint32_t)brk_cont->brk > op_num) { zend_op *brk_opline = op_array->opcodes + brk_cont->brk; if (brk_opline->opcode == ZEND_FREE) { zval *var = EX_VAR(brk_opline->op1.var); zval_ptr_dtor_nogc(var); } else if (brk_opline->opcode == ZEND_FE_FREE) { zval *var = EX_VAR(brk_opline->op1.var); if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); } } } } /* If yield was used as a function argument there may be active * method calls those objects need to be freed */ while (execute_data->call) { if (Z_OBJ(execute_data->call->This)) { OBJ_RELEASE(Z_OBJ(execute_data->call->This)); } execute_data->call = execute_data->call->prev_execute_data; } }
ZEND_API void zval_internal_ptr_dtor(zval *zval_ptr) /* {{{ */ { if (Z_REFCOUNTED_P(zval_ptr)) { zend_refcounted *ref = Z_COUNTED_P(zval_ptr); if (GC_DELREF(ref) == 0) { if (Z_TYPE_P(zval_ptr) == IS_STRING) { zend_string *str = (zend_string*)ref; CHECK_ZVAL_STRING(str); ZEND_ASSERT(!ZSTR_IS_INTERNED(str)); ZEND_ASSERT((GC_FLAGS(str) & IS_STR_PERSISTENT)); free(str); } else { zend_error_noreturn(E_CORE_ERROR, "Internal zval's can't be arrays, objects, resources or reference"); } } } }
void ZEND_FASTCALL zend_jit_copy_extra_args_helper(EXECUTE_DATA_D) { zend_op_array *op_array = &EX(func)->op_array; if (EXPECTED(!(op_array->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) { uint32_t first_extra_arg = op_array->num_args; uint32_t num_args = EX_NUM_ARGS(); zval *end, *src, *dst; uint32_t type_flags = 0; if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) { /* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */ #ifdef HAVE_GCC_GLOBAL_REGS opline += first_extra_arg; #endif } /* move extra args into separate array after all CV and TMP vars */ end = EX_VAR_NUM(first_extra_arg - 1); src = end + (num_args - first_extra_arg); dst = src + (op_array->last_var + op_array->T - first_extra_arg); if (EXPECTED(src != dst)) { do { type_flags |= Z_TYPE_INFO_P(src); ZVAL_COPY_VALUE(dst, src); ZVAL_UNDEF(src); src--; dst--; } while (src != end); if (type_flags & (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) { ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_FREE_EXTRA_ARGS); } } else { do { if (Z_REFCOUNTED_P(src)) { ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_FREE_EXTRA_ARGS); break; } src--; } while (src != end); } } }
static int php_get_address_from_array(const HashTable *ht, const char *key, php_socket *sock, php_sockaddr_storage *ss, socklen_t *ss_len) { zval *val; if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) { php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", key); return FAILURE; } if (Z_REFCOUNTED_P(val)) { Z_ADDREF_P(val); } convert_to_string_ex(val); if (!php_set_inet46_addr(ss, ss_len, Z_STRVAL_P(val), sock)) { zval_ptr_dtor(val); return FAILURE; } zval_ptr_dtor(val); return SUCCESS; }
/* {{{ proto bool SplHeap::insert(mixed $value) Push $value on the heap */ SPL_METHOD(SplHeap, insert) { zval *value; spl_heap_object *intern; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) { return; } intern = Z_SPLHEAP_P(getThis()); if (intern->heap->flags & SPL_HEAP_CORRUPTED) { zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0); return; } if (Z_REFCOUNTED_P(value)) Z_ADDREF_P(value); spl_ptr_heap_insert(intern->heap, value, getThis()); RETURN_TRUE; }
/* {{{ proto mixed SplPriorityQueue::extract() extract the element out of the top of the priority queue */ SPL_METHOD(SplPriorityQueue, extract) { zval value, *value_out; spl_heap_object *intern; if (zend_parse_parameters_none() == FAILURE) { return; } intern = Z_SPLHEAP_P(getThis()); if (intern->heap->flags & SPL_HEAP_CORRUPTED) { zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0); return; } spl_ptr_heap_delete_top(intern->heap, &value, getThis()); if (Z_ISUNDEF(value)) { zend_throw_exception(spl_ce_RuntimeException, "Can't extract from an empty heap", 0); return; } value_out = spl_pqueue_extract_helper(&value, intern->flags); if (!value_out) { zend_error(E_RECOVERABLE_ERROR, "Unable to extract from the PriorityQueue node"); zval_ptr_dtor(&value); return; } if (Z_REFCOUNTED_P(value_out)) { Z_ADDREF_P(value_out); } RETVAL_ZVAL(value_out, 1, 0); zval_ptr_dtor(&value); }
static void zend_persist_zval_calc(zval *z) { zend_uchar flags; uint size; switch (Z_TYPE_P(z)) { case IS_STRING: case IS_CONSTANT: flags = Z_GC_FLAGS_P(z) & ~ (IS_STR_PERSISTENT | IS_STR_INTERNED | IS_STR_PERMANENT); ADD_INTERNED_STRING(Z_STR_P(z), 0); if (!Z_REFCOUNTED_P(z)) { Z_TYPE_FLAGS_P(z) &= ~ (IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE); } Z_GC_FLAGS_P(z) |= flags; break; case IS_ARRAY: size = zend_shared_memdup_size(Z_ARR_P(z), sizeof(zend_array)); if (size) { ADD_SIZE(size); zend_hash_persist_calc(Z_ARRVAL_P(z), zend_persist_zval_calc); } break; case IS_REFERENCE: size = zend_shared_memdup_size(Z_REF_P(z), sizeof(zend_reference)); if (size) { ADD_SIZE(size); zend_persist_zval_calc(Z_REFVAL_P(z)); } break; case IS_CONSTANT_AST: size = zend_shared_memdup_size(Z_AST_P(z), sizeof(zend_ast_ref)); if (size) { ADD_SIZE(size); zend_persist_ast_calc(Z_ASTVAL_P(z)); } break; } }
/** * Restore a memory stack applying GC to all observed variables */ static void zephir_memory_restore_stack_common(zend_zephir_globals_def *g) { size_t i; zephir_memory_entry *prev, *active_memory; zephir_symbol_table *active_symbol_table; zval *ptr; #ifndef ZEPHIR_RELEASE int show_backtrace = 0; #endif active_memory = g->active_memory; assert(active_memory != NULL); if (EXPECTED(!CG(unclean_shutdown))) { /* Clean active symbol table */ if (g->active_symbol_table) { active_symbol_table = g->active_symbol_table; if (active_symbol_table->scope == active_memory) { zend_execute_data *ex = EG(current_execute_data); //zend_hash_destroy(EG(current_execute_data)->symbol_table); //FREE_HASHTABLE(EG(current_execute_data)->symbol_table); //EG(current_execute_data)->symbol_table = active_symbol_table->symbol_table; ex->symbol_table = active_symbol_table->symbol_table; g->active_symbol_table = active_symbol_table->prev; efree(active_symbol_table); } } /* Check for non freed hash key zvals, mark as null to avoid string freeing */ for (i = 0; i < active_memory->hash_pointer; ++i) { assert(active_memory->hash_addresses[i] != NULL); if (!Z_REFCOUNTED_P(active_memory->hash_addresses[i])) continue; if (Z_REFCOUNT_P(active_memory->hash_addresses[i]) <= 1) { ZVAL_NULL(active_memory->hash_addresses[i]); } else { zval_copy_ctor(active_memory->hash_addresses[i]); } } #ifndef ZEPHIR_RELEASE for (i = 0; i < active_memory->pointer; ++i) { if (active_memory->addresses[i] != NULL) { zval *var = active_memory->addresses[i]; if (Z_TYPE_P(var) > IS_CALLABLE) { fprintf(stderr, "%s: observed variable #%d (%p) has invalid type %u [%s]\n", __func__, (int)i, var, Z_TYPE_P(var), active_memory->func); show_backtrace = 1; } if (!Z_REFCOUNTED_P(var)) { continue; } if (Z_REFCOUNT_P(var) == 0) { fprintf(stderr, "%s: observed variable #%d (%p) has 0 references, type=%d [%s]\n", __func__, (int)i, var, Z_TYPE_P(var), active_memory->func); show_backtrace = 1; } else if (Z_REFCOUNT_P(var) >= 1000000) { fprintf(stderr, "%s: observed variable #%d (%p) has too many references (%u), type=%d [%s]\n", __func__, (int)i, var, Z_REFCOUNT_P(var), Z_TYPE_P(var), active_memory->func); show_backtrace = 1; } #if 0 /* Skip this check, PDO does return variables with is_ref = 1 and refcount = 1*/ else if (Z_REFCOUNT_PP(var) == 1 && Z_ISREF_PP(var)) { fprintf(stderr, "%s: observed variable #%d (%p) is a reference with reference count = 1, type=%d [%s]\n", __func__, (int)i, *var, Z_TYPE_PP(var), active_memory->func); } #endif } } #endif /* Traverse all zvals allocated, reduce the reference counting or free them */ for (i = 0; i < active_memory->pointer; ++i) { ptr = active_memory->addresses[i]; if (EXPECTED(ptr != NULL)) { if (!Z_REFCOUNTED_P(ptr)) continue; if (Z_REFCOUNT_P(ptr) == 1) { zval_ptr_dtor(ptr); } else { Z_DELREF_P(ptr); } } } } #ifndef ZEPHIR_RELEASE active_memory->func = NULL; #endif prev = active_memory->prev; if (active_memory >= g->end_memory || active_memory < g->start_memory) { #ifndef ZEPHIR_RELEASE assert(g->active_memory->permanent == 0); #endif assert(prev != NULL); if (active_memory->hash_addresses != NULL) { efree(active_memory->hash_addresses); } if (active_memory->addresses != NULL) { efree(active_memory->addresses); } efree(g->active_memory); g->active_memory = prev; prev->next = NULL; } else { #ifndef ZEPHIR_RELEASE assert(g->active_memory->permanent == 1); #endif active_memory->pointer = 0; active_memory->hash_pointer = 0; g->active_memory = prev; } #ifndef ZEPHIR_RELEASE if (g->active_memory) { zephir_memory_entry *f = g->active_memory; if (f >= g->start_memory && f < g->end_memory - 1) { assert(f->permanent == 1); assert(f->next != NULL); if (f > g->start_memory) { assert(f->prev != NULL); } } } if (show_backtrace == 1) { zephir_print_backtrace(); } #endif }
static phpdbg_watchpoint_t *phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch) { phpdbg_watchpoint_t *ret = watch; /* exclude references & refcounted */ if (!watch->parent || watch->parent->type != WATCH_ON_ZVAL || watch->type == WATCH_ON_HASHTABLE) { phpdbg_watchpoint_t *old_watch = zend_hash_find_ptr(&PHPDBG_G(watchpoints), watch->str); if (old_watch) { #define return_and_free_watch(x) { \ phpdbg_watchpoint_t *ref = phpdbg_get_refcount_watch(old_watch); \ if (ref) { \ phpdbg_add_watch_collision(ref); \ } \ if (watch != old_watch) { \ phpdbg_free_watch(watch); \ efree(watch); \ } \ return (x); \ } if (watch->flags & PHPDBG_WATCH_RECURSIVE) { if (old_watch->flags & PHPDBG_WATCH_RECURSIVE) { return_and_free_watch(NULL); } else { old_watch->flags &= ~(PHPDBG_WATCH_SIMPLE | PHPDBG_WATCH_IMPLICIT); old_watch->flags |= PHPDBG_WATCH_RECURSIVE; return_and_free_watch(old_watch); } } else { if (!(old_watch->flags & PHPDBG_WATCH_RECURSIVE)) { old_watch->flags |= watch->flags & (PHPDBG_WATCH_IMPLICIT | PHPDBG_WATCH_SIMPLE); } return_and_free_watch(NULL); } } else { if (watch->parent && watch->parent->type == WATCH_ON_HASHTABLE) { watch->parent->implicit_ht_count++; } zend_hash_add_ptr(&PHPDBG_G(watchpoints), watch->str, watch); } } phpdbg_store_watchpoint(watch); if (watch->parent && watch->parent->type == WATCH_ON_ZVAL && Z_REFCOUNTED_P(watch->parent->addr.zv)) { phpdbg_add_watch_collision(phpdbg_create_refcounted_watchpoint(watch, Z_COUNTED_P(watch->parent->addr.zv))); } if (watch->type == WATCH_ON_ZVAL) { if (watch->parent_container) { HashTable *ht_watches; phpdbg_btree_result *find; if (!(find = phpdbg_btree_find(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container))) { phpdbg_watch_ht_info *hti = emalloc(sizeof(*hti)); hti->dtor = watch->parent_container->pDestructor; ht_watches = &hti->watches; zend_hash_init(ht_watches, 0, grrrrr, ZVAL_PTR_DTOR, 0); phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container, hti); watch->parent_container->pDestructor = (dtor_func_t) phpdbg_watch_HashTable_dtor; } else { ht_watches = &((phpdbg_watch_ht_info *) find->ptr)->watches; } zend_hash_add_ptr(ht_watches, watch->name_in_parent, watch); } if (Z_ISREF_P(watch->addr.zv)) { ret = phpdbg_create_reference_watch(watch); } } phpdbg_activate_watchpoint(watch); return ret; }
static void spl_ptr_heap_zval_ctor(zval *elem) { /* {{{ */ if (Z_REFCOUNTED_P(elem)) { Z_ADDREF_P(elem); } }
/* {{{ my_copy_zval */ static APC_HOTSPOT void my_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt) { apc_pool* pool = ctxt->pool; assert(dst != NULL); assert(src != NULL); memcpy(dst, src, sizeof(zval)); if (Z_REFCOUNTED_P(src)) { if (zend_hash_num_elements(&ctxt->copied)) { zval *rc = zend_hash_index_find( &ctxt->copied, (uintptr_t) Z_COUNTED_P(src)); if (rc) { ZVAL_COPY(dst, rc); return; } } } switch (Z_TYPE_P(src)) { case IS_RESOURCE: case IS_TRUE: case IS_FALSE: case IS_LONG: case IS_DOUBLE: case IS_NULL: break; case IS_REFERENCE: Z_REF_P(dst) = my_copy_reference(Z_REF_P(src), ctxt); break; case IS_INDIRECT: my_copy_zval(dst, Z_INDIRECT_P(src), ctxt); break; case IS_CONSTANT: case IS_STRING: if (ctxt->copy == APC_COPY_OUT) { ZVAL_DUP(dst, src); } else { Z_TYPE_INFO_P(dst) = IS_STRING_EX; Z_STR_P(dst) = apc_pstrcpy(Z_STR_P(src), pool); } break; case IS_ARRAY: if(ctxt->serializer == NULL) { Z_ARRVAL_P(dst) = my_copy_hashtable(Z_ARRVAL_P(src), ctxt); break; } /* break intentionally omitted */ case IS_OBJECT: if(ctxt->copy == APC_COPY_IN) { dst = my_serialize_object(dst, src, ctxt); } else dst = my_unserialize_object(dst, src, ctxt); break; case IS_CALLABLE: /* XXX implement this */ assert(0); break; default: assert(0); } if (Z_REFCOUNTED_P(dst) && ctxt->copied.nTableSize) { zend_hash_index_update(&ctxt->copied, (uintptr_t) Z_COUNTED_P(src), dst); } }
static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *stack) { int count = 0; HashTable *ht = NULL; Bucket *p, *end; zval *zv; GC_STACK_DCL(stack); do { /* don't count references for compatibility ??? */ if (GC_TYPE(ref) != IS_REFERENCE) { count++; } if (GC_TYPE(ref) == IS_OBJECT) { zend_object *obj = (zend_object*)ref; if (EXPECTED(!(OBJ_FLAGS(ref) & IS_OBJ_FREE_CALLED))) { int n; zval *zv, *end; /* optimization: color is GC_BLACK (0) */ if (!GC_INFO(ref)) { gc_add_garbage(ref); } if (obj->handlers->dtor_obj != zend_objects_destroy_object || obj->ce->destructor != NULL) { *flags |= GC_HAS_DESTRUCTORS; } ht = obj->handlers->get_gc(obj, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) goto next; while (!Z_REFCOUNTED_P(--end)) { /* count non-refcounted for compatibility ??? */ if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } if (zv == end) goto next; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); GC_STACK_PUSH(ref); } /* count non-refcounted for compatibility ??? */ } else if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); continue; } goto next; } } else { goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { /* optimization: color is GC_BLACK (0) */ if (!GC_INFO(ref)) { gc_add_garbage(ref); } ht = (zend_array*)ref; } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); continue; } } goto next; } else { goto next; } if (!ht->nNumUsed) goto next; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } /* count non-refcounted for compatibility ??? */ if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } if (p == end) goto next; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); GC_STACK_PUSH(ref); } /* count non-refcounted for compatibility ??? */ } else if (Z_TYPE_P(zv) != IS_UNDEF) { count++; } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); continue; } next: ref = GC_STACK_POP(); } while (ref); return count; }
static void gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffer *root) { HashTable *ht = NULL; Bucket *p, *end; zval *zv; tail_call: if (root || (GC_REF_ADDRESS(ref) != 0 && GC_REF_CHECK_COLOR(ref, GC_BLACK))) { GC_TRACE_REF(ref, "removing from buffer"); if (root) { gc_remove_from_roots(root); GC_REF_SET_INFO(ref, 0); root = NULL; } else { GC_REMOVE_FROM_BUFFER(ref); } if (GC_TYPE(ref) == IS_OBJECT) { zend_object *obj = (zend_object*)ref; if (EXPECTED(!(OBJ_FLAGS(ref) & IS_OBJ_FREE_CALLED))) { int n; zval *zv, *end; ht = obj->handlers->get_gc(obj, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) return; while (!Z_REFCOUNTED_P(--end)) { if (zv == end) return; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); gc_remove_nested_data_from_buffer(ref, NULL); } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); goto tail_call; } } else { return; } } else if (GC_TYPE(ref) == IS_ARRAY) { ht = (zend_array*)ref; } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); goto tail_call; } return; } else { return; } if (!ht->nNumUsed) return; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } if (p == end) return; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); gc_remove_nested_data_from_buffer(ref, NULL); } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); goto tail_call; } }
static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) { HashTable *ht = NULL; Bucket *p, *end; zval *zv; GC_STACK_DCL(stack); do { GC_BENCH_INC(zval_marked_grey); if (GC_TYPE(ref) == IS_OBJECT) { zend_object *obj = (zend_object*)ref; if (EXPECTED(!(OBJ_FLAGS(ref) & IS_OBJ_FREE_CALLED))) { int n; zval *zv, *end; ht = obj->handlers->get_gc(obj, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) goto next; while (!Z_REFCOUNTED_P(--end)) { if (zv == end) goto next; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); GC_STACK_PUSH(ref); } } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); continue; } goto next; } } else { goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { if (((zend_array*)ref) == &EG(symbol_table)) { GC_REF_SET_BLACK(ref); goto next; } else { ht = (zend_array*)ref; } } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); continue; } } goto next; } else { goto next; } if (!ht->nNumUsed) goto next; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } if (p == end) goto next; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); GC_STACK_PUSH(ref); } } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); continue; } next: ref = GC_STACK_POP(); } while (ref); }
static void gc_scan(zend_refcounted *ref, gc_stack *stack) { HashTable *ht = NULL; Bucket *p, *end; zval *zv; GC_STACK_DCL(stack); tail_call: if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { if (GC_REFCOUNT(ref) > 0) { if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) { GC_REF_SET_BLACK(ref); if (UNEXPECTED(!_stack->next)) { gc_stack_next(_stack); } /* Split stack and reuse the tail */ _stack->next->prev = NULL; gc_scan_black(ref, _stack->next); _stack->next->prev = _stack; } } else { if (GC_TYPE(ref) == IS_OBJECT) { zend_object *obj = (zend_object*)ref; if (EXPECTED(!(OBJ_FLAGS(ref) & IS_OBJ_FREE_CALLED))) { int n; zval *zv, *end; ht = obj->handlers->get_gc(obj, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) goto next; while (!Z_REFCOUNTED_P(--end)) { if (zv == end) goto next; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); GC_STACK_PUSH(ref); } } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); goto tail_call; } goto next; } } else { goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { if ((zend_array*)ref == &EG(symbol_table)) { GC_REF_SET_BLACK(ref); goto next; } else { ht = (zend_array*)ref; } } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); goto tail_call; } } goto next; } else { goto next; } if (!ht->nNumUsed) goto next; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } if (p == end) goto next; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); GC_STACK_PUSH(ref); } } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); goto tail_call; } } } next: ref = GC_STACK_POP(); if (ref) { goto tail_call; } }