--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | APC |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
+ | Rasmus Lerdorf <rasmus@php.net> |
+ | Arun C. Murthy <arunc@yahoo-inc.com> |
+ | Gopal Vijayaraghavan <gopalv@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+
+ This software was contributed to PHP by Community Connect Inc. in 2002
+ and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
+ Future revisions and derivatives of this source code must acknowledge
+ Community Connect Inc. as the original contributor of this module by
+ leaving this note intact in the source code.
+
+ All other licensing and usage conditions are those of the PHP Group.
+
+ */
+
+/* $Id: apc_compile.c,v 3.87.2.4 2008/03/28 21:05:14 rasmus Exp $ */
+
+#include "apc_compile.h"
+#include "apc_globals.h"
+#include "apc_zend.h"
+
+#ifndef Z_REFCOUNT_P
+#define Z_REFCOUNT_P(pz) (pz)->refcount
+#define Z_REFCOUNT_PP(ppz) Z_REFCOUNT_P(*(ppz))
+#endif
+
+#ifndef Z_SET_REFCOUNT_P
+#define Z_SET_REFCOUNT_P(pz, rc) (pz)->refcount = rc
+#define Z_SET_REFCOUNT_PP(ppz, rc) Z_SET_REFCOUNT_P(*(ppz), rc)
+#endif
+
+#ifndef Z_ADDREF_P
+#define Z_ADDREF_P(pz) (pz)->refcount++
+#define Z_ADDREF_PP(ppz) Z_ADDREF_P(*(ppz))
+#endif
+
+#ifndef Z_DELREF_P
+#define Z_DELREF_P(pz) (pz)->refcount--
+#define Z_DELREF_PP(ppz) Z_DELREF_P(*(ppz))
+#endif
+
+#ifndef Z_ISREF_P
+#define Z_ISREF_P(pz) (pz)->is_ref
+#define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz))
+#endif
+
+#ifndef Z_SET_ISREF_P
+#define Z_SET_ISREF_P(pz) (pz)->is_ref = 1
+#define Z_SET_ISREF_PP(ppz) Z_SET_ISREF_P(*(ppz))
+#endif
+
+#ifndef Z_UNSET_ISREF_P
+#define Z_UNSET_ISREF_P(pz) (pz)->is_ref = 0
+#define Z_UNSET_ISREF_PP(ppz) Z_UNSET_ISREF_P(*(ppz))
+#endif
+
+#ifndef Z_SET_ISREF_TO_P
+#define Z_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = isref
+#define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref)
+#endif
+
+typedef void* (*ht_copy_fun_t)(void*, void*, apc_malloc_t, apc_free_t);
+typedef void (*ht_free_fun_t)(void*, apc_free_t);
+typedef int (*ht_check_copy_fun_t)(Bucket*, va_list);
+
+#ifdef ZEND_ENGINE_2
+typedef void (*ht_fixup_fun_t)(Bucket*, zend_class_entry*, zend_class_entry*);
+#endif
+
+#define CHECK(p) { if ((p) == NULL) return NULL; }
+
+/* {{{ internal function declarations */
+
+static int is_derived_class(zend_op_array* op_array, const char* key, int key_size);
+
+static zend_function* my_bitwise_copy_function(zend_function*, zend_function*, apc_malloc_t);
+
+/*
+ * The "copy" functions perform deep-copies on a particular data structure
+ * (passed as the second argument). They also optionally allocate space for
+ * the destination data structure if the first argument is null.
+ */
+static zval** my_copy_zval_ptr(zval**, const zval**, apc_malloc_t, apc_free_t);
+static zval* my_copy_zval(zval*, const zval*, apc_malloc_t, apc_free_t);
+static znode* my_copy_znode(znode*, znode*, apc_malloc_t, apc_free_t);
+static zend_op* my_copy_zend_op(zend_op*, zend_op*, apc_malloc_t, apc_free_t);
+static zend_function* my_copy_function(zend_function*, zend_function*, apc_malloc_t, apc_free_t);
+static zend_function_entry* my_copy_function_entry(zend_function_entry*, zend_function_entry*, apc_malloc_t, apc_free_t);
+static zend_class_entry* my_copy_class_entry(zend_class_entry*, zend_class_entry*, apc_malloc_t, apc_free_t);
+static HashTable* my_copy_hashtable_ex(HashTable*, HashTable*, ht_copy_fun_t, ht_free_fun_t, int, apc_malloc_t, apc_free_t, ht_check_copy_fun_t, ...);
+#define my_copy_hashtable( dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate) \
+ my_copy_hashtable_ex(dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate, NULL)
+static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate);
+#ifdef ZEND_ENGINE_2
+static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate);
+static zend_arg_info* my_copy_arg_info_array(zend_arg_info*, zend_arg_info*, uint, apc_malloc_t, apc_free_t);
+static zend_arg_info* my_copy_arg_info(zend_arg_info*, zend_arg_info*, apc_malloc_t, apc_free_t);
+#endif
+/*
+ * The "destroy" functions free the memory associated with a particular data
+ * structure but do not free the pointer to the data structure.
+ *
+ * my_destroy_zval() returns SUCCESS or FAILURE, FAILURE means that
+ * the zval* has other references elsewhere
+ */
+static int my_destroy_zval(zval*, apc_free_t);
+static void my_destroy_zval_ptr(zval**, apc_free_t);
+static void my_destroy_zend_op(zend_op*, apc_free_t);
+static void my_destroy_znode(znode*, apc_free_t);
+static void my_destroy_function(zend_function*, apc_free_t);
+static void my_destroy_function_entry(zend_function_entry*, apc_free_t);
+static void my_destroy_class_entry(zend_class_entry*, apc_free_t);
+static void my_destroy_hashtable(HashTable*, ht_free_fun_t, apc_free_t);
+static void my_destroy_op_array(zend_op_array*, apc_free_t);
+#ifdef ZEND_ENGINE_2
+static void my_destroy_property_info(zend_property_info*, apc_free_t);
+static void my_destroy_arg_info_array(zend_arg_info* src, uint, apc_free_t);
+static void my_destroy_arg_info(zend_arg_info*, apc_free_t);
+#endif
+
+/*
+ * The "free" functions work exactly like their "destroy" counterparts (see
+ * above) but also free the pointer to the data structure.
+ */
+static void my_free_zval_ptr(zval**, apc_free_t);
+static void my_free_function(zend_function*, apc_free_t);
+static void my_free_hashtable(HashTable*, ht_free_fun_t, apc_free_t);
+#ifdef ZEND_ENGINE_2
+static void my_free_property_info(zend_property_info* src, apc_free_t);
+static void my_free_arg_info_array(zend_arg_info*, uint, apc_free_t);
+static void my_free_arg_info(zend_arg_info*, apc_free_t);
+#endif
+
+/*
+ * The "fixup" functions need for ZEND_ENGINE_2
+ */
+#ifdef ZEND_ENGINE_2
+static void my_fixup_function( Bucket *p, zend_class_entry *src, zend_class_entry *dst );
+static void my_fixup_hashtable( HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst );
+/* my_fixup_function_for_execution is the same as my_fixup_function
+ * but named differently for clarity
+ */
+#define my_fixup_function_for_execution my_fixup_function
+
+#ifdef ZEND_ENGINE_2_2
+static void my_fixup_property_info( Bucket *p, zend_class_entry *src, zend_class_entry *dst );
+#define my_fixup_property_info_for_execution my_fixup_property_info
+#endif
+
+#endif
+
+/*
+ * These functions return "1" if the member/function is
+ * defined/overridden in the 'current' class and not inherited.
+ */
+static int my_check_copy_function(Bucket* src, va_list args);
+static int my_check_copy_default_property(Bucket* p, va_list args);
+#ifdef ZEND_ENGINE_2
+static int my_check_copy_property_info(Bucket* src, va_list args);
+static int my_check_copy_static_member(Bucket* src, va_list args);
+#endif
+
+/* }}} */
+
+/* {{{ check_op_array_integrity */
+#if 0
+static void check_op_array_integrity(zend_op_array* src)
+{
+ int i, j;
+
+ /* These sorts of checks really aren't particularly effective, but they
+ * can provide a welcome sanity check when debugging. Just don't enable
+ * for production use! */
+
+ assert(src->refcount != NULL);
+ assert(src->opcodes != NULL);
+ assert(src->last > 0);
+
+ for (i = 0; i < src->last; i++) {
+ zend_op* op = &src->opcodes[i];
+ znode* nodes[] = { &op->result, &op->op1, &op->op2 };
+ for (j = 0; j < 3; j++) {
+ assert(nodes[j]->op_type == IS_CONST ||
+ nodes[j]->op_type == IS_VAR ||
+ nodes[j]->op_type == IS_TMP_VAR ||
+ nodes[j]->op_type == IS_UNUSED);
+
+ if (nodes[j]->op_type == IS_CONST) {
+ int type = nodes[j]->u.constant.type;
+ assert(type == IS_RESOURCE ||
+ type == IS_BOOL ||
+ type == IS_LONG ||
+ type == IS_DOUBLE ||
+ type == IS_NULL ||
+ type == IS_CONSTANT ||
+ type == IS_STRING ||
+ type == FLAG_IS_BC ||
+ type == IS_ARRAY ||
+ type == IS_CONSTANT_ARRAY ||
+ type == IS_OBJECT);
+ }
+ }
+ }
+}
+#endif
+/* }}} */
+
+/* {{{ is_derived_class */
+static int is_derived_class(zend_op_array* op_array, const char* key, int key_size)
+{
+ int i;
+
+ /*
+ * Scan the op_array for execution-time class declarations of derived
+ * classes. If we find one whose key matches our current class key, we
+ * know the current class is a derived class.
+ *
+ * This check is exceedingly inefficient (fortunately it only has to occur
+ * once, when the source file is first compiled and cached), but the
+ * compiler should save this information for us -- definitely a candidate
+ * for a Zend Engine patch.
+ *
+ * XXX checking for derived classes provides a minimal (albeit measurable)
+ * speed up. It may not be worth the added complexity -- considere
+ * removing this optimization.
+ */
+
+ for (i = 0; i < op_array->last; i++) {
+ zend_op* op = &op_array->opcodes[i];
+
+#ifdef ZEND_ENGINE_2
+ if (op->opcode == ZEND_DECLARE_CLASS &&
+ op->extended_value == ZEND_DECLARE_INHERITED_CLASS)
+#else
+ if (op->opcode == ZEND_DECLARE_FUNCTION_OR_CLASS &&
+ op->extended_value == ZEND_DECLARE_INHERITED_CLASS)
+#endif
+ {
+ if (op->op1.u.constant.value.str.len == key_size &&
+ !memcmp(op->op1.u.constant.value.str.val, key, key_size))
+ {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+/* {{{ my_bitwise_copy_function */
+static zend_function* my_bitwise_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate)
+{
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zend_function*) allocate(sizeof(src[0])));
+ }
+
+ /* We only need to do a bitwise copy */
+ memcpy(dst, src, sizeof(src[0]));
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ my_copy_zval_ptr */
+static zval** my_copy_zval_ptr(zval** dst, const zval** src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+ zval* dst_new;
+
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zval**) allocate(sizeof(zval*)));
+ local_dst_alloc = 1;
+ }
+
+ if(!(dst[0] = (zval*) allocate(sizeof(zval)))) {
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+ }
+ if(!(dst_new = my_copy_zval(*dst, *src, allocate, deallocate))) {
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+ }
+ if(dst_new != *dst) {
+ deallocate(*dst);
+ *dst = dst_new;
+ }
+
+ Z_SET_REFCOUNT_PP(dst, Z_REFCOUNT_PP(src));
+ Z_SET_ISREF_TO_PP(dst, Z_ISREF_PP(src));
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ my_copy_zval */
+static zval* my_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ zval **tmp;
+ TSRMLS_FETCH();
+
+ assert(dst != NULL);
+ assert(src != NULL);
+
+ memcpy(dst, src, sizeof(src[0]));
+
+ switch (src->type & ~IS_CONSTANT_INDEX) {
+ case IS_RESOURCE:
+ case IS_BOOL:
+ case IS_LONG:
+ case IS_DOUBLE:
+ case IS_NULL:
+ break;
+
+ case IS_CONSTANT:
+ case IS_STRING:
+#ifndef ZEND_ENGINE_2
+ case FLAG_IS_BC:
+#endif
+ if (src->value.str.val) {
+ CHECK(dst->value.str.val = apc_xmemcpy(src->value.str.val,
+ src->value.str.len+1,
+ allocate));
+ }
+ break;
+
+ case IS_ARRAY:
+
+ if(APCG(copied_zvals)) {
+ if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) {
+ Z_ADDREF_PP(tmp);
+ return *tmp;
+ }
+
+ zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&dst, sizeof(zval*), NULL);
+ }
+ /* fall through */
+
+ case IS_CONSTANT_ARRAY:
+
+ CHECK(dst->value.ht =
+ my_copy_hashtable(NULL,
+ src->value.ht,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ allocate, deallocate));
+ break;
+
+ case IS_OBJECT:
+#ifndef ZEND_ENGINE_2
+ CHECK(dst->value.obj.ce =
+ my_copy_class_entry(NULL, src->value.obj.ce, allocate, deallocate));
+
+ if(!(dst->value.obj.properties = my_copy_hashtable(NULL,
+ src->value.obj.properties,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ allocate, deallocate))) {
+ my_destroy_class_entry(dst->value.obj.ce, deallocate);
+ return NULL;
+ }
+ break;
+#else
+ dst->type = IS_NULL;
+#endif
+ break;
+
+ default:
+ assert(0);
+ }
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ my_copy_znode */
+static znode* my_copy_znode(znode* dst, znode* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ assert(dst != NULL);
+ assert(src != NULL);
+
+ memcpy(dst, src, sizeof(src[0]));
+
+#ifdef IS_CV
+ assert(dst ->op_type == IS_CONST ||
+ dst ->op_type == IS_VAR ||
+ dst ->op_type == IS_CV ||
+ dst ->op_type == IS_TMP_VAR ||
+ dst ->op_type == IS_UNUSED);
+#else
+ assert(dst ->op_type == IS_CONST ||
+ dst ->op_type == IS_VAR ||
+ dst ->op_type == IS_TMP_VAR ||
+ dst ->op_type == IS_UNUSED);
+#endif
+
+ if (src->op_type == IS_CONST) {
+ if(!my_copy_zval(&dst->u.constant, &src->u.constant, allocate, deallocate)) {
+ return NULL;
+ }
+ }
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ my_copy_zend_op */
+static zend_op* my_copy_zend_op(zend_op* dst, zend_op* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ assert(dst != NULL);
+ assert(src != NULL);
+
+ memcpy(dst, src, sizeof(src[0]));
+
+ if( my_copy_znode(&dst->result, &src->result, allocate, deallocate) == NULL
+ || my_copy_znode(&dst->op1, &src->op1, allocate, deallocate) == NULL
+ || my_copy_znode(&dst->op2, &src->op2, allocate, deallocate) == NULL)
+ {
+ return NULL;
+ }
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ my_copy_function */
+static zend_function* my_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+ TSRMLS_FETCH();
+
+ assert(src != NULL);
+
+ if(!dst) local_dst_alloc = 1;
+ CHECK(dst = my_bitwise_copy_function(dst, src, allocate));
+
+ switch (src->type) {
+ case ZEND_INTERNAL_FUNCTION:
+ case ZEND_OVERLOADED_FUNCTION:
+ /* shallow copy because op_array is internal */
+ dst->op_array = src->op_array;
+ break;
+
+ case ZEND_USER_FUNCTION:
+ case ZEND_EVAL_CODE:
+ if(!apc_copy_op_array(&dst->op_array,
+ &src->op_array,
+ allocate, deallocate TSRMLS_CC)) {
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+#ifdef ZEND_ENGINE_2
+ /*
+ * op_array bitwise copying overwrites what ever you modified
+ * before apc_copy_op_array - which is why this code is outside
+ * my_bitwise_copy_function.
+ */
+
+ /* zend_do_inheritance will re-look this up, because the pointers
+ * in prototype are from a function table of another class. It just
+ * helps if that one is from EG(class_table).
+ */
+ dst->common.prototype = NULL;
+
+ /* once a method is marked as ZEND_ACC_IMPLEMENTED_ABSTRACT then you
+ * have to carry around a prototype. Thankfully zend_do_inheritance
+ * sets this properly as well
+ */
+ dst->common.fn_flags = src->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT);
+#endif
+
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ my_copy_function_entry */
+static zend_function_entry* my_copy_function_entry(zend_function_entry* dst, zend_function_entry* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zend_function_entry*) allocate(sizeof(src[0])));
+ local_dst_alloc = 1;
+ }
+
+ /* Start with a bitwise copy */
+ memcpy(dst, src, sizeof(src[0]));
+
+ dst->fname = NULL;
+#ifdef ZEND_ENGINE_2
+ dst->arg_info = NULL;
+#else
+ dst->func_arg_types = NULL;
+#endif
+
+ if (src->fname) {
+ if(!(dst->fname = apc_xstrdup(src->fname, allocate))) {
+ goto cleanup;
+ }
+ }
+
+#ifdef ZEND_ENGINE_2
+ if (src->arg_info) {
+ if(!(dst->arg_info = my_copy_arg_info_array(NULL,
+ src->arg_info,
+ src->num_args,
+ allocate,
+ deallocate))) {
+ goto cleanup;
+ }
+ }
+#else
+ if (src->func_arg_types) {
+ if(!(dst->func_arg_types = apc_xmemcpy(src->func_arg_types,
+ src->func_arg_types[0]+1,
+ allocate))) {
+ goto cleanup;
+ }
+ }
+#endif
+
+ return dst;
+
+cleanup:
+ if(dst->fname) deallocate(dst->fname);
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+}
+/* }}} */
+
+#ifdef ZEND_ENGINE_2
+/* {{{ my_copy_property_info */
+static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zend_property_info*) allocate(sizeof(*src)));
+ local_dst_alloc = 1;
+ }
+
+ /* Start with a bitwise copy */
+ memcpy(dst, src, sizeof(*src));
+
+ dst->name = NULL;
+#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
+ dst->doc_comment = NULL;
+#endif
+
+ if (src->name) {
+ /* private members are stored inside property_info as a mangled
+ * string of the form:
+ * \0<classname>\0<membername>\0
+ */
+ if(!(dst->name =
+ apc_xmemcpy(src->name, src->name_length+1, allocate))) {
+ goto cleanup;
+ }
+ }
+
+#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
+ if (src->doc_comment) {
+ if( !(dst->doc_comment =
+ apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
+ goto cleanup;
+ }
+ }
+#endif
+
+ return dst;
+
+cleanup:
+ if(dst->name) deallocate(dst->name);
+#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
+ if(dst->doc_comment) deallocate(dst->doc_comment);
+#endif
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ my_copy_property_info_for_execution */
+static zend_property_info* my_copy_property_info_for_execution(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zend_property_info*) allocate(sizeof(*src)));
+ local_dst_alloc = 1;
+ }
+
+ /* We need only a shallow copy */
+ memcpy(dst, src, sizeof(*src));
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ my_copy_arg_info_array */
+static zend_arg_info* my_copy_arg_info_array(zend_arg_info* dst, zend_arg_info* src, uint num_args, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+ int i = 0;
+
+
+ if (!dst) {
+ CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)*num_args));
+ local_dst_alloc = 1;
+ }
+
+ /* Start with a bitwise copy */
+ memcpy(dst, src, sizeof(*src)*num_args);
+
+ for(i=0; i < num_args; i++) {
+ if(!(my_copy_arg_info( &dst[i], &src[i], allocate, deallocate))) {
+ if(i) my_destroy_arg_info_array(dst, i-1, deallocate);
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+ }
+ }
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ my_copy_arg_info */
+static zend_arg_info* my_copy_arg_info(zend_arg_info* dst, zend_arg_info* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)));
+ local_dst_alloc = 1;
+ }
+
+ /* Start with a bitwise copy */
+ memcpy(dst, src, sizeof(*src));
+
+ dst->name = NULL;
+ dst->class_name = NULL;
+
+ if (src->name) {
+ if(!(dst->name =
+ apc_xmemcpy(src->name, src->name_len+1, allocate))) {
+ goto cleanup;
+ }
+ }
+
+ if (src->class_name) {
+ if(!(dst->class_name =
+ apc_xmemcpy(src->class_name, src->class_name_len+1, allocate))) {
+ goto cleanup;
+ }
+ }
+
+ return dst;
+
+cleanup:
+ if(dst->name) deallocate(dst->name);
+ if(dst->class_name) deallocate(dst->name);
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+}
+/* }}} */
+#endif
+
+/* {{{ my_copy_class_entry */
+static zend_class_entry* my_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+ int i = 0;
+
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zend_class_entry*) allocate(sizeof(*src)));
+ local_dst_alloc = 1;
+ }
+
+ /* Start with a bitwise copy */
+ memcpy(dst, src, sizeof(*src));
+
+ dst->name = NULL;
+ dst->builtin_functions = NULL;
+ memset(&dst->function_table, 0, sizeof(dst->function_table));
+ memset(&dst->default_properties, 0, sizeof(dst->default_properties));
+#ifndef ZEND_ENGINE_2
+ dst->refcount = NULL;
+#else
+ dst->static_members = NULL;
+ dst->doc_comment = NULL;
+ dst->filename = NULL;
+ memset(&dst->properties_info, 0, sizeof(dst->properties_info));
+ memset(&dst->constants_table, 0, sizeof(dst->constants_table));
+ memset(&dst->default_static_members, 0, sizeof(dst->default_static_members));
+#endif
+
+ if (src->name) {
+ if(!(dst->name = apc_xstrdup(src->name, allocate))) {
+ goto cleanup;
+ }
+ }
+
+#ifndef ZEND_ENGINE_2
+ if(!(dst->refcount = apc_xmemcpy(src->refcount,
+ sizeof(src->refcount[0]),
+ allocate))) {
+ goto cleanup;
+ }
+#endif
+
+ if(!(my_copy_hashtable_ex(&dst->function_table,
+ &src->function_table,
+ (ht_copy_fun_t) my_copy_function,
+ (ht_free_fun_t) my_free_function,
+ 0,
+ allocate, deallocate,
+ (ht_check_copy_fun_t) my_check_copy_function,
+ src))) {
+ goto cleanup;
+ }
+
+#ifdef ZEND_ENGINE_2
+
+ /* the interfaces are populated at runtime using ADD_INTERFACE */
+ dst->interfaces = NULL;
+
+ /* the current count includes inherited interfaces as well,
+ the real dynamic ones are the first <n> which are zero'd
+ out in zend_do_end_class_declaration */
+ for(i = 0 ; i < src->num_interfaces ; i++) {
+ if(src->interfaces[i])
+ {
+ dst->num_interfaces = i;
+ break;
+ }
+ }
+
+ /* these will either be set inside my_fixup_hashtable or
+ * they will be copied out from parent inside zend_do_inheritance
+ */
+ dst->constructor = NULL;
+ dst->destructor = NULL;
+ dst->clone = NULL;
+ dst->__get = NULL;
+ dst->__set = NULL;
+ dst->__unset = NULL;
+ dst->__isset = NULL;
+ dst->__call = NULL;
+#ifdef ZEND_ENGINE_2_2
+ dst->__tostring = NULL;
+#endif
+
+ /* unset function proxies */
+ dst->serialize_func = NULL;
+ dst->unserialize_func = NULL;
+
+ my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function, src, dst);
+#endif
+
+ if(!(my_copy_hashtable_ex(&dst->default_properties,
+ &src->default_properties,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ allocate,deallocate,
+ (ht_check_copy_fun_t) my_check_copy_default_property,
+ src))) {
+ goto cleanup;
+ }
+
+#ifdef ZEND_ENGINE_2
+
+ if(!(my_copy_hashtable_ex(&dst->properties_info,
+ &src->properties_info,
+ (ht_copy_fun_t) my_copy_property_info,
+ (ht_free_fun_t) my_free_property_info,
+ 0,
+ allocate, deallocate,
+ (ht_check_copy_fun_t) my_check_copy_property_info,
+ src))) {
+ goto cleanup;
+ }
+
+#ifdef ZEND_ENGINE_2_2
+ /* php5.2 introduced a scope attribute for property info */
+ my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst);
+#endif
+
+ if(!my_copy_hashtable_ex(&dst->default_static_members,
+ &src->default_static_members,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ allocate, deallocate,
+ (ht_check_copy_fun_t) my_check_copy_static_member,
+ src,
+ &src->default_static_members)) {
+ goto cleanup;
+ }
+ if(src->static_members != &src->default_static_members)
+ {
+ if(!(dst->static_members = my_copy_hashtable_ex(NULL,
+ src->static_members,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ allocate, deallocate,
+ (ht_check_copy_fun_t) my_check_copy_static_member,
+ src,
+ src->static_members))) {
+ goto cleanup;
+ }
+ }
+ else
+ {
+ dst->static_members = &dst->default_static_members;
+ }
+
+ if(!(my_copy_hashtable(&dst->constants_table,
+ &src->constants_table,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ allocate, deallocate))) {
+ goto cleanup;
+ }
+
+ if (src->doc_comment) {
+ if(!(dst->doc_comment =
+ apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
+ goto cleanup;
+ }
+ }
+#endif
+
+ if (src->builtin_functions) {
+ int i, n;
+
+ for (n = 0; src->type == ZEND_INTERNAL_CLASS && src->builtin_functions[n].fname != NULL; n++) {}
+
+ if(!(dst->builtin_functions =
+ (zend_function_entry*)
+ allocate((n + 1) * sizeof(zend_function_entry)))) {
+ goto cleanup;
+ }
+
+
+ for (i = 0; i < n; i++) {
+ if(!my_copy_function_entry(&dst->builtin_functions[i],
+ &src->builtin_functions[i],
+ allocate, deallocate)) {
+ int ii;
+
+ for(ii=i-1; i>=0; i--) my_destroy_function_entry(&dst->builtin_functions[ii], deallocate);
+ goto cleanup;
+ }
+ }
+ *(char**)&(dst->builtin_functions[n].fname) = NULL;
+ }
+
+#ifdef ZEND_ENGINE_2
+ if (src->filename) {
+ if(!(dst->filename = apc_xstrdup(src->filename, allocate))) {
+ goto cleanup;
+ }
+ }
+#endif
+
+ return dst;
+
+
+cleanup:
+ if(dst->name) deallocate(dst->name);
+#ifdef ZEND_ENGINE_2
+ if(dst->doc_comment) deallocate(dst->doc_comment);
+ if(dst->filename) deallocate(dst->filename);
+#else
+ if(dst->refcount) deallocate(dst->refcount);
+#endif
+
+ if(dst->builtin_functions) deallocate(dst->builtin_functions);
+ if(dst->function_table.arBuckets) my_destroy_hashtable(&dst->function_table, (ht_free_fun_t) my_free_function, deallocate);
+ if(dst->default_properties.arBuckets) my_destroy_hashtable(&dst->default_properties, (ht_free_fun_t) my_free_zval_ptr, deallocate);
+
+#ifdef ZEND_ENGINE_2
+ if(dst->properties_info.arBuckets) my_destroy_hashtable(&dst->properties_info, (ht_free_fun_t) my_free_property_info, deallocate);
+ if(dst->default_static_members.arBuckets)
+ {
+ my_destroy_hashtable(&dst->default_static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate);
+ }
+ if(dst->static_members && dst->static_members != &(dst->default_static_members))
+ {
+ my_destroy_hashtable(dst->static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate);
+ deallocate(dst->static_members);
+ }
+ if(dst->constants_table.arBuckets) my_destroy_hashtable(&dst->constants_table, (ht_free_fun_t) my_free_zval_ptr, deallocate);
+#endif
+ if(local_dst_alloc) deallocate(dst);
+
+ return NULL;
+}
+/* }}} */
+
+/* {{{ my_copy_hashtable */
+static HashTable* my_copy_hashtable_ex(HashTable* dst,
+ HashTable* src,
+ ht_copy_fun_t copy_fn,
+ ht_free_fun_t free_fn,
+ int holds_ptrs,
+ apc_malloc_t allocate,
+ apc_free_t deallocate,
+ ht_check_copy_fun_t check_fn,
+ ...)
+{
+ Bucket* curr = NULL;
+ Bucket* prev = NULL;
+ Bucket* newp = NULL;
+ int first = 1;
+ int local_dst_alloc = 0;
+ int index = 0;
+
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (HashTable*) allocate(sizeof(src[0])));
+ local_dst_alloc = 1;
+ }
+
+ memcpy(dst, src, sizeof(src[0]));
+
+ /* allocate buckets for the new hashtable */
+ if(!(dst->arBuckets = allocate(dst->nTableSize * sizeof(Bucket*)))) {
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+ }
+
+ memset(dst->arBuckets, 0, dst->nTableSize * sizeof(Bucket*));
+ dst->pInternalPointer = NULL;
+ dst->pListHead = NULL;
+
+ for (curr = src->pListHead; curr != NULL; curr = curr->pListNext) {
+ int n = curr->h % dst->nTableSize;
+
+ if(check_fn) {
+ va_list args;
+ va_start(args, check_fn);
+
+ /* Call the check_fn to see if the current bucket
+ * needs to be copied out
+ */
+ if(!check_fn(curr, args)) {
+ dst->nNumOfElements--;
+ continue;
+ }
+
+ va_end(args);
+ }
+
+ /* create a copy of the bucket 'curr' */
+ if(!(newp =
+ (Bucket*) apc_xmemcpy(curr,
+ sizeof(Bucket) + curr->nKeyLength - 1,
+ allocate))) {
+ goto cleanup;
+ }
+
+ /* insert 'newp' into the linked list at its hashed index */
+ if (dst->arBuckets[n]) {
+ newp->pNext = dst->arBuckets[n];
+ newp->pLast = NULL;
+ newp->pNext->pLast = newp;
+ }
+ else {
+ newp->pNext = newp->pLast = NULL;
+ }
+
+ dst->arBuckets[n] = newp;
+
+ /* copy the bucket data using our 'copy_fn' callback function */
+ if(!(newp->pData = copy_fn(NULL, curr->pData, allocate, deallocate))) {
+ goto cleanup;
+ }
+
+ if (holds_ptrs) {
+ memcpy(&newp->pDataPtr, newp->pData, sizeof(void*));
+ }
+ else {
+ newp->pDataPtr = NULL;
+ }
+
+ /* insert 'newp' into the table-thread linked list */
+ newp->pListLast = prev;
+ newp->pListNext = NULL;
+
+ if (prev) {
+ prev->pListNext = newp;
+ }
+
+ if (first) {
+ dst->pListHead = newp;
+ first = 0;
+ }
+
+ prev = newp;
+ }
+
+ dst->pListTail = newp;
+
+ return dst;
+
+ cleanup:
+ for(index = 0; index < dst->nTableSize; index++)
+ {
+ curr = dst->arBuckets[index];
+ while(curr != NULL)
+ {
+ Bucket * tmp = curr;
+ if(curr->pData && free_fn)
+ {
+ free_fn(curr->pData, deallocate);
+ }
+ curr = curr->pNext;
+ deallocate(tmp);
+ }
+ }
+ deallocate(dst->arBuckets);
+ if(local_dst_alloc) deallocate(dst);
+ else dst->arBuckets = NULL;
+
+ return NULL;
+}
+/* }}} */
+
+/* {{{ my_copy_static_variables */
+static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ if (src->static_variables == NULL) {
+ return NULL;
+ }
+
+ return my_copy_hashtable(NULL,
+ src->static_variables,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ allocate, deallocate);
+}
+/* }}} */
+
+/* {{{ apc_copy_zval */
+zval* apc_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ int local_dst_alloc = 0;
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zval*) allocate(sizeof(zval)));
+ local_dst_alloc = 1;
+ }
+
+ dst = my_copy_zval(dst, src, allocate, deallocate);
+ if(!dst) {
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+ }
+ return dst;
+}
+/* }}} */
+
+#ifdef ZEND_ENGINE_2
+/* {{{ apc_fixup_op_array_jumps */
+static void apc_fixup_op_array_jumps(zend_op_array *dst, zend_op_array *src )
+{
+ int i;
+
+ for (i=0; i < dst->last; ++i) {
+ zend_op *zo = &(dst->opcodes[i]);
+ /*convert opline number to jump address*/
+ switch (zo->opcode) {
+ case ZEND_JMP:
+ /*Note: if src->opcodes != dst->opcodes then we need to the opline according to src*/
+ zo->op1.u.jmp_addr = dst->opcodes + (zo->op1.u.jmp_addr - src->opcodes);
+ break;
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ zo->op2.u.jmp_addr = dst->opcodes + (zo->op2.u.jmp_addr - src->opcodes);
+ break;
+ default:
+ break;
+ }
+ }
+}
+/* }}} */
+#endif
+
+/* {{{ apc_copy_op_array */
+zend_op_array* apc_copy_op_array(zend_op_array* dst, zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC)
+{
+ int i;
+ int local_dst_alloc = 0;
+ apc_fileinfo_t fileinfo;
+ char canon_path[MAXPATHLEN];
+ char *fullpath = NULL;
+#ifdef ZEND_ENGINE_2
+ apc_opflags_t * flags = NULL;
+#endif
+
+ assert(src != NULL);
+
+ if (!dst) {
+ CHECK(dst = (zend_op_array*) allocate(sizeof(src[0])));
+ local_dst_alloc = 1;
+ }
+
+ if(APCG(apc_optimize_function)) {
+ APCG(apc_optimize_function)(src TSRMLS_CC);
+ }
+
+ /* start with a bitwise copy of the array */
+ memcpy(dst, src, sizeof(src[0]));
+
+ dst->function_name = NULL;
+ dst->filename = NULL;
+ dst->refcount = NULL;
+ dst->opcodes = NULL;
+ dst->brk_cont_array = NULL;
+ dst->static_variables = NULL;
+#ifdef ZEND_ENGINE_2
+ dst->try_catch_array = NULL;
+ dst->arg_info = NULL;
+ dst->doc_comment = NULL;
+#else
+ dst->arg_types = NULL;
+#endif
+#ifdef ZEND_ENGINE_2_1
+ dst->vars = NULL;
+#endif
+
+ /* copy the arg types array (if set) */
+#ifdef ZEND_ENGINE_2
+ if (src->arg_info) {
+ if(!(dst->arg_info = my_copy_arg_info_array(NULL,
+ src->arg_info,
+ src->num_args,
+ allocate,
+ deallocate))) {
+ goto cleanup;
+ }
+ }
+#else
+ if (src->arg_types) {
+ if(!(dst->arg_types = apc_xmemcpy(src->arg_types,
+ sizeof(src->arg_types[0]) * (src->arg_types[0]+1),
+ allocate))) {
+ goto cleanup;
+ }
+ }
+#endif
+
+ if (src->function_name) {
+ if(!(dst->function_name = apc_xstrdup(src->function_name, allocate))) {
+ goto cleanup;
+ }
+ }
+ if (src->filename) {
+ if(!(dst->filename = apc_xstrdup(src->filename, allocate))) {
+ goto cleanup;
+ }
+ }
+
+ if(!(dst->refcount = apc_xmemcpy(src->refcount,
+ sizeof(src->refcount[0]),
+ allocate))) {
+ goto cleanup;
+ }
+
+ /* deep-copy the opcodes */
+ if(!(dst->opcodes = (zend_op*) allocate(sizeof(zend_op) * src->last))) {
+ goto cleanup;
+ }
+
+#ifdef ZEND_ENGINE_2
+ if(APCG(reserved_offset) != -1) {
+ /* Insanity alert: the void* pointer is cast into an apc_opflags_t
+ * struct. apc_zend_init() checks to ensure that it fits in a void* */
+ flags = (apc_opflags_t*) & (dst->reserved[APCG(reserved_offset)]);
+ memset(flags, 0, sizeof(apc_opflags_t));
+ /* assert(sizeof(apc_opflags_t) < sizeof(dst->reserved)); */
+ }
+#endif
+
+ for (i = 0; i < src->last; i++) {
+#ifdef ZEND_ENGINE_2
+ zend_op *zo = &(src->opcodes[i]);
+ /* a lot of files are merely constant arrays with no jumps */
+ switch (zo->opcode) {
+ case ZEND_JMP:
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ if(flags != NULL) {
+ flags->has_jumps = 1;
+ }
+ break;
+#ifdef ZEND_ENGINE_2
+ /* auto_globals_jit was not in php-4.3.* */
+ case ZEND_FETCH_R:
+ case ZEND_FETCH_W:
+ case ZEND_FETCH_IS:
+ case ZEND_FETCH_FUNC_ARG:
+ if(PG(auto_globals_jit) && flags != NULL)
+ {
+ /* The fetch is only required if auto_globals_jit=1 */
+ if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL &&
+ zo->op1.op_type == IS_CONST &&
+ zo->op1.u.constant.type == IS_STRING) {
+ znode * varname = &zo->op1;
+ if (varname->u.constant.value.str.val[0] == '_') {
+#define SET_IF_AUTOGLOBAL(member) \
+ if(!strcmp(varname->u.constant.value.str.val, #member)) \
+ flags->member = 1 /* no ';' here */
+ SET_IF_AUTOGLOBAL(_GET);
+ else SET_IF_AUTOGLOBAL(_POST);
+ else SET_IF_AUTOGLOBAL(_COOKIE);
+ else SET_IF_AUTOGLOBAL(_SERVER);
+ else SET_IF_AUTOGLOBAL(_ENV);
+ else SET_IF_AUTOGLOBAL(_FILES);
+ else SET_IF_AUTOGLOBAL(_REQUEST);
+ else if(zend_is_auto_global(
+ varname->u.constant.value.str.val,
+ varname->u.constant.value.str.len
+ TSRMLS_CC))
+ {
+ flags->unknown_global = 1;
+ }
+ }
+ }
+ }
+ break;
+#endif
+ case ZEND_RECV_INIT:
+ if(zo->op2.op_type == IS_CONST &&
+ zo->op2.u.constant.type == IS_CONSTANT_ARRAY) {
+ if(flags != NULL) {
+ flags->deep_copy = 1;
+ }
+ }
+ break;
+ default:
+ if((zo->op1.op_type == IS_CONST &&
+ zo->op1.u.constant.type == IS_CONSTANT_ARRAY) ||
+ (zo->op2.op_type == IS_CONST &&
+ zo->op2.u.constant.type == IS_CONSTANT_ARRAY)) {
+ if(flags != NULL) {
+ flags->deep_copy = 1;
+ }
+ }
+ break;
+ }
+#endif
+ if(!(my_copy_zend_op(dst->opcodes+i, src->opcodes+i, allocate, deallocate))) {
+ int ii;
+ for(ii = i-1; ii>=0; ii--) {
+ my_destroy_zend_op(dst->opcodes+ii, deallocate);
+ }
+ goto cleanup;
+ }
+#ifdef ZEND_ENGINE_2
+/* This code breaks apc's rule#1 - cache what you compile */
+ if(APCG(fpstat)==0) {
+ if((zo->opcode == ZEND_INCLUDE_OR_EVAL) &&
+ (zo->op1.op_type == IS_CONST && zo->op1.u.constant.type == IS_STRING)) {
+ /* constant includes */
+ if(!IS_ABSOLUTE_PATH(Z_STRVAL_P(&zo->op1.u.constant),Z_STRLEN_P(&zo->op1.u.constant))) {
+ if (apc_search_paths(Z_STRVAL_P(&zo->op1.u.constant), PG(include_path), &fileinfo) == 0) {
+ if((fullpath = realpath(fileinfo.fullpath, canon_path))) {
+ /* everything has to go through a realpath() */
+ zend_op *dzo = &(dst->opcodes[i]);
+ deallocate(dzo->op1.u.constant.value.str.val);
+ dzo->op1.u.constant.value.str.len = strlen(fullpath);
+ dzo->op1.u.constant.value.str.val = apc_xstrdup(fullpath, allocate);
+ }
+ }
+ }
+ }
+ }
+#endif
+ }
+
+#ifdef ZEND_ENGINE_2
+ if(flags == NULL || flags->has_jumps) {
+ apc_fixup_op_array_jumps(dst,src);
+ }
+#endif
+
+ /* copy the break-continue array */
+ if (src->brk_cont_array) {
+ if(!(dst->brk_cont_array =
+ apc_xmemcpy(src->brk_cont_array,
+ sizeof(src->brk_cont_array[0]) * src->last_brk_cont,
+ allocate))) {
+ goto cleanup_opcodes;
+ }
+ }
+
+ /* copy the table of static variables */
+ if (src->static_variables) {
+ if(!(dst->static_variables = my_copy_static_variables(src, allocate, deallocate))) {
+ goto cleanup_opcodes;
+ }
+ }
+
+#ifdef ZEND_ENGINE_2
+ if (src->try_catch_array) {
+ if(!(dst->try_catch_array =
+ apc_xmemcpy(src->try_catch_array,
+ sizeof(src->try_catch_array[0]) * src->last_try_catch,
+ allocate))) {
+ goto cleanup_opcodes;
+ }
+ }
+#endif
+
+#ifdef ZEND_ENGINE_2_1 /* PHP 5.1 */
+ if (src->vars) {
+ if(!(dst->vars = apc_xmemcpy(src->vars,
+ sizeof(src->vars[0]) * src->last_var,
+ allocate))) {
+ goto cleanup_opcodes;
+ }
+
+ for(i = 0; i < src->last_var; i++) dst->vars[i].name = NULL;
+
+ for(i = 0; i < src->last_var; i++) {
+ if(!(dst->vars[i].name = apc_xmemcpy(src->vars[i].name,
+ src->vars[i].name_len + 1,
+ allocate))) {
+ dst->last_var = i;
+ goto cleanup_opcodes;
+ }
+ }
+ }
+#endif
+
+#ifdef ZEND_ENGINE_2
+ if (src->doc_comment) {
+ if (!(dst->doc_comment
+ = apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
+ goto cleanup_opcodes;
+ }
+ }
+#endif
+
+ return dst;
+
+cleanup_opcodes:
+ if(dst->opcodes) {
+ for(i=0; i < src->last; i++) my_destroy_zend_op(dst->opcodes+i, deallocate);
+ }
+cleanup:
+ if(dst->function_name) deallocate(dst->function_name);
+ if(dst->refcount) deallocate(dst->refcount);
+ if(dst->filename) deallocate(dst->filename);
+#ifdef ZEND_ENGINE_2
+ if(dst->arg_info) my_free_arg_info_array(dst->arg_info, dst->num_args, deallocate);
+ if(dst->try_catch_array) deallocate(dst->try_catch_array);
+ if(dst->doc_comment) deallocate(dst->doc_comment);
+#else
+ if(dst->arg_types) deallocate(dst->arg_types);
+#endif
+ if(dst->opcodes) deallocate(dst->opcodes);
+ if(dst->brk_cont_array) deallocate(dst->brk_cont_array);
+ if(dst->static_variables) my_free_hashtable(dst->static_variables, (ht_free_fun_t)my_free_zval_ptr, (apc_free_t)deallocate);
+#ifdef ZEND_ENGINE_2_1
+ if (dst->vars) {
+ for(i=0; i < dst->last_var; i++) {
+ if(dst->vars[i].name) deallocate(dst->vars[i].name);
+ }
+ deallocate(dst->vars);
+ }
+#endif
+ if(local_dst_alloc) deallocate(dst);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ apc_copy_new_functions */
+apc_function_t* apc_copy_new_functions(int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC)
+{
+ apc_function_t* array;
+ int new_count; /* number of new functions in table */
+ int i;
+
+ new_count = zend_hash_num_elements(CG(function_table)) - old_count;
+ assert(new_count >= 0);
+
+ CHECK(array =
+ (apc_function_t*)
+ allocate(sizeof(apc_function_t) * (new_count+1)));
+
+ if (new_count == 0) {
+ array[0].function = NULL;
+ return array;
+ }
+
+ /* Skip the first `old_count` functions in the table */
+ zend_hash_internal_pointer_reset(CG(function_table));
+ for (i = 0; i < old_count; i++) {
+ zend_hash_move_forward(CG(function_table));
+ }
+
+ /* Add the next `new_count` functions to our array */
+ for (i = 0; i < new_count; i++) {
+ char* key;
+ uint key_size;
+ zend_function* fun;
+
+ zend_hash_get_current_key_ex(CG(function_table),
+ &key,
+ &key_size,
+ NULL,
+ 0,
+ NULL);
+
+ zend_hash_get_current_data(CG(function_table), (void**) &fun);
+
+ if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) {
+ int ii;
+ for(ii=i-1; ii>=0; ii--) {
+ deallocate(array[ii].name);
+ my_free_function(array[ii].function, deallocate);
+ }
+ deallocate(array);
+ return NULL;
+ }
+ array[i].name_len = (int) key_size-1;
+ if(!(array[i].function = my_copy_function(NULL, fun, allocate, deallocate))) {
+ int ii;
+ deallocate(array[i].name);
+ for(ii=i-1; ii>=0; ii--) {
+ deallocate(array[ii].name);
+ my_free_function(array[ii].function, deallocate);
+ }
+ deallocate(array);
+ return NULL;
+ }
+ zend_hash_move_forward(CG(function_table));
+ }
+
+ array[i].function = NULL;
+ return array;
+}
+/* }}} */
+
+/* {{{ apc_copy_new_classes */
+apc_class_t* apc_copy_new_classes(zend_op_array* op_array, int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC)
+{
+ apc_class_t* array;
+ int new_count; /* number of new classes in table */
+ int i;
+
+ new_count = zend_hash_num_elements(CG(class_table)) - old_count;
+ assert(new_count >= 0);
+
+ CHECK(array =
+ (apc_class_t*)
+ allocate(sizeof(apc_class_t)*(new_count+1)));
+
+ if (new_count == 0) {
+ array[0].class_entry = NULL;
+ return array;
+ }
+
+ /* Skip the first `old_count` classes in the table */
+ zend_hash_internal_pointer_reset(CG(class_table));
+ for (i = 0; i < old_count; i++) {
+ zend_hash_move_forward(CG(class_table));
+ }
+
+ /* Add the next `new_count` classes to our array */
+ for (i = 0; i < new_count; i++) {
+ char* key;
+ uint key_size;
+ zend_class_entry* elem = NULL;
+
+ array[i].class_entry = NULL;
+
+ zend_hash_get_current_key_ex(CG(class_table),
+ &key,
+ &key_size,
+ NULL,
+ 0,
+ NULL);
+
+ zend_hash_get_current_data(CG(class_table), (void**) &elem);
+
+
+#ifdef ZEND_ENGINE_2
+ elem = *((zend_class_entry**)elem);
+#endif
+
+ if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) {
+ int ii;
+
+ for(ii=i-1; ii>=0; ii--) {
+ deallocate(array[ii].name);
+ my_destroy_class_entry(array[ii].class_entry, deallocate);
+ deallocate(array[ii].class_entry);
+ }
+ deallocate(array);
+ return NULL;
+ }
+ array[i].name_len = (int) key_size-1;
+ if(!(array[i].class_entry = my_copy_class_entry(NULL, elem, allocate, deallocate))) {
+ int ii;
+
+ deallocate(array[i].name);
+ for(ii=i-1; ii>=0; ii--) {
+ deallocate(array[ii].name);
+ my_destroy_class_entry(array[ii].class_entry, deallocate);
+ deallocate(array[ii].class_entry);
+ }
+ deallocate(array);
+ return NULL;
+ }
+
+ /*
+ * If the class has a pointer to its parent class, save the parent
+ * name so that we can enable compile-time inheritance when we reload
+ * the child class; otherwise, set the parent name to null and scan
+ * the op_array to determine if this class inherits from some base
+ * class at execution-time.
+ */
+
+ if (elem->parent) {
+ if(!(array[i].parent_name =
+ apc_xstrdup(elem->parent->name, allocate))) {
+ int ii;
+
+ for(ii=i; ii>=0; ii--) {
+ deallocate(array[ii].name);
+ my_destroy_class_entry(array[ii].class_entry, deallocate);
+ deallocate(array[ii].class_entry);
+ if(ii==i) continue;
+ if(array[ii].parent_name) deallocate(array[ii].parent_name);
+ }
+ deallocate(array);
+ return NULL;
+ }
+ array[i].is_derived = 1;
+ }
+ else {
+ array[i].parent_name = NULL;
+ array[i].is_derived = is_derived_class(op_array, key, key_size);
+ }
+
+ zend_hash_move_forward(CG(class_table));
+ }
+
+ array[i].class_entry = NULL;
+ return array;
+}
+/* }}} */
+
+/* {{{ my_destroy_zval_ptr */
+static void my_destroy_zval_ptr(zval** src, apc_free_t deallocate)
+{
+ assert(src != NULL);
+ if(my_destroy_zval(src[0], deallocate) == SUCCESS) {
+ deallocate(src[0]);
+ }
+}
+/* }}} */
+
+/* {{{ my_destroy_zval */
+static int my_destroy_zval(zval* src, apc_free_t deallocate)
+{
+ zval **tmp;
+ TSRMLS_FETCH();
+
+ switch (src->type & ~IS_CONSTANT_INDEX) {
+ case IS_RESOURCE:
+ case IS_BOOL:
+ case IS_LONG:
+ case IS_DOUBLE:
+ case IS_NULL:
+ break;
+
+ case IS_CONSTANT:
+ case IS_STRING:
+#ifndef ZEND_ENGINE_2
+ case FLAG_IS_BC:
+#endif
+ deallocate(src->value.str.val);
+ break;
+
+ case IS_ARRAY:
+
+ /* Maintain a list of zvals we've copied to properly handle recursive structures */
+ if(APCG(copied_zvals)) {
+ if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) {
+ Z_DELREF_PP(tmp);
+ return FAILURE;
+ }
+ zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&src, sizeof(zval*), NULL);
+ }
+ /* fall through */
+
+ case IS_CONSTANT_ARRAY:
+ my_free_hashtable(src->value.ht,
+ (ht_free_fun_t) my_free_zval_ptr,
+ deallocate);
+ break;
+
+ case IS_OBJECT:
+#ifndef ZEND_ENGINE_2
+ my_destroy_class_entry(src->value.obj.ce, deallocate);
+ deallocate(src->value.obj.ce);
+ my_free_hashtable(src->value.obj.properties,
+ (ht_free_fun_t) my_free_zval_ptr,
+ deallocate);
+#endif
+ break;
+
+ default:
+ assert(0);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ my_destroy_znode */
+static void my_destroy_znode(znode* src, apc_free_t deallocate)
+{
+ if (src->op_type == IS_CONST) {
+ my_destroy_zval(&src->u.constant, deallocate);
+ }
+}
+/* }}} */
+
+/* {{{ my_destroy_zend_op */
+static void my_destroy_zend_op(zend_op* src, apc_free_t deallocate)
+{
+ my_destroy_znode(&src->result, deallocate);
+ my_destroy_znode(&src->op1, deallocate);
+ my_destroy_znode(&src->op2, deallocate);
+}
+/* }}} */
+
+/* {{{ my_destroy_function */
+static void my_destroy_function(zend_function* src, apc_free_t deallocate)
+{
+ assert(src != NULL);
+
+ switch (src->type) {
+ case ZEND_INTERNAL_FUNCTION:
+ case ZEND_OVERLOADED_FUNCTION:
+ break;
+
+ case ZEND_USER_FUNCTION:
+ case ZEND_EVAL_CODE:
+ my_destroy_op_array(&src->op_array, deallocate);
+ break;
+
+ default:
+ assert(0);
+ }
+}
+/* }}} */
+
+/* {{{ my_destroy_function_entry */
+static void my_destroy_function_entry(zend_function_entry* src, apc_free_t deallocate)
+{
+ assert(src != NULL);
+
+ deallocate(src->fname);
+#ifdef ZEND_ENGINE_2
+ if (src->arg_info) {
+ my_free_arg_info_array(src->arg_info, src->num_args, deallocate);
+ }
+#else
+ if (src->func_arg_types) {
+ deallocate(src->func_arg_types);
+ }
+#endif
+}
+/* }}} */
+
+#ifdef ZEND_ENGINE_2
+/* {{{ my_destroy_property_info*/
+static void my_destroy_property_info(zend_property_info* src, apc_free_t deallocate)
+{
+ assert(src != NULL);
+
+ deallocate(src->name);
+#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
+ if(src->doc_comment) deallocate(src->doc_comment);
+#endif
+}
+/* }}} */
+
+/* {{{ my_destroy_arg_info_array */
+static void my_destroy_arg_info_array(zend_arg_info* src, uint num_args, apc_free_t deallocate)
+{
+ int i = 0;
+
+ assert(src != NULL);
+
+ for(i=0; i < num_args; i++) {
+ my_destroy_arg_info(&src[i], deallocate);
+ }
+}
+/* }}} */
+
+/* {{{ my_destroy_arg_info */
+static void my_destroy_arg_info(zend_arg_info* src, apc_free_t deallocate)
+{
+ assert(src != NULL);
+
+ deallocate(src->name);
+ deallocate(src->class_name);
+}
+/* }}} */
+#endif
+
+/* {{{ my_destroy_class_entry */
+static void my_destroy_class_entry(zend_class_entry* src, apc_free_t deallocate)
+{
+ uint i;
+
+ assert(src != NULL);
+
+ deallocate(src->name);
+#ifndef ZEND_ENGINE_2
+ deallocate(src->refcount);
+#else
+ if(src->doc_comment) deallocate(src->doc_comment);
+ if(src->filename) deallocate(src->filename);
+#endif
+
+ my_destroy_hashtable(&src->function_table,
+ (ht_free_fun_t) my_free_function,
+ deallocate);
+
+ my_destroy_hashtable(&src->default_properties,
+ (ht_free_fun_t) my_free_zval_ptr,
+ deallocate);
+
+#ifdef ZEND_ENGINE_2
+ my_destroy_hashtable(&src->properties_info,
+ (ht_free_fun_t) my_free_property_info,
+ deallocate);
+ if(src->static_members)
+ {
+ my_destroy_hashtable(src->static_members,
+ (ht_free_fun_t) my_free_zval_ptr,
+ deallocate);
+ if(src->static_members != &(src->default_static_members))
+ {
+ deallocate(src->static_members);
+ }
+ }
+
+ my_destroy_hashtable(&src->constants_table,
+ (ht_free_fun_t) my_free_zval_ptr,
+ deallocate);
+#endif
+
+ if (src->builtin_functions) {
+ for (i = 0; src->builtin_functions[i].fname != NULL; i++) {
+ my_destroy_function_entry(&src->builtin_functions[i], deallocate);
+ }
+ deallocate(src->builtin_functions);
+ }
+}
+/* }}} */
+
+/* {{{ my_destroy_hashtable */
+static void my_destroy_hashtable(HashTable* src, ht_free_fun_t free_fn, apc_free_t deallocate)
+{
+ int i;
+
+ assert(src != NULL);
+
+ for (i = 0; i < src->nTableSize; i++) {
+ Bucket* p = src->arBuckets[i];
+ while (p != NULL) {
+ Bucket* q = p;
+ p = p->pNext;
+ free_fn(q->pData, deallocate);
+ deallocate(q);
+ }
+ }
+
+ deallocate(src->arBuckets);
+}
+/* }}} */
+
+/* {{{ my_destroy_op_array */
+static void my_destroy_op_array(zend_op_array* src, apc_free_t deallocate)
+{
+ int i;
+
+ assert(src != NULL);
+
+#ifdef ZEND_ENGINE_2
+ if (src->arg_info) {
+ my_free_arg_info_array(src->arg_info, src->num_args, deallocate);
+ }
+#else
+ if (src->arg_types) {
+ deallocate(src->arg_types);
+ }
+#endif
+
+ deallocate(src->function_name);
+ deallocate(src->filename);
+ deallocate(src->refcount);
+
+ for (i = 0; i < src->last; i++) {
+ my_destroy_zend_op(src->opcodes + i, deallocate);
+ }
+ deallocate(src->opcodes);
+
+ if (src->brk_cont_array) {
+ deallocate(src->brk_cont_array);
+ }
+
+ if (src->static_variables) {
+ my_free_hashtable(src->static_variables,
+ (ht_free_fun_t) my_free_zval_ptr,
+ deallocate);
+ }
+
+#ifdef ZEND_ENGINE_2_1
+ if (src->vars) {
+ for(i=0; i < src->last_var; i++) {
+ if(src->vars[i].name) deallocate(src->vars[i].name);
+ }
+ deallocate(src->vars);
+ }
+#endif
+#ifdef ZEND_ENGINE_2
+ if(src->try_catch_array) {
+ deallocate(src->try_catch_array);
+ }
+ if (src->doc_comment) {
+ deallocate(src->doc_comment);
+ }
+#endif
+}
+/* }}} */
+
+/* {{{ my_free_zval_ptr */
+static void my_free_zval_ptr(zval** src, apc_free_t deallocate)
+{
+ my_destroy_zval_ptr(src, deallocate);
+ deallocate(src);
+}
+/* }}} */
+
+#ifdef ZEND_ENGINE_2
+/* {{{ my_free_property_info */
+static void my_free_property_info(zend_property_info* src, apc_free_t deallocate)
+{
+ my_destroy_property_info(src, deallocate);
+ deallocate(src);
+}
+/* }}} */
+
+/* {{{ my_free_arg_info_array */
+static void my_free_arg_info_array(zend_arg_info* src, uint num_args, apc_free_t deallocate)
+{
+ my_destroy_arg_info_array(src, num_args, deallocate);
+ deallocate(src);
+}
+/* }}} */
+
+/* {{{ my_free_arg_info */
+static void my_free_arg_info(zend_arg_info* src, apc_free_t deallocate)
+{
+ my_destroy_arg_info(src, deallocate);
+ deallocate(src);
+}
+/* }}} */
+#endif
+
+/* {{{ my_free_function */
+static void my_free_function(zend_function* src, apc_free_t deallocate)
+{
+ my_destroy_function(src, deallocate);
+ deallocate(src);
+}
+/* }}} */
+
+/* {{{ my_free_hashtable */
+static void my_free_hashtable(HashTable* src, ht_free_fun_t free_fn, apc_free_t deallocate)
+{
+ my_destroy_hashtable(src, free_fn, deallocate);
+ deallocate(src);
+}
+/* }}} */
+
+/* {{{ apc_free_op_array */
+void apc_free_op_array(zend_op_array* src, apc_free_t deallocate)
+{
+ if (src != NULL) {
+ my_destroy_op_array(src, deallocate);
+ deallocate(src);
+ }
+}
+/* }}} */
+
+/* {{{ apc_free_functions */
+void apc_free_functions(apc_function_t* src, apc_free_t deallocate)
+{
+ int i;
+
+ if (src != NULL) {
+ for (i = 0; src[i].function != NULL; i++) {
+ deallocate(src[i].name);
+ my_destroy_function(src[i].function, deallocate);
+ deallocate(src[i].function);
+ }
+ deallocate(src);
+ }
+}
+/* }}} */
+
+/* {{{ apc_free_classes */
+void apc_free_classes(apc_class_t* src, apc_free_t deallocate)
+{
+ int i;
+
+ if (src != NULL) {
+ for (i = 0; src[i].class_entry != NULL; i++) {
+ deallocate(src[i].name);
+ deallocate(src[i].parent_name);
+ my_destroy_class_entry(src[i].class_entry, deallocate);
+ deallocate(src[i].class_entry);
+ }
+ deallocate(src);
+ }
+}
+/* }}} */
+
+/* {{{ apc_free_zval */
+void apc_free_zval(zval* src, apc_free_t deallocate)
+{
+ if (src != NULL) {
+ if(my_destroy_zval(src, deallocate) == SUCCESS) {
+ deallocate(src);
+ }
+ }
+}
+/* }}} */
+
+
+/* Used only by my_prepare_op_array_for_execution */
+#define APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION() \
+ /* The fetch is only required if auto_globals_jit=1 */ \
+ if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL && \
+ zo->op1.op_type == IS_CONST && \
+ zo->op1.u.constant.type == IS_STRING && \
+ zo->op1.u.constant.value.str.val[0] == '_') { \
+ \
+ znode* varname = &zo->op1; \
+ (void)zend_is_auto_global(varname->u.constant.value.str.val, \
+ varname->u.constant.value.str.len \
+ TSRMLS_CC); \
+ } \
+
+/* {{{ my_prepare_op_array_for_execution */
+static int my_prepare_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC)
+{
+ /* combine my_fetch_global_vars and my_copy_data_exceptions.
+ * - Pre-fetch superglobals which would've been pre-fetched in parse phase.
+ * - If the opcode stream contain mutable data, ensure a copy.
+ * - Fixup array jumps in the same loop.
+ */
+ int i=src->last;
+ zend_op *zo;
+ zend_op *dzo;
+#ifdef ZEND_ENGINE_2
+ apc_opflags_t * flags = APCG(reserved_offset) != -1 ?
+ (apc_opflags_t*) & (src->reserved[APCG(reserved_offset)]) : NULL;
+ int needcopy = flags ? flags->deep_copy : 1;
+ /* auto_globals_jit was not in php4 */
+ int do_prepare_fetch_global = PG(auto_globals_jit) && (flags == NULL || flags->unknown_global);
+
+#define FETCH_AUTOGLOBAL(member) do { \
+ if(flags && flags->member == 1) { \
+ zend_is_auto_global(#member,\
+ (sizeof(#member) - 1)\
+ TSRMLS_CC);\
+ } \
+}while(0);
+
+ FETCH_AUTOGLOBAL(_GET);
+ FETCH_AUTOGLOBAL(_POST);
+ FETCH_AUTOGLOBAL(_COOKIE);
+ FETCH_AUTOGLOBAL(_SERVER);
+ FETCH_AUTOGLOBAL(_ENV);
+ FETCH_AUTOGLOBAL(_FILES);
+ FETCH_AUTOGLOBAL(_REQUEST);
+
+#else
+ int needcopy = 0;
+ int do_prepare_fetch_global = 0;
+ int j = 0;
+
+ for(j = 0; j < src->last; j++) {
+ zo = &src->opcodes[j];
+
+ if( ((zo->op1.op_type == IS_CONST &&
+ zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) ||
+ ((zo->op2.op_type == IS_CONST &&
+ zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) {
+ needcopy = 1;
+ }
+ }
+#endif
+
+ if(needcopy) {
+
+ dst->opcodes = (zend_op*) apc_xmemcpy(src->opcodes,
+ sizeof(zend_op) * src->last,
+ apc_php_malloc);
+ zo = src->opcodes;
+ dzo = dst->opcodes;
+ while(i > 0) {
+
+ if( ((zo->op1.op_type == IS_CONST &&
+ zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) ||
+ ((zo->op2.op_type == IS_CONST &&
+ zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) {
+
+ if(!(my_copy_zend_op(dzo, zo, apc_php_malloc, apc_php_free))) {
+ assert(0); /* emalloc failed or a bad constant array */
+ }
+ }
+
+#ifdef ZEND_ENGINE_2
+ switch(zo->opcode) {
+ case ZEND_JMP:
+ dzo->op1.u.jmp_addr = dst->opcodes +
+ (zo->op1.u.jmp_addr - src->opcodes);
+ break;
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ dzo->op2.u.jmp_addr = dst->opcodes +
+ (zo->op2.u.jmp_addr - src->opcodes);
+ break;
+ case ZEND_FETCH_R:
+ case ZEND_FETCH_W:
+ case ZEND_FETCH_IS:
+ case ZEND_FETCH_FUNC_ARG:
+ if(do_prepare_fetch_global)
+ {
+ APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION();
+ }
+ break;
+ default:
+ break;
+ }
+#endif
+ i--;
+ zo++;
+ dzo++;
+ }
+#ifdef ZEND_ENGINE_2
+ } else { /* !needcopy */
+ /* The fetch is only required if auto_globals_jit=1 */
+ if(do_prepare_fetch_global)
+ {
+ zo = src->opcodes;
+ while(i > 0) {
+
+ if(zo->opcode == ZEND_FETCH_R ||
+ zo->opcode == ZEND_FETCH_W ||
+ zo->opcode == ZEND_FETCH_IS ||
+ zo->opcode == ZEND_FETCH_FUNC_ARG
+ ) {
+ APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION();
+ }
+
+ i--;
+ zo++;
+ }
+ }
+#endif
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ apc_copy_op_array_for_execution */
+zend_op_array* apc_copy_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC)
+{
+ if(dst == NULL) {
+ dst = (zend_op_array*) emalloc(sizeof(src[0]));
+ }
+ memcpy(dst, src, sizeof(src[0]));
+ dst->static_variables = my_copy_static_variables(src, apc_php_malloc, apc_php_free);
+
+ dst->refcount = apc_xmemcpy(src->refcount,
+ sizeof(src->refcount[0]),
+ apc_php_malloc);
+
+ my_prepare_op_array_for_execution(dst,src TSRMLS_CC);
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ apc_copy_function_for_execution */
+zend_function* apc_copy_function_for_execution(zend_function* src)
+{
+ zend_function* dst;
+ TSRMLS_FETCH();
+
+ dst = (zend_function*) emalloc(sizeof(src[0]));
+ memcpy(dst, src, sizeof(src[0]));
+ apc_copy_op_array_for_execution(&(dst->op_array), &(src->op_array) TSRMLS_CC);
+ return dst;
+}
+/* }}} */
+
+/* {{{ apc_copy_function_for_execution_ex */
+zend_function* apc_copy_function_for_execution_ex(void *dummy, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate)
+{
+ if(src->type==ZEND_INTERNAL_FUNCTION || src->type==ZEND_OVERLOADED_FUNCTION) return src;
+ return apc_copy_function_for_execution(src);
+}
+/* }}} */
+
+/* {{{ apc_copy_class_entry_for_execution */
+zend_class_entry* apc_copy_class_entry_for_execution(zend_class_entry* src, int is_derived)
+{
+ zend_class_entry* dst = (zend_class_entry*) emalloc(sizeof(src[0]));
+ memcpy(dst, src, sizeof(src[0]));
+
+#ifdef ZEND_ENGINE_2
+ if(src->num_interfaces)
+ {
+ /* These are slots to be populated later by ADD_INTERFACE insns */
+ dst->interfaces = apc_php_malloc(
+ sizeof(zend_class_entry*) * src->num_interfaces);
+ memset(dst->interfaces, 0,
+ sizeof(zend_class_entry*) * src->num_interfaces);
+ }
+ else
+ {
+ /* assert(dst->interfaces == NULL); */
+ }
+#endif
+
+#ifndef ZEND_ENGINE_2
+ dst->refcount = apc_xmemcpy(src->refcount,
+ sizeof(src->refcount[0]),
+ apc_php_malloc);
+#endif
+
+ /* Deep-copy the class properties, because they will be modified */
+
+ my_copy_hashtable(&dst->default_properties,
+ &src->default_properties,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ apc_php_malloc, apc_php_free);
+
+ /* For derived classes, we must also copy the function hashtable (although
+ * we can merely bitwise copy the functions it contains) */
+
+ my_copy_hashtable(&dst->function_table,
+ &src->function_table,
+ (ht_copy_fun_t) apc_copy_function_for_execution_ex,
+ NULL,
+ 0,
+ apc_php_malloc, apc_php_free);
+#ifdef ZEND_ENGINE_2
+ my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function_for_execution, src, dst);
+
+ /* zend_do_inheritance merges properties_info.
+ * Need only shallow copying as it doesn't hold the pointers.
+ */
+ my_copy_hashtable(&dst->properties_info,
+ &src->properties_info,
+ (ht_copy_fun_t) my_copy_property_info_for_execution,
+ NULL,
+ 0,
+ apc_php_malloc, apc_php_free);
+
+#ifdef ZEND_ENGINE_2_2
+ /* php5.2 introduced a scope attribute for property info */
+ my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst);
+#endif
+
+ /* if inheritance results in a hash_del, it might result in
+ * a pefree() of the pointers here. Deep copying required.
+ */
+
+ my_copy_hashtable(&dst->constants_table,
+ &src->constants_table,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ NULL,
+ 1,
+ apc_php_malloc, apc_php_free);
+
+ my_copy_hashtable(&dst->default_static_members,
+ &src->default_static_members,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ apc_php_malloc, apc_php_free);
+
+ if(src->static_members != &(src->default_static_members))
+ {
+ dst->static_members = my_copy_hashtable(NULL,
+ src->static_members,
+ (ht_copy_fun_t) my_copy_zval_ptr,
+ (ht_free_fun_t) my_free_zval_ptr,
+ 1,
+ apc_php_malloc, apc_php_free);
+ }
+ else
+ {
+ dst->static_members = &(dst->default_static_members);
+ }
+
+#endif
+
+ return dst;
+}
+/* }}} */
+
+/* {{{ apc_free_class_entry_after_execution */
+void apc_free_class_entry_after_execution(zend_class_entry* src)
+{
+#ifdef ZEND_ENGINE_2
+ if(src->num_interfaces > 0 && src->interfaces) {
+ apc_php_free(src->interfaces);
+ src->interfaces = NULL;
+ src->num_interfaces = 0;
+ }
+ /* my_destroy_hashtable() does not play nice with refcounts */
+
+ zend_hash_clean(&src->default_static_members);
+ if(src->static_members != &(src->default_static_members))
+ {
+ zend_hash_destroy(src->static_members);
+ apc_php_free(src->static_members);
+ src->static_members = NULL;
+ }
+ else
+ {
+ src->static_members = NULL;
+ }
+ zend_hash_clean(&src->default_properties);
+ zend_hash_clean(&src->constants_table);
+#endif
+
+ /* TODO: more cleanup */
+}
+/* }}} */
+
+#ifdef ZEND_ENGINE_2
+
+/* {{{ my_fixup_function */
+static void my_fixup_function(Bucket *p, zend_class_entry *src, zend_class_entry *dst)
+{
+ zend_function* zf = p->pData;
+
+ #define SET_IF_SAME_NAME(member) \
+ do { \
+ if(src->member && !strcmp(zf->common.function_name, src->member->common.function_name)) { \
+ dst->member = zf; \
+ } \
+ } \
+ while(0)
+
+ if(zf->common.scope == src)
+ {
+
+ /* Fixing up the default functions for objects here since
+ * we need to compare with the newly allocated functions
+ *
+ * caveat: a sub-class method can have the same name as the
+ * parent's constructor and create problems.
+ */
+
+ if(zf->common.fn_flags & ZEND_ACC_CTOR) dst->constructor = zf;
+ else if(zf->common.fn_flags & ZEND_ACC_DTOR) dst->destructor = zf;
+ else if(zf->common.fn_flags & ZEND_ACC_CLONE) dst->clone = zf;
+ else
+ {
+ SET_IF_SAME_NAME(__get);
+ SET_IF_SAME_NAME(__set);
+ SET_IF_SAME_NAME(__unset);
+ SET_IF_SAME_NAME(__isset);
+ SET_IF_SAME_NAME(__call);
+#ifdef ZEND_ENGINE_2_2
+ SET_IF_SAME_NAME(__tostring);
+#endif
+ }
+ zf->common.scope = dst;
+ }
+ else
+ {
+ /* no other function should reach here */
+ assert(0);
+ }
+
+ #undef SET_IF_SAME_NAME
+}
+/* }}} */
+
+#ifdef ZEND_ENGINE_2_2
+/* {{{ my_fixup_property_info */
+static void my_fixup_property_info(Bucket *p, zend_class_entry *src, zend_class_entry *dst)
+{
+ zend_property_info* property_info = (zend_property_info*)p->pData;
+
+ if(property_info->ce == src)
+ {
+ property_info->ce = dst;
+ }
+ else
+ {
+ assert(0); /* should never happen */
+ }
+}
+/* }}} */
+#endif
+
+/* {{{ my_fixup_hashtable */
+static void my_fixup_hashtable(HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst)
+{
+ Bucket *p;
+
+ uint i;
+
+ for (i = 0; i < ht->nTableSize; i++) {
+ if(!ht->arBuckets) break;
+ p = ht->arBuckets[i];
+ while (p != NULL) {
+ fixup(p, src, dst);
+ p = p->pNext;
+ }
+ }
+}
+/* }}} */
+
+#endif
+
+/* {{{ my_check_copy_function */
+static int my_check_copy_function(Bucket* p, va_list args)
+{
+ zend_class_entry* src = va_arg(args, zend_class_entry*);
+ zend_function* zf = (zend_function*)p->pData;
+#ifndef ZEND_ENGINE_2
+ zend_class_entry* parent = src->parent;
+ zend_function* parent_fn = NULL;
+#endif
+
+#ifdef ZEND_ENGINE_2
+ return (zf->common.scope == src);
+#else
+ if (parent &&
+ zend_hash_quick_find(&parent->function_table, p->arKey,
+ p->nKeyLength, p->h, (void **) &parent_fn)==SUCCESS) {
+
+ if((parent_fn && zf) &&
+ (parent_fn->op_array.refcount == zf->op_array.refcount))
+ {
+ return 0;
+ }
+ }
+ return 1;
+#endif
+}
+/* }}} */
+
+/* {{{ my_check_copy_default_property */
+static int my_check_copy_default_property(Bucket* p, va_list args)
+{
+ zend_class_entry* src = va_arg(args, zend_class_entry*);
+ zend_class_entry* parent = src->parent;
+ zval ** child_prop = (zval**)p->pData;
+ zval ** parent_prop = NULL;
+
+ if (parent &&
+ zend_hash_quick_find(&parent->default_properties, p->arKey,
+ p->nKeyLength, p->h, (void **) &parent_prop)==SUCCESS) {
+
+ if((parent_prop && child_prop) && (*parent_prop) == (*child_prop))
+ {
+ return 0;
+ }
+ }
+
+ /* possibly not in the parent */
+ return 1;
+}
+/* }}} */
+
+#ifdef ZEND_ENGINE_2
+
+/* {{{ my_check_copy_property_info */
+static int my_check_copy_property_info(Bucket* p, va_list args)
+{
+ zend_class_entry* src = va_arg(args, zend_class_entry*);
+ zend_class_entry* parent = src->parent;
+ zend_property_info* child_info = (zend_property_info*)p->pData;
+ zend_property_info* parent_info = NULL;
+
+#ifdef ZEND_ENGINE_2_2
+ /* so much easier */
+ return (child_info->ce == src);
+#endif
+
+ if (parent &&
+ zend_hash_quick_find(&parent->properties_info, p->arKey, p->nKeyLength,
+ p->h, (void **) &parent_info)==SUCCESS) {
+ if(parent_info->flags & ZEND_ACC_PRIVATE)
+ {
+ return 1;
+ }
+ if((parent_info->flags & ZEND_ACC_PPP_MASK) !=
+ (child_info->flags & ZEND_ACC_PPP_MASK))
+ {
+ /* TODO: figure out whether ACC_CHANGED is more appropriate
+ * here */
+ return 1;
+ }
+ return 0;
+ }
+
+ /* property doesn't exist in parent, copy into cached child */
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_check_copy_static_member */
+static int my_check_copy_static_member(Bucket* p, va_list args)
+{
+ zend_class_entry* src = va_arg(args, zend_class_entry*);
+ HashTable * ht = va_arg(args, HashTable*);
+ zend_class_entry* parent = src->parent;
+ HashTable * parent_ht = NULL;
+ char * member_name;
+ char * class_name = NULL;
+
+ zend_property_info *parent_info = NULL;
+ zend_property_info *child_info = NULL;
+ zval ** parent_prop = NULL;
+ zval ** child_prop = (zval**)(p->pData);
+
+ if(!parent) {
+ return 1;
+ }
+
+ /* these do not need free'ing */
+#ifdef ZEND_ENGINE_2_2
+ zend_unmangle_property_name(p->arKey, p->nKeyLength-1, &class_name, &member_name);
+#else
+ zend_unmangle_property_name(p->arKey, &class_name, &member_name);
+#endif
+
+ /* please refer do_inherit_property_access_check in zend_compile.c
+ * to understand why we lookup in properties_info.
+ */
+ if((zend_hash_find(&parent->properties_info, member_name,
+ strlen(member_name)+1, (void**)&parent_info) == SUCCESS)
+ &&
+ (zend_hash_find(&src->properties_info, member_name,
+ strlen(member_name)+1, (void**)&child_info) == SUCCESS))
+ {
+ if(child_info->flags & ZEND_ACC_STATIC &&
+ (parent_info->flags & ZEND_ACC_PROTECTED &&
+ child_info->flags & ZEND_ACC_PUBLIC))
+ {
+ /* Do not copy into static_members. zend_do_inheritance
+ * will automatically insert a NULL value.
+ * TODO: decrement refcount or fixup when copying out for exec ?
+ */
+ return 0;
+ }
+ if(ht == &(src->default_static_members))
+ {
+ parent_ht = &parent->default_static_members;
+ }
+ else
+ {
+ parent_ht = parent->static_members;
+ }
+
+ if(zend_hash_quick_find(parent_ht, p->arKey,
+ p->nKeyLength, p->h, (void**)&parent_prop) == SUCCESS)
+ {
+ /* they point to the same zval */
+ if(*parent_prop == *child_prop)
+ {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+/* }}} */
+#endif
+
+/* {{{ apc_register_optimizer(apc_optimize_function_t optimizer)
+ * register a optimizer callback function, returns the previous callback
+ */
+apc_optimize_function_t apc_register_optimizer(apc_optimize_function_t optimizer TSRMLS_DC) {
+ apc_optimize_function_t old_optimizer = APCG(apc_optimize_function);
+ APCG(apc_optimize_function) = optimizer;
+ return old_optimizer;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
+ * vim<600: expandtab sw=4 ts=4 sts=4
+ */