ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, zend_bool fast_shutdown) { zend_object **obj_ptr, **end, *obj; if (objects->top <= 1) { return; } /* Free object contents, but don't free objects themselves, so they show up as leaks */ end = objects->object_buckets + 1; obj_ptr = objects->object_buckets + objects->top; if (fast_shutdown) { do { obj_ptr--; obj = *obj_ptr; if (IS_OBJ_VALID(obj)) { if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED; if (obj->handlers->free_obj && obj->handlers->free_obj != zend_object_std_dtor) { GC_ADDREF(obj); obj->handlers->free_obj(obj); GC_DELREF(obj); } } } } while (obj_ptr != end); } else { do { obj_ptr--; obj = *obj_ptr; if (IS_OBJ_VALID(obj)) { if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED; if (obj->handlers->free_obj) { GC_ADDREF(obj); obj->handlers->free_obj(obj); GC_DELREF(obj); } } } } while (obj_ptr != end); } }
ZEND_API void ZEND_FASTCALL zend_objects_store_mark_destructed(zend_objects_store *objects) { if (objects->object_buckets && objects->top > 1) { zend_object **obj_ptr = objects->object_buckets + 1; zend_object **end = objects->object_buckets + objects->top; do { zend_object *obj = *obj_ptr; if (IS_OBJ_VALID(obj)) { GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED; } obj_ptr++; } while (obj_ptr != end); } }
ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object) /* {{{ */ { /* Make sure we hold a reference count during the destructor call otherwise, when the destructor ends the storage might be freed when the refcount reaches 0 a second time */ if (EG(objects_store).object_buckets && IS_OBJ_VALID(EG(objects_store).object_buckets[object->handle])) { if (GC_REFCOUNT(object) == 0) { if (!(GC_FLAGS(object) & IS_OBJ_DESTRUCTOR_CALLED)) { GC_FLAGS(object) |= IS_OBJ_DESTRUCTOR_CALLED; if (object->handlers->dtor_obj && (object->handlers->dtor_obj != zend_objects_destroy_object || object->ce->destructor)) { GC_ADDREF(object); object->handlers->dtor_obj(object); GC_DELREF(object); } } if (GC_REFCOUNT(object) == 0) { uint32_t handle = object->handle; void *ptr; EG(objects_store).object_buckets[handle] = SET_OBJ_INVALID(object); if (!(GC_FLAGS(object) & IS_OBJ_FREE_CALLED)) { GC_FLAGS(object) |= IS_OBJ_FREE_CALLED; if (object->handlers->free_obj) { GC_ADDREF(object); object->handlers->free_obj(object); GC_DELREF(object); } } ptr = ((char*)object) - object->handlers->offset; GC_REMOVE_FROM_BUFFER(object); efree(ptr); ZEND_OBJECTS_STORE_ADD_TO_FREE_LIST(handle); } } else { GC_DELREF(object); } } }
ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object) /* {{{ */ { ZEND_ASSERT(GC_REFCOUNT(object) == 0); /* GC might have released this object already. */ if (UNEXPECTED(GC_TYPE(object) == IS_NULL)) { return; } /* Make sure we hold a reference count during the destructor call otherwise, when the destructor ends the storage might be freed when the refcount reaches 0 a second time */ if (!(OBJ_FLAGS(object) & IS_OBJ_DESTRUCTOR_CALLED)) { GC_ADD_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); if (object->handlers->dtor_obj != zend_objects_destroy_object || object->ce->destructor) { GC_SET_REFCOUNT(object, 1); object->handlers->dtor_obj(object); GC_DELREF(object); } } if (GC_REFCOUNT(object) == 0) { uint32_t handle = object->handle; void *ptr; ZEND_ASSERT(EG(objects_store).object_buckets != NULL); ZEND_ASSERT(IS_OBJ_VALID(EG(objects_store).object_buckets[handle])); EG(objects_store).object_buckets[handle] = SET_OBJ_INVALID(object); if (!(OBJ_FLAGS(object) & IS_OBJ_FREE_CALLED)) { GC_ADD_FLAGS(object, IS_OBJ_FREE_CALLED); GC_SET_REFCOUNT(object, 1); object->handlers->free_obj(object); } ptr = ((char*)object) - object->handlers->offset; GC_REMOVE_FROM_BUFFER(object); efree(ptr); ZEND_OBJECTS_STORE_ADD_TO_FREE_LIST(handle); } }
ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_store *objects) { if (objects->top > 1) { uint32_t i; for (i = 1; i < objects->top; i++) { zend_object *obj = objects->object_buckets[i]; if (IS_OBJ_VALID(obj)) { if (!(OBJ_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) { GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); if (obj->handlers->dtor_obj != zend_objects_destroy_object || obj->ce->destructor) { GC_ADDREF(obj); obj->handlers->dtor_obj(obj); GC_DELREF(obj); } } } } } }
static int pgsql_stmt_dtor(pdo_stmt_t *stmt) { pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; zend_bool server_obj_usable = IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) && !(GC_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); if (S->result) { /* free the resource */ PQclear(S->result); S->result = NULL; } if (S->stmt_name) { if (S->is_prepared && server_obj_usable) { pdo_pgsql_db_handle *H = S->H; char *q = NULL; PGresult *res; spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); res = PQexec(H->server, q); efree(q); if (res) { PQclear(res); } } efree(S->stmt_name); S->stmt_name = NULL; } if (S->param_lengths) { efree(S->param_lengths); S->param_lengths = NULL; } if (S->param_values) { efree(S->param_values); S->param_values = NULL; } if (S->param_formats) { efree(S->param_formats); S->param_formats = NULL; } if (S->param_types) { efree(S->param_types); S->param_types = NULL; } if (S->query) { efree(S->query); S->query = NULL; } if (S->cursor_name) { if (server_obj_usable) { pdo_pgsql_db_handle *H = S->H; char *q = NULL; PGresult *res; spprintf(&q, 0, "CLOSE %s", S->cursor_name); res = PQexec(H->server, q); efree(q); if (res) PQclear(res); } efree(S->cursor_name); S->cursor_name = NULL; } if(S->cols) { efree(S->cols); S->cols = NULL; } efree(S); stmt->driver_data = NULL; return 1; }
/** * Copies of internal methods from Zend/zend_execute_API.c * These are used to call internal methods (not in the function table) from the external method. * TODO: See if xdebug works */ int runkit_forward_call_user_function(zend_function *fbc, zend_function *fbc_inner, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ { uint32_t i; zend_execute_data *call, dummy_execute_data; zend_fcall_info_cache fci_cache_local = {0}; zend_function *func; /* {{{ patch for runkit */ zend_fcall_info fci = {0}; zend_fcall_info_cache *fci_cache = NULL; fci.size = sizeof(fci); fci.object = NULL; // FIXME for methods? // object ? Z_OBJ_P(object) : NULL; ZVAL_STR(&fci.function_name, fbc_inner->common.function_name); zend_string_addref(fbc_inner->common.function_name); fci.retval = return_value; fci.param_count = ZEND_CALL_NUM_ARGS(EG(current_execute_data)); fci.params = ZEND_CALL_ARG(EG(current_execute_data), 1); // params and param_count From zend_API.c fci.no_separation = (zend_bool)1; // ??? /* end patch for runkit }}} */ ZVAL_UNDEF(fci.retval); if (!EG(active)) { return FAILURE; /* executor is already inactive */ } if (EG(exception)) { return FAILURE; /* we would result in an unstable executor otherwise */ } /* Initialize execute_data */ if (!EG(current_execute_data)) { /* This only happens when we're called outside any execute()'s * It shouldn't be strictly necessary to NULL execute_data out, * but it may make bugs easier to spot */ memset(&dummy_execute_data, 0, sizeof(zend_execute_data)); EG(current_execute_data) = &dummy_execute_data; } else if (EG(current_execute_data)->func && ZEND_USER_CODE(EG(current_execute_data)->func->common.type) && EG(current_execute_data)->opline->opcode != ZEND_DO_FCALL && EG(current_execute_data)->opline->opcode != ZEND_DO_ICALL && EG(current_execute_data)->opline->opcode != ZEND_DO_UCALL && EG(current_execute_data)->opline->opcode != ZEND_DO_FCALL_BY_NAME) { /* Insert fake frame in case of include or magic calls */ dummy_execute_data = *EG(current_execute_data); dummy_execute_data.prev_execute_data = EG(current_execute_data); dummy_execute_data.call = NULL; dummy_execute_data.opline = NULL; dummy_execute_data.func = NULL; EG(current_execute_data) = &dummy_execute_data; } if (!fci_cache || !RUNKIT_IS_FCI_CACHE_INITIALIZED(fci_cache)) { zend_string *callable_name; char *error = NULL; if (!fci_cache) { fci_cache = &fci_cache_local; } if (!zend_is_callable_ex(&fci.function_name, fci.object, IS_CALLABLE_CHECK_SILENT, &callable_name, fci_cache, &error)) { if (error) { zend_error(E_WARNING, "Invalid callback %s, %s", ZSTR_VAL(callable_name), error); efree(error); } if (callable_name) { zend_string_release(callable_name); } if (EG(current_execute_data) == &dummy_execute_data) { EG(current_execute_data) = dummy_execute_data.prev_execute_data; } return FAILURE; } else if (error) { /* Capitalize the first latter of the error message */ if (error[0] >= 'a' && error[0] <= 'z') { error[0] += ('A' - 'a'); } zend_error(E_DEPRECATED, "%s", error); efree(error); } zend_string_release(callable_name); } func = fbc_inner; fci.object = (func->common.fn_flags & ZEND_ACC_STATIC) ? NULL : fci_cache->object; call = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC, func, fci.param_count, fci_cache->called_scope, fci.object); if (fci.object && (!EG(objects_store).object_buckets || !IS_OBJ_VALID(EG(objects_store).object_buckets[fci.object->handle]))) { if (EG(current_execute_data) == &dummy_execute_data) { EG(current_execute_data) = dummy_execute_data.prev_execute_data; } return FAILURE; } if (func->common.fn_flags & (ZEND_ACC_ABSTRACT | ZEND_ACC_DEPRECATED)) { if (func->common.fn_flags & ZEND_ACC_ABSTRACT) { zend_throw_error(NULL, "Cannot call abstract method %s::%s()", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name)); if (EG(current_execute_data) == &dummy_execute_data) { EG(current_execute_data) = dummy_execute_data.prev_execute_data; } return FAILURE; } if (func->common.fn_flags & ZEND_ACC_DEPRECATED) { zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated", func->common.scope ? ZSTR_VAL(func->common.scope->name) : "", func->common.scope ? "::" : "", ZSTR_VAL(func->common.function_name)); } } for (i = 0; i < fci.param_count; i++) { zval *param; zval *arg = &fci.params[i]; if (ARG_SHOULD_BE_SENT_BY_REF(func, i + 1)) { if (UNEXPECTED(!Z_ISREF_P(arg))) { if (!fci.no_separation) { /* Separation is enabled -- create a ref */ ZVAL_NEW_REF(arg, arg); } else if (!ARG_MAY_BE_SENT_BY_REF(func, i + 1)) { /* By-value send is not allowed -- emit a warning, * but still perform the call with a by-value send. */ zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given", i + 1, func->common.scope ? ZSTR_VAL(func->common.scope->name) : "", func->common.scope ? "::" : "", ZSTR_VAL(func->common.function_name)); } } } else { if (Z_ISREF_P(arg) && !(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { /* don't separate references for __call */ arg = Z_REFVAL_P(arg); } } param = ZEND_CALL_ARG(call, i + 1); ZVAL_COPY(param, arg); } if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) { ZEND_ASSERT(GC_TYPE((zend_object *)func->op_array.prototype) == IS_OBJECT); GC_ADDREF((zend_object *)func->op_array.prototype); ZEND_ADD_CALL_FLAG(call, ZEND_CALL_CLOSURE); } if (func->type == ZEND_USER_FUNCTION) { int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; zend_init_execute_data(call, &func->op_array, fci.retval); zend_execute_ex(call); if (call_via_handler) { /* We must re-initialize function again */ RUNKIT_CLEAR_FCI_CACHE(fci_cache); } } else if (func->type == ZEND_INTERNAL_FUNCTION) { int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; ZVAL_NULL(fci.retval); call->prev_execute_data = EG(current_execute_data); call->return_value = NULL; /* this is not a constructor call */ EG(current_execute_data) = call; if (EXPECTED(zend_execute_internal == NULL)) { /* saves one function call if zend_execute_internal is not used */ func->internal_function.handler(call, fci.retval); } else { zend_execute_internal(call, fci.retval); } EG(current_execute_data) = call->prev_execute_data; zend_vm_stack_free_args(call); /* We shouldn't fix bad extensions here, because it can break proper ones (Bug #34045) if (!EX(function_state).function->common.return_reference) { INIT_PZVAL(f->retval); }*/ if (EG(exception)) { zval_ptr_dtor(fci.retval); ZVAL_UNDEF(fci.retval); } if (call_via_handler) { /* We must re-initialize function again */ RUNKIT_CLEAR_FCI_CACHE(fci_cache); } } else { /* ZEND_OVERLOADED_FUNCTION */ ZVAL_NULL(fci.retval); /* Not sure what should be done here if it's a static method */ if (fci.object) { call->prev_execute_data = EG(current_execute_data); EG(current_execute_data) = call; fci.object->handlers->call_method(func->common.function_name, fci.object, call, fci.retval); EG(current_execute_data) = call->prev_execute_data; } else { zend_throw_error(NULL, "Cannot call overloaded function for non-object"); } zend_vm_stack_free_args(call); if (func->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { zend_string_release(func->common.function_name); } efree(func); if (EG(exception)) { zval_ptr_dtor(fci.retval); ZVAL_UNDEF(fci.retval); } } zend_vm_stack_free_call_frame(call); if (EG(current_execute_data) == &dummy_execute_data) { EG(current_execute_data) = dummy_execute_data.prev_execute_data; } if (EG(exception)) { zend_throw_exception_internal(NULL); } return SUCCESS; }