X-Git-Url: http://ftp.carnet.hr/carnet-debian/scm?p=php5-apc.git;a=blobdiff_plain;f=apc_compile.c;fp=apc_compile.c;h=7627a55eb595ddc617897a8c83bbd2802024c3a5;hp=0000000000000000000000000000000000000000;hb=3682e0a7a26931aabca2b6e54eb08efd7dc0430b;hpb=575ce08215526bb71a967d69d601e77e1afbcd11 diff --git a/apc_compile.c b/apc_compile.c new file mode 100644 index 0000000..7627a55 --- /dev/null +++ b/apc_compile.c @@ -0,0 +1,2572 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + 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\0\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 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 + */