text: /*
   * This file is part of the Zephir.
   *
   * (c) Phalcon Team <team@zephir-lang.com>
   *
   * For the full copyright and license information, please view the LICENSE
   * file that was distributed with this source code. If you did not receive
   * a copy of the license it is available through the world-wide-web at the
   * following url: https://docs.zephir-lang.com/en/latest/license
   */
  
  #ifndef ZEPHIR_KERNEL_MEMORY_H
  #define ZEPHIR_KERNEL_MEMORY_H
  
  #include <php.h>
  #include <Zend/zend.h>
  #include "php_ext.h"
  #include "kernel/globals.h"
  
  #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64)
  	#define ZEPHIR_ENABLE_64BITS 1
  #endif
  
  #define ZEPHIR_NUM_PREALLOCATED_FRAMES 25
  
  /** Memory frame */
  typedef struct _zephir_memory_entry {
  	size_t pointer;
  	size_t capacity;
  	zval **addresses;
  #ifndef ZEPHIR_RELEASE
  	int permanent;
  	const char *func;
  #endif
  } zephir_memory_entry;
  
  /** Virtual Symbol Table */
  typedef struct _zephir_symbol_table {
  	struct _zephir_memory_entry *scope;
  	zend_array *symbol_table;
  	struct _zephir_symbol_table *prev;
  } zephir_symbol_table;
  
  typedef struct _zephir_method_globals {
  	/* Memory */
  	zephir_memory_entry *active_memory; /**< The current memory frame */
  
  	/* Virtual Symbol Tables */
  	zephir_symbol_table *active_symbol_table;
  } zephir_method_globals;
  
  /* Memory Frames */
  void ZEPHIR_FASTCALL zephir_memory_grow_stack(zephir_method_globals *g, const char *func);
  void ZEPHIR_FASTCALL zephir_memory_restore_stack(zephir_method_globals *g, const char *func);
  
  #define ZEPHIR_MM_GROW()  \
  	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); \
  	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
  
  #define ZEPHIR_MM_RESTORE() \
  	zephir_memory_restore_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); \
  	pefree(ZEPHIR_METHOD_GLOBALS_PTR, 0); \
  	ZEPHIR_METHOD_GLOBALS_PTR = NULL;
  
  void zephir_initialize_memory(zend_zephir_globals_def *zephir_globals_ptr);
  int zephir_cleanup_fcache(void *pDest, int num_args, va_list args, zend_hash_key *hash_key);
  void zephir_deinitialize_memory();
  
  #define zephir_ptr_dtor(x) zval_ptr_dtor(x)
  
  void ZEPHIR_FASTCALL zephir_do_memory_observe(zval *var, const zephir_method_globals *g);
  #define zephir_memory_observe(var) zephir_do_memory_observe(var, ZEPHIR_METHOD_GLOBALS_PTR);
  
  #define zephir_safe_zval_ptr_dtor(pzval)
  
  void zephir_create_symbol_table(zephir_method_globals *g);
  
  #define ZEPHIR_CREATE_SYMBOL_TABLE() zephir_create_symbol_table(ZEPHIR_METHOD_GLOBALS_PTR);
  
  int zephir_set_symbol(zval *key_name, zval *value);
  int zephir_set_symbol_str(char *key_name, unsigned int key_length, zval *value);
  
  #define ZEPHIR_INIT_VAR(z) \
  	zephir_memory_observe(z); \
  	ZVAL_NULL(z);
  
  #define ZEPHIR_INIT_ZVAL_NREF(z) ZVAL_UNDEF(&z);
  
  #define ZEPHIR_INIT_NVAR(z) \
  	do { \
  		if (Z_TYPE_P(z) == IS_UNDEF) { \
  			zephir_memory_observe(z); \
  		} else if (Z_REFCOUNTED_P(z) && !Z_ISREF_P(z)) { \
  			if (Z_REFCOUNT_P(z) > 1) { \
  				Z_DELREF_P(z); \
  			} else { \
  				zval_dtor(z); \
  			} \
  		} \
  		ZVAL_NULL(z); \
  	} while (0)
  
  /* only removes the value body of the zval */
  #define ZEPHIR_INIT_LNVAR(z) ZEPHIR_INIT_NVAR(&z)
  
  #define ZEPHIR_CPY_WRT(d, v) \
  	Z_TRY_ADDREF_P(v); \
  	if (Z_TYPE_P(d) > IS_UNDEF) { \
  		if (Z_REFCOUNTED_P(d) && Z_REFCOUNT_P(d) > 0) { \
  			zephir_ptr_dtor(d); \
  		} \
  	} else { \
  		zephir_memory_observe(d); \
  	} \
  	ZVAL_COPY_VALUE(d, v);
  
  #define ZEPHIR_CPY_WRT_CTOR(d, v) \
  	if (d) { \
  		if (Z_REFCOUNTED_P(d) && Z_REFCOUNT_P(d) > 0) { \
  			zephir_ptr_dtor(d); \
  		} \
  	} else { \
  		/*TODO: as above */ \
  	} \
  	ZVAL_DUP(d, v);
  
  #define ZEPHIR_OBS_VAR(z) \
  	zephir_memory_observe(z)
  
  #define ZEPHIR_OBS_VAR_ONCE(z) \
  	if (Z_TYPE_P(z) == IS_UNDEF) { \
  		zephir_memory_observe(z); \
  	}
  
  #define ZEPHIR_OBS_COPY_OR_DUP(z, v) \
  		ZEPHIR_OBS_VAR_ONCE(z); \
  		ZVAL_COPY(z, v);
  
  #define ZEPHIR_HASH_COPY(z, v) \
  	if (Z_TYPE_P(z) == IS_ARRAY && Z_TYPE_P(v) == IS_ARRAY) { \
  		zend_hash_copy(Z_ARRVAL_P(z), Z_ARRVAL_P(v), (copy_ctor_func_t) zval_add_ref); \
  	}
  
  #define ZEPHIR_OBS_NVAR(z) \
  	if (Z_TYPE_P(z) != IS_UNDEF) { \
  		if (Z_REFCOUNTED_P(z) && Z_REFCOUNT_P(z) > 1) { \
  			Z_DELREF_P(z); \
  		} else {\
  			zephir_ptr_dtor(z); \
  			ZVAL_NULL(z); \
  		} \
  	} else { \
  		zephir_memory_observe(z); \
  	}
  
  /* TODO: this might causes troubles, since we cannot observe here, since we aren't using double pointers
   * figure out away to fix this (if it's an issue, which it isn't if observing isn't necessary)
   */
  #define ZEPHIR_OBSERVE_OR_NULLIFY_PPZV(ppzv) \
  	do { \
  		zval *tmp_ = (ppzv); \
  		if (tmp_ != NULL) { \
  			if (Z_TYPE_P(tmp_) != IS_UNDEF) { \
  				zephir_ptr_dtor(tmp_); \
  			} else { \
  				zephir_memory_observe(tmp_); \
  			} \
  			ZVAL_NULL(tmp_); \
  		} \
  	} while (0)
  
  #define ZEPHIR_SEPARATE(z) SEPARATE_ZVAL(z)
  
  #define ZEPHIR_SEPARATE_PARAM(z) \
  	do { \
  		zval *orig_ptr = z; \
  		ZEPHIR_SEPARATE(orig_ptr); \
  		/*zephir_memory_observe(orig_ptr);*/ \
  	} while (0)
  
  #endif
  
  /* Backwards compatibility for GC API change in PHP 7.3 */
  #if PHP_VERSION_ID < 70300
  #  define GC_ADDREF(p)            ++GC_REFCOUNT(p)
  #  define GC_DELREF(p)            --GC_REFCOUNT(p)
  #  define GC_SET_REFCOUNT(p, rc)  GC_REFCOUNT(p) = rc
  #endif
  