New PHP5 APC - version 3.0.19, using PHP5 5.2.0-8+etch11,
[php5-apc.git] / apc_compile.c
1 /*
2   +----------------------------------------------------------------------+
3   | APC                                                                  |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2008 The PHP Group                                     |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt.                                 |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Daniel Cowgill <dcowgill@communityconnect.com>              |
16   |          Rasmus Lerdorf <rasmus@php.net>                             |
17   |          Arun C. Murthy <arunc@yahoo-inc.com>                        |
18   |          Gopal Vijayaraghavan <gopalv@yahoo-inc.com>                 |
19   +----------------------------------------------------------------------+
20
21    This software was contributed to PHP by Community Connect Inc. in 2002
22    and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
23    Future revisions and derivatives of this source code must acknowledge
24    Community Connect Inc. as the original contributor of this module by
25    leaving this note intact in the source code.
26
27    All other licensing and usage conditions are those of the PHP Group.
28
29  */
30
31 /* $Id: apc_compile.c,v 3.87.2.8 2008/05/11 18:57:00 rasmus Exp $ */
32
33 #include "apc_compile.h"
34 #include "apc_globals.h"
35 #include "apc_zend.h"
36
37 #ifndef Z_REFCOUNT_P
38 #define Z_REFCOUNT_P(pz)              (pz)->refcount
39 #define Z_REFCOUNT_PP(ppz)            Z_REFCOUNT_P(*(ppz))
40 #endif
41  
42 #ifndef Z_SET_REFCOUNT_P
43 #define Z_SET_REFCOUNT_P(pz, rc)      (pz)->refcount = rc
44 #define Z_SET_REFCOUNT_PP(ppz, rc)    Z_SET_REFCOUNT_P(*(ppz), rc)
45 #endif
46
47 #ifndef Z_ADDREF_P
48 #define Z_ADDREF_P(pz)                (pz)->refcount++
49 #define Z_ADDREF_PP(ppz)              Z_ADDREF_P(*(ppz))
50 #endif
51  
52 #ifndef Z_DELREF_P
53 #define Z_DELREF_P(pz)                (pz)->refcount--
54 #define Z_DELREF_PP(ppz)              Z_DELREF_P(*(ppz))
55 #endif
56  
57 #ifndef Z_ISREF_P
58 #define Z_ISREF_P(pz)                 (pz)->is_ref
59 #define Z_ISREF_PP(ppz)               Z_ISREF_P(*(ppz))
60 #endif
61  
62 #ifndef Z_SET_ISREF_P
63 #define Z_SET_ISREF_P(pz)             (pz)->is_ref = 1
64 #define Z_SET_ISREF_PP(ppz)           Z_SET_ISREF_P(*(ppz))
65 #endif
66  
67 #ifndef Z_UNSET_ISREF_P
68 #define Z_UNSET_ISREF_P(pz)           (pz)->is_ref = 0
69 #define Z_UNSET_ISREF_PP(ppz)         Z_UNSET_ISREF_P(*(ppz))
70 #endif
71  
72 #ifndef Z_SET_ISREF_TO_P
73 #define Z_SET_ISREF_TO_P(pz, isref)   (pz)->is_ref = isref
74 #define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref)
75 #endif
76  
77 typedef void* (*ht_copy_fun_t)(void*, void*, apc_malloc_t, apc_free_t);
78 typedef void  (*ht_free_fun_t)(void*, apc_free_t);
79 typedef int (*ht_check_copy_fun_t)(Bucket*, va_list);
80
81 #ifdef ZEND_ENGINE_2
82 typedef void (*ht_fixup_fun_t)(Bucket*, zend_class_entry*, zend_class_entry*);
83 #endif
84
85 #define CHECK(p) { if ((p) == NULL) return NULL; }
86
87 /* {{{ internal function declarations */
88
89 static int is_derived_class(zend_op_array* op_array, const char* key, int key_size);
90
91 static zend_function* my_bitwise_copy_function(zend_function*, zend_function*, apc_malloc_t);
92
93 /*
94  * The "copy" functions perform deep-copies on a particular data structure
95  * (passed as the second argument). They also optionally allocate space for
96  * the destination data structure if the first argument is null.
97  */
98 static zval** my_copy_zval_ptr(zval**, const zval**, apc_malloc_t, apc_free_t);
99 static zval* my_copy_zval(zval*, const zval*, apc_malloc_t, apc_free_t);
100 static znode* my_copy_znode(znode*, znode*, apc_malloc_t, apc_free_t);
101 static zend_op* my_copy_zend_op(zend_op*, zend_op*, apc_malloc_t, apc_free_t);
102 static zend_function* my_copy_function(zend_function*, zend_function*, apc_malloc_t, apc_free_t);
103 static zend_function_entry* my_copy_function_entry(zend_function_entry*, const zend_function_entry*, apc_malloc_t, apc_free_t);
104 static zend_class_entry* my_copy_class_entry(zend_class_entry*, zend_class_entry*, apc_malloc_t, apc_free_t);
105 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, ...);
106 #define my_copy_hashtable( dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate) \
107     my_copy_hashtable_ex(dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate, NULL)
108 static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate);
109 #ifdef ZEND_ENGINE_2
110 static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate);
111 static zend_arg_info* my_copy_arg_info_array(zend_arg_info*, const zend_arg_info*, uint, apc_malloc_t, apc_free_t);
112 static zend_arg_info* my_copy_arg_info(zend_arg_info*, const zend_arg_info*, apc_malloc_t, apc_free_t);
113 #endif
114 /*
115  * The "destroy" functions free the memory associated with a particular data
116  * structure but do not free the pointer to the data structure.
117  *
118  * my_destroy_zval() returns SUCCESS or FAILURE, FAILURE means that
119  * the zval* has other references elsewhere 
120  */
121 static int  my_destroy_zval(zval*, apc_free_t); 
122 static void my_destroy_zval_ptr(zval**, apc_free_t);
123 static void my_destroy_zend_op(zend_op*, apc_free_t);
124 static void my_destroy_znode(znode*, apc_free_t);
125 static void my_destroy_function(zend_function*, apc_free_t);
126 static void my_destroy_function_entry(zend_function_entry*, apc_free_t);
127 static void my_destroy_class_entry(zend_class_entry*, apc_free_t);
128 static void my_destroy_hashtable(HashTable*, ht_free_fun_t, apc_free_t);
129 static void my_destroy_op_array(zend_op_array*, apc_free_t);
130 #ifdef ZEND_ENGINE_2
131 static void my_destroy_property_info(zend_property_info*, apc_free_t);
132 static void my_destroy_arg_info_array(zend_arg_info* src, uint, apc_free_t);
133 static void my_destroy_arg_info(zend_arg_info*, apc_free_t);
134 #endif
135
136 /*
137  * The "free" functions work exactly like their "destroy" counterparts (see
138  * above) but also free the pointer to the data structure.
139  */
140 static void my_free_zval_ptr(zval**, apc_free_t);
141 static void my_free_function(zend_function*, apc_free_t);
142 static void my_free_hashtable(HashTable*, ht_free_fun_t, apc_free_t);
143 #ifdef ZEND_ENGINE_2
144 static void my_free_property_info(zend_property_info* src, apc_free_t);
145 static void my_free_arg_info_array(zend_arg_info*, uint, apc_free_t);
146 static void my_free_arg_info(zend_arg_info*, apc_free_t);
147 #endif
148
149 /*
150  * The "fixup" functions need for ZEND_ENGINE_2
151  */
152 #ifdef ZEND_ENGINE_2
153 static void my_fixup_function( Bucket *p, zend_class_entry *src, zend_class_entry *dst );
154 static void my_fixup_hashtable( HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst );
155 /* my_fixup_function_for_execution is the same as my_fixup_function
156  * but named differently for clarity
157  */
158 #define my_fixup_function_for_execution my_fixup_function
159
160 #ifdef ZEND_ENGINE_2_2
161 static void my_fixup_property_info( Bucket *p, zend_class_entry *src, zend_class_entry *dst );
162 #define my_fixup_property_info_for_execution my_fixup_property_info
163 #endif
164
165 #endif
166
167 /*
168  * These functions return "1" if the member/function is
169  * defined/overridden in the 'current' class and not inherited.
170  */
171 static int my_check_copy_function(Bucket* src, va_list args);
172 static int my_check_copy_default_property(Bucket* p, va_list args);
173 #ifdef ZEND_ENGINE_2
174 static int my_check_copy_property_info(Bucket* src, va_list args);
175 static int my_check_copy_static_member(Bucket* src, va_list args);
176 #endif
177
178 /* }}} */
179
180 /* {{{ check_op_array_integrity */
181 #if 0
182 static void check_op_array_integrity(zend_op_array* src)
183 {
184     int i, j;
185
186     /* These sorts of checks really aren't particularly effective, but they
187      * can provide a welcome sanity check when debugging. Just don't enable
188      * for production use!  */
189
190     assert(src->refcount != NULL);
191     assert(src->opcodes != NULL);
192     assert(src->last > 0);
193
194     for (i = 0; i < src->last; i++) {
195         zend_op* op = &src->opcodes[i];
196         znode* nodes[] = { &op->result, &op->op1, &op->op2 };
197         for (j = 0; j < 3; j++) {
198             assert(nodes[j]->op_type == IS_CONST ||
199                    nodes[j]->op_type == IS_VAR ||
200                    nodes[j]->op_type == IS_TMP_VAR ||
201                    nodes[j]->op_type == IS_UNUSED);
202
203             if (nodes[j]->op_type == IS_CONST) {
204                 int type = nodes[j]->u.constant.type;
205                 assert(type == IS_RESOURCE ||
206                        type == IS_BOOL ||
207                        type == IS_LONG ||
208                        type == IS_DOUBLE ||
209                        type == IS_NULL ||
210                        type == IS_CONSTANT ||
211                        type == IS_STRING ||
212                        type == FLAG_IS_BC ||
213                        type == IS_ARRAY ||
214                        type == IS_CONSTANT_ARRAY ||
215                        type == IS_OBJECT);
216             }
217         }
218     }
219 }
220 #endif
221 /* }}} */
222
223 /* {{{ is_derived_class */
224 static int is_derived_class(zend_op_array* op_array, const char* key, int key_size)
225 {
226     int i;
227
228     /*
229      * Scan the op_array for execution-time class declarations of derived
230      * classes. If we find one whose key matches our current class key, we
231      * know the current class is a derived class.
232      *
233      * This check is exceedingly inefficient (fortunately it only has to occur
234      * once, when the source file is first compiled and cached), but the
235      * compiler should save this information for us -- definitely a candidate
236      * for a Zend Engine patch.
237      *
238      * XXX checking for derived classes provides a minimal (albeit measurable)
239      * speed up. It may not be worth the added complexity -- considere
240      * removing this optimization.
241      */
242
243     for (i = 0; i < op_array->last; i++) {
244         zend_op* op = &op_array->opcodes[i];
245
246 #ifdef ZEND_ENGINE_2        
247         if (op->opcode == ZEND_DECLARE_CLASS &&
248             op->extended_value == ZEND_DECLARE_INHERITED_CLASS)
249 #else            
250         if (op->opcode == ZEND_DECLARE_FUNCTION_OR_CLASS &&
251             op->extended_value == ZEND_DECLARE_INHERITED_CLASS)
252 #endif            
253         {
254             if (op->op1.u.constant.value.str.len == key_size &&
255                 !memcmp(op->op1.u.constant.value.str.val, key, key_size))
256             {
257                 return 1;
258             }
259         }
260     }
261
262     return 0;
263 }
264 /* }}} */
265
266 /* {{{ my_bitwise_copy_function */
267 static zend_function* my_bitwise_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate)
268 {
269     assert(src != NULL);
270
271     if (!dst) {
272         CHECK(dst = (zend_function*) allocate(sizeof(src[0])));
273     }
274
275     /* We only need to do a bitwise copy */
276     memcpy(dst, src, sizeof(src[0]));
277
278     return dst;
279 }
280 /* }}} */
281
282 /* {{{ my_copy_zval_ptr */
283 static zval** my_copy_zval_ptr(zval** dst, const zval** src, apc_malloc_t allocate, apc_free_t deallocate)
284 {
285     int local_dst_alloc = 0;
286     zval* dst_new;
287     
288     assert(src != NULL);
289
290     if (!dst) {
291         CHECK(dst = (zval**) allocate(sizeof(zval*)));
292         local_dst_alloc = 1;
293     }
294
295 #ifdef ZEND_ENGINE_2_3
296     if(!(dst[0] = (zval*) allocate(sizeof(zval_gc_info)))) {
297         if(local_dst_alloc) deallocate(dst);
298         return NULL;
299     }
300
301     if(allocate == apc_php_malloc) {
302         GC_ZVAL_INIT(dst[0]);
303     }
304 #else
305     if(!(dst[0] = (zval*) allocate(sizeof(zval)))) {
306         if(local_dst_alloc) deallocate(dst);
307         return NULL;
308     }
309 #endif
310     if(!(dst_new = my_copy_zval(*dst, *src, allocate, deallocate))) {
311         if(local_dst_alloc) deallocate(dst);
312         return NULL;
313     }
314     if(dst_new != *dst) {
315         deallocate(*dst);
316         *dst = dst_new;
317     }
318
319     Z_SET_REFCOUNT_PP(dst, Z_REFCOUNT_PP((zval**)src));
320     Z_SET_ISREF_TO_PP(dst, Z_ISREF_PP((zval**)src));
321     
322     return dst;
323 }
324 /* }}} */
325
326 /* {{{ my_copy_zval */
327 static zval* my_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
328 {
329     zval **tmp;
330     TSRMLS_FETCH();
331     
332     assert(dst != NULL);
333     assert(src != NULL);
334
335     memcpy(dst, src, sizeof(src[0]));
336
337     switch (src->type & ~IS_CONSTANT_INDEX) {
338     case IS_RESOURCE:
339     case IS_BOOL:
340     case IS_LONG:
341     case IS_DOUBLE:
342     case IS_NULL:
343         break;
344
345     case IS_CONSTANT:
346     case IS_STRING:
347 #ifndef ZEND_ENGINE_2        
348     case FLAG_IS_BC:
349 #endif        
350         if (src->value.str.val) {
351             CHECK(dst->value.str.val = apc_xmemcpy(src->value.str.val,
352                                                    src->value.str.len+1,
353                                                    allocate));
354         }
355         break;
356     
357     case IS_ARRAY:
358
359         if(APCG(copied_zvals)) {
360             if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) {
361                 Z_ADDREF_PP(tmp);
362                 return *tmp;
363             }
364         
365             zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&dst, sizeof(zval*), NULL);
366         }
367         /* fall through */
368  
369     case IS_CONSTANT_ARRAY:
370
371         CHECK(dst->value.ht =
372             my_copy_hashtable(NULL,
373                               src->value.ht,
374                               (ht_copy_fun_t) my_copy_zval_ptr,
375                               (ht_free_fun_t) my_free_zval_ptr,
376                               1,
377                               allocate, deallocate));
378         break;
379
380     case IS_OBJECT:
381 #ifndef ZEND_ENGINE_2        
382         CHECK(dst->value.obj.ce =
383             my_copy_class_entry(NULL, src->value.obj.ce, allocate, deallocate));
384
385         if(!(dst->value.obj.properties = my_copy_hashtable(NULL,
386                               src->value.obj.properties,
387                               (ht_copy_fun_t) my_copy_zval_ptr,
388                               (ht_free_fun_t) my_free_zval_ptr,
389                               1,
390                               allocate, deallocate))) {
391             my_destroy_class_entry(dst->value.obj.ce, deallocate);
392             return NULL;
393         }
394         break;
395 #else
396         dst->type = IS_NULL;
397 #endif    
398         break;
399
400     default:
401         assert(0);
402     }
403
404     return dst;
405 }
406 /* }}} */
407
408 /* {{{ my_copy_znode */
409 static znode* my_copy_znode(znode* dst, znode* src, apc_malloc_t allocate, apc_free_t deallocate)
410 {
411     assert(dst != NULL);
412     assert(src != NULL);
413
414     memcpy(dst, src, sizeof(src[0]));
415
416 #ifdef IS_CV
417     assert(dst ->op_type == IS_CONST ||
418            dst ->op_type == IS_VAR ||
419            dst ->op_type == IS_CV ||
420            dst ->op_type == IS_TMP_VAR ||
421            dst ->op_type == IS_UNUSED);
422 #else
423     assert(dst ->op_type == IS_CONST ||
424            dst ->op_type == IS_VAR ||
425            dst ->op_type == IS_TMP_VAR ||
426            dst ->op_type == IS_UNUSED);
427 #endif
428
429     if (src->op_type == IS_CONST) {
430         if(!my_copy_zval(&dst->u.constant, &src->u.constant, allocate, deallocate)) {
431             return NULL;
432         }
433     }
434
435     return dst;
436 }
437 /* }}} */
438
439 /* {{{ my_copy_zend_op */
440 static zend_op* my_copy_zend_op(zend_op* dst, zend_op* src, apc_malloc_t allocate, apc_free_t deallocate)
441 {
442     assert(dst != NULL);
443     assert(src != NULL);
444
445     memcpy(dst, src, sizeof(src[0]));
446
447     if( my_copy_znode(&dst->result, &src->result, allocate, deallocate) == NULL 
448             || my_copy_znode(&dst->op1, &src->op1, allocate, deallocate) == NULL
449             || my_copy_znode(&dst->op2, &src->op2, allocate, deallocate) == NULL)
450     {
451         return NULL;
452     }
453
454     return dst;
455 }
456 /* }}} */
457
458 /* {{{ my_copy_function */
459 static zend_function* my_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate)
460 {
461     int local_dst_alloc = 0;
462         TSRMLS_FETCH();
463
464     assert(src != NULL);
465
466     if(!dst) local_dst_alloc = 1;
467     CHECK(dst = my_bitwise_copy_function(dst, src, allocate));
468
469     switch (src->type) {
470     case ZEND_INTERNAL_FUNCTION:
471     case ZEND_OVERLOADED_FUNCTION:
472         /* shallow copy because op_array is internal */
473         dst->op_array = src->op_array;
474         break;
475         
476     case ZEND_USER_FUNCTION:
477     case ZEND_EVAL_CODE:
478         if(!apc_copy_op_array(&dst->op_array,
479                                 &src->op_array,
480                                 allocate, deallocate TSRMLS_CC)) {
481             if(local_dst_alloc) deallocate(dst);
482             return NULL;
483         }
484         break;
485
486     default:
487         assert(0);
488     }
489 #ifdef ZEND_ENGINE_2
490     /* 
491      * op_array bitwise copying overwrites what ever you modified
492      * before apc_copy_op_array - which is why this code is outside 
493      * my_bitwise_copy_function. 
494      */
495
496     /* zend_do_inheritance will re-look this up, because the pointers
497      * in prototype are from a function table of another class. It just
498      * helps if that one is from EG(class_table).
499      */
500     dst->common.prototype = NULL; 
501
502     /* once a method is marked as ZEND_ACC_IMPLEMENTED_ABSTRACT then you
503      * have to carry around a prototype. Thankfully zend_do_inheritance
504      * sets this properly as well
505      */
506     dst->common.fn_flags = src->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT);
507 #endif
508
509
510     return dst;
511 }
512 /* }}} */
513
514 /* {{{ my_copy_function_entry */
515 static zend_function_entry* my_copy_function_entry(zend_function_entry* dst, const zend_function_entry* src, apc_malloc_t allocate, apc_free_t deallocate)
516 {
517     int local_dst_alloc = 0;
518     assert(src != NULL);
519
520     if (!dst) {
521         CHECK(dst = (zend_function_entry*) allocate(sizeof(src[0])));
522         local_dst_alloc = 1;
523     }
524
525     /* Start with a bitwise copy */
526     memcpy(dst, src, sizeof(src[0]));
527
528     dst->fname = NULL;
529 #ifdef ZEND_ENGINE_2
530     dst->arg_info = NULL;
531 #else
532     dst->func_arg_types = NULL;
533 #endif
534
535     if (src->fname) {
536         if(!(dst->fname = apc_xstrdup(src->fname, allocate))) {
537             goto cleanup;
538         }
539     }
540
541 #ifdef ZEND_ENGINE_2    
542     if (src->arg_info) {
543         if(!(dst->arg_info = my_copy_arg_info_array(NULL,
544                                                 src->arg_info,
545                                                 src->num_args,
546                                                 allocate,
547                                                 deallocate))) {
548             goto cleanup;
549         }
550     }
551 #else    
552     if (src->func_arg_types) {
553         if(!(dst->func_arg_types = apc_xmemcpy(src->func_arg_types,
554                                                 src->func_arg_types[0]+1,
555                                                 allocate))) {
556             goto cleanup;
557         }
558     }
559 #endif
560
561     return dst;
562
563 cleanup:
564     if(dst->fname) deallocate((char*)dst->fname);
565     if(local_dst_alloc) deallocate(dst);
566     return NULL;
567 }
568 /* }}} */
569
570 #ifdef ZEND_ENGINE_2
571 /* {{{ my_copy_property_info */
572 static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate)
573 {
574     int local_dst_alloc = 0;
575     
576     assert(src != NULL);
577
578     if (!dst) {
579         CHECK(dst = (zend_property_info*) allocate(sizeof(*src)));
580         local_dst_alloc = 1;
581     }
582
583     /* Start with a bitwise copy */
584     memcpy(dst, src, sizeof(*src));
585
586     dst->name = NULL;
587 #if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
588     dst->doc_comment = NULL;
589 #endif
590
591     if (src->name) {
592         /* private members are stored inside property_info as a mangled
593          * string of the form:
594          *      \0<classname>\0<membername>\0
595          */
596         if(!(dst->name = 
597                     apc_xmemcpy(src->name, src->name_length+1, allocate))) {
598             goto cleanup;
599         }
600     }
601
602 #if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
603     if (src->doc_comment) {
604         if( !(dst->doc_comment =
605                     apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
606             goto cleanup;
607         }
608     }
609 #endif
610
611     return dst;
612
613 cleanup:
614     if(dst->name) deallocate(dst->name);
615 #if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
616     if(dst->doc_comment) deallocate(dst->doc_comment);
617 #endif
618     if(local_dst_alloc) deallocate(dst);
619     return NULL;
620 }
621 /* }}} */
622
623 /* {{{ my_copy_property_info_for_execution */
624 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)
625 {
626     int local_dst_alloc = 0;
627     
628     assert(src != NULL);
629
630     if (!dst) {
631         CHECK(dst = (zend_property_info*) allocate(sizeof(*src)));
632         local_dst_alloc = 1;
633     }
634
635     /* We need only a shallow copy */
636     memcpy(dst, src, sizeof(*src));
637
638     return dst;
639 }
640 /* }}} */
641
642 /* {{{ my_copy_arg_info_array */
643 static zend_arg_info* my_copy_arg_info_array(zend_arg_info* dst, const zend_arg_info* src, uint num_args, apc_malloc_t allocate, apc_free_t deallocate)
644 {
645     int local_dst_alloc = 0;
646     int i = 0;
647
648     
649     if (!dst) {
650         CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)*num_args));
651         local_dst_alloc = 1;
652     }
653
654     /* Start with a bitwise copy */
655     memcpy(dst, src, sizeof(*src)*num_args);
656
657     for(i=0; i < num_args; i++) {
658         if(!(my_copy_arg_info( &dst[i], &src[i], allocate, deallocate))) {            
659             if(i) my_destroy_arg_info_array(dst, i-1, deallocate);
660             if(local_dst_alloc) deallocate(dst);
661             return NULL;
662         }
663     }
664
665     return dst;    
666 }
667 /* }}} */
668
669 /* {{{ my_copy_arg_info */
670 static zend_arg_info* my_copy_arg_info(zend_arg_info* dst, const zend_arg_info* src, apc_malloc_t allocate, apc_free_t deallocate)
671 {
672     int local_dst_alloc = 0;
673     
674     assert(src != NULL);
675
676     if (!dst) {
677         CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)));
678         local_dst_alloc = 1;
679     }
680
681     /* Start with a bitwise copy */
682     memcpy(dst, src, sizeof(*src));
683
684     dst->name = NULL;
685     dst->class_name = NULL;
686
687     if (src->name) {
688         if(!(dst->name = 
689                     apc_xmemcpy(src->name, src->name_len+1, allocate))) {
690             goto cleanup;
691         }
692     }
693
694     if (src->class_name) {
695         if(!(dst->class_name = 
696                     apc_xmemcpy(src->class_name, src->class_name_len+1, allocate))) {
697             goto cleanup;
698         }
699     }
700
701     return dst;
702
703 cleanup:
704     if(dst->name) deallocate((char*)dst->name);
705     if(dst->class_name) deallocate((char*)dst->class_name);
706     if(local_dst_alloc) deallocate(dst);
707     return NULL;
708 }
709 /* }}} */
710 #endif
711
712 /* {{{ my_copy_class_entry */
713 static zend_class_entry* my_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate)
714 {
715     int local_dst_alloc = 0;
716     int i = 0;
717
718     assert(src != NULL);
719
720     if (!dst) {
721         CHECK(dst = (zend_class_entry*) allocate(sizeof(*src)));
722         local_dst_alloc = 1;
723     }
724
725     /* Start with a bitwise copy */
726     memcpy(dst, src, sizeof(*src));
727
728     dst->name = NULL;
729     dst->builtin_functions = NULL;
730     memset(&dst->function_table, 0, sizeof(dst->function_table));
731     memset(&dst->default_properties, 0, sizeof(dst->default_properties));
732 #ifndef ZEND_ENGINE_2
733     dst->refcount = NULL;
734 #else
735     dst->static_members = NULL;
736     dst->doc_comment = NULL;
737     dst->filename = NULL;
738     memset(&dst->properties_info, 0, sizeof(dst->properties_info));
739     memset(&dst->constants_table, 0, sizeof(dst->constants_table));
740     memset(&dst->default_static_members, 0, sizeof(dst->default_static_members));
741 #endif
742
743     if (src->name) {
744         if(!(dst->name = apc_xstrdup(src->name, allocate))) {
745             goto cleanup;
746         }
747     }
748
749 #ifndef ZEND_ENGINE_2    
750     if(!(dst->refcount = apc_xmemcpy(src->refcount,
751                                       sizeof(src->refcount[0]),
752                                       allocate))) {
753         goto cleanup;
754     }
755 #endif        
756
757     if(!(my_copy_hashtable_ex(&dst->function_table,
758                             &src->function_table,
759                             (ht_copy_fun_t) my_copy_function,
760                             (ht_free_fun_t) my_free_function,
761                             0,
762                             allocate, deallocate,
763                             (ht_check_copy_fun_t) my_check_copy_function,
764                             src))) {
765         goto cleanup;
766     }
767
768 #ifdef ZEND_ENGINE_2
769
770     /* the interfaces are populated at runtime using ADD_INTERFACE */
771     dst->interfaces = NULL; 
772
773     /* the current count includes inherited interfaces as well,
774        the real dynamic ones are the first <n> which are zero'd
775        out in zend_do_end_class_declaration */
776     for(i = 0 ; i < src->num_interfaces ; i++) {
777         if(src->interfaces[i])
778         {
779             dst->num_interfaces = i;
780             break;
781         }
782     }
783
784     /* these will either be set inside my_fixup_hashtable or 
785      * they will be copied out from parent inside zend_do_inheritance 
786      */
787     dst->constructor =  NULL;
788     dst->destructor = NULL;
789     dst->clone = NULL;
790     dst->__get = NULL;
791     dst->__set = NULL;
792     dst->__unset = NULL;
793     dst->__isset = NULL;
794     dst->__call = NULL;
795 #ifdef ZEND_ENGINE_2_2
796     dst->__tostring = NULL;
797 #endif
798
799     /* unset function proxies */
800     dst->serialize_func = NULL;
801     dst->unserialize_func = NULL;
802     
803     my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function, src, dst);
804 #endif
805
806     if(!(my_copy_hashtable_ex(&dst->default_properties,
807                             &src->default_properties,
808                             (ht_copy_fun_t) my_copy_zval_ptr,
809                             (ht_free_fun_t) my_free_zval_ptr,
810                             1,
811                             allocate,deallocate,
812                             (ht_check_copy_fun_t) my_check_copy_default_property,
813                             src))) {
814         goto cleanup;
815     }
816
817 #ifdef ZEND_ENGINE_2
818     
819     if(!(my_copy_hashtable_ex(&dst->properties_info,
820                             &src->properties_info,
821                             (ht_copy_fun_t) my_copy_property_info,
822                             (ht_free_fun_t) my_free_property_info,
823                             0,
824                             allocate, deallocate,
825                             (ht_check_copy_fun_t) my_check_copy_property_info,
826                             src))) {
827         goto cleanup;
828     }
829
830 #ifdef ZEND_ENGINE_2_2
831     /* php5.2 introduced a scope attribute for property info */
832     my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst);
833 #endif
834     
835     if(!my_copy_hashtable_ex(&dst->default_static_members,
836                             &src->default_static_members,
837                             (ht_copy_fun_t) my_copy_zval_ptr,
838                             (ht_free_fun_t) my_free_zval_ptr,
839                             1,
840                             allocate, deallocate,
841                             (ht_check_copy_fun_t) my_check_copy_static_member,
842                             src,
843                             &src->default_static_members)) {
844         goto cleanup;
845     }
846     if(src->static_members != &src->default_static_members)
847     {
848         if(!(dst->static_members = my_copy_hashtable_ex(NULL,
849                             src->static_members,
850                             (ht_copy_fun_t) my_copy_zval_ptr,
851                             (ht_free_fun_t) my_free_zval_ptr,
852                             1,
853                             allocate, deallocate,
854                             (ht_check_copy_fun_t) my_check_copy_static_member,
855                             src,
856                             src->static_members))) {
857             goto cleanup;
858         }
859     }
860     else
861     {
862         dst->static_members = &dst->default_static_members;
863     }
864
865     if(!(my_copy_hashtable(&dst->constants_table,
866                             &src->constants_table,
867                             (ht_copy_fun_t) my_copy_zval_ptr,
868                             (ht_free_fun_t) my_free_zval_ptr,
869                             1,
870                             allocate, deallocate))) {
871         goto cleanup;
872     }
873
874     if (src->doc_comment) {
875         if(!(dst->doc_comment =
876                     apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
877             goto cleanup;
878         }
879     }
880 #endif
881     
882     if (src->builtin_functions) {
883         int i, n;
884
885         for (n = 0; src->type == ZEND_INTERNAL_CLASS && src->builtin_functions[n].fname != NULL; n++) {}
886
887         if(!(dst->builtin_functions =
888             (zend_function_entry*)
889                 allocate((n + 1) * sizeof(zend_function_entry)))) {
890             goto cleanup;
891         }
892
893
894         for (i = 0; i < n; i++) {
895             if(!my_copy_function_entry((zend_function_entry*)(&dst->builtin_functions[i]),
896                                    &src->builtin_functions[i],
897                                    allocate, deallocate)) {
898                 int ii;
899
900                 for(ii=i-1; ii>=0; ii--) my_destroy_function_entry((zend_function_entry*)(&dst->builtin_functions[ii]), deallocate);
901                 goto cleanup;
902             }
903         }
904         *(char**)&(dst->builtin_functions[n].fname) = NULL;
905     }
906
907 #ifdef ZEND_ENGINE_2
908     if (src->filename) {
909         if(!(dst->filename = apc_xstrdup(src->filename, allocate))) {
910             goto cleanup;
911         }
912     }
913 #endif
914    
915     return dst;
916
917
918 cleanup:
919     if(dst->name) deallocate(dst->name);
920 #ifdef ZEND_ENGINE_2
921     if(dst->doc_comment) deallocate(dst->doc_comment);
922     if(dst->filename) deallocate(dst->filename);
923 #else
924     if(dst->refcount) deallocate(dst->refcount);
925 #endif
926     
927     if(dst->builtin_functions) deallocate((zend_function_entry*)dst->builtin_functions);
928     if(dst->function_table.arBuckets) my_destroy_hashtable(&dst->function_table, (ht_free_fun_t) my_free_function, deallocate);
929     if(dst->default_properties.arBuckets) my_destroy_hashtable(&dst->default_properties, (ht_free_fun_t) my_free_zval_ptr, deallocate);
930
931 #ifdef ZEND_ENGINE_2
932     if(dst->properties_info.arBuckets) my_destroy_hashtable(&dst->properties_info, (ht_free_fun_t) my_free_property_info, deallocate);
933     if(dst->default_static_members.arBuckets)
934     {
935         my_destroy_hashtable(&dst->default_static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate);
936     }
937     if(dst->static_members && dst->static_members != &(dst->default_static_members))
938     {
939         my_destroy_hashtable(dst->static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate);
940         deallocate(dst->static_members);
941     }
942     if(dst->constants_table.arBuckets) my_destroy_hashtable(&dst->constants_table, (ht_free_fun_t) my_free_zval_ptr, deallocate);
943 #endif
944     if(local_dst_alloc) deallocate(dst);
945
946     return NULL;
947 }
948 /* }}} */
949
950 /* {{{ my_copy_hashtable */
951 static HashTable* my_copy_hashtable_ex(HashTable* dst,
952                                     HashTable* src,
953                                     ht_copy_fun_t copy_fn,
954                                     ht_free_fun_t free_fn,
955                                     int holds_ptrs,
956                                     apc_malloc_t allocate, 
957                                     apc_free_t deallocate,
958                                     ht_check_copy_fun_t check_fn,
959                                     ...)
960 {
961     Bucket* curr = NULL;
962     Bucket* prev = NULL;
963     Bucket* newp = NULL;
964     int first = 1;
965     int local_dst_alloc = 0;
966     int index = 0;
967
968     assert(src != NULL);
969
970     if (!dst) {
971         CHECK(dst = (HashTable*) allocate(sizeof(src[0])));
972         local_dst_alloc = 1;
973     }
974
975     memcpy(dst, src, sizeof(src[0]));
976
977     /* allocate buckets for the new hashtable */
978     if(!(dst->arBuckets = allocate(dst->nTableSize * sizeof(Bucket*)))) {
979         if(local_dst_alloc) deallocate(dst);
980         return NULL;
981     }
982
983     memset(dst->arBuckets, 0, dst->nTableSize * sizeof(Bucket*));
984     dst->pInternalPointer = NULL;
985     dst->pListHead = NULL;
986     
987     for (curr = src->pListHead; curr != NULL; curr = curr->pListNext) {
988         int n = curr->h % dst->nTableSize;
989
990         if(check_fn) {
991             va_list args;
992             va_start(args, check_fn);
993
994             /* Call the check_fn to see if the current bucket 
995              * needs to be copied out
996              */
997             if(!check_fn(curr, args)) {
998                 dst->nNumOfElements--;
999                 continue;
1000             }
1001
1002             va_end(args);
1003         }
1004
1005         /* create a copy of the bucket 'curr' */
1006         if(!(newp =
1007             (Bucket*) apc_xmemcpy(curr,
1008                                   sizeof(Bucket) + curr->nKeyLength - 1,
1009                                   allocate))) {
1010             goto cleanup;
1011         }
1012
1013         /* insert 'newp' into the linked list at its hashed index */
1014         if (dst->arBuckets[n]) {
1015             newp->pNext = dst->arBuckets[n];
1016             newp->pLast = NULL;
1017             newp->pNext->pLast = newp;
1018         }
1019         else {
1020             newp->pNext = newp->pLast = NULL;
1021         }
1022
1023         dst->arBuckets[n] = newp;
1024
1025         /* copy the bucket data using our 'copy_fn' callback function */
1026         if(!(newp->pData = copy_fn(NULL, curr->pData, allocate, deallocate))) {
1027             goto cleanup;
1028         }
1029
1030         if (holds_ptrs) {
1031             memcpy(&newp->pDataPtr, newp->pData, sizeof(void*));
1032         }
1033         else {
1034             newp->pDataPtr = NULL;
1035         }
1036
1037         /* insert 'newp' into the table-thread linked list */
1038         newp->pListLast = prev;
1039         newp->pListNext = NULL;
1040
1041         if (prev) {
1042             prev->pListNext = newp;
1043         }
1044
1045         if (first) {
1046             dst->pListHead = newp;
1047             first = 0;
1048         }
1049
1050         prev = newp;
1051     }
1052
1053     dst->pListTail = newp;
1054
1055     return dst;
1056     
1057     cleanup:
1058     for(index = 0; index < dst->nTableSize; index++)
1059     {
1060         curr = dst->arBuckets[index];
1061         while(curr != NULL)
1062         {
1063             Bucket * tmp = curr;
1064             if(curr->pData && free_fn)
1065             {
1066                 free_fn(curr->pData, deallocate);
1067             }
1068             curr = curr->pNext;
1069             deallocate(tmp);
1070         }
1071     }   
1072     deallocate(dst->arBuckets);
1073     if(local_dst_alloc) deallocate(dst);
1074     else dst->arBuckets = NULL;
1075
1076     return NULL;
1077 }
1078 /* }}} */
1079
1080 /* {{{ my_copy_static_variables */
1081 static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate)
1082
1083     if (src->static_variables == NULL) {
1084         return NULL;
1085     }
1086
1087     return my_copy_hashtable(NULL,
1088                              src->static_variables,
1089                              (ht_copy_fun_t) my_copy_zval_ptr,
1090                              (ht_free_fun_t) my_free_zval_ptr,
1091                              1,
1092                              allocate, deallocate);
1093 }
1094 /* }}} */
1095
1096 /* {{{ apc_copy_zval */
1097 zval* apc_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
1098 {
1099     int local_dst_alloc = 0;
1100     assert(src != NULL);
1101
1102     if (!dst) {
1103 #ifdef ZEND_ENGINE_2_3
1104         CHECK(dst = (zval*) allocate(sizeof(zval_gc_info)));
1105
1106         if(allocate == apc_php_malloc) {
1107             GC_ZVAL_INIT(dst);
1108         }
1109 #else
1110         CHECK(dst = (zval*) allocate(sizeof(zval)));
1111 #endif
1112         local_dst_alloc = 1;
1113     }
1114
1115     dst = my_copy_zval(dst, src, allocate, deallocate);
1116     if(!dst) {
1117         if(local_dst_alloc) deallocate(dst);
1118         return NULL;
1119     }
1120     return dst; 
1121 }
1122 /* }}} */
1123
1124 #ifdef ZEND_ENGINE_2
1125 /* {{{ apc_fixup_op_array_jumps */
1126 static void apc_fixup_op_array_jumps(zend_op_array *dst, zend_op_array *src )
1127 {
1128     int i;
1129
1130     for (i=0; i < dst->last; ++i) {
1131         zend_op *zo = &(dst->opcodes[i]);
1132         /*convert opline number to jump address*/
1133         switch (zo->opcode) {
1134             case ZEND_JMP:
1135                 /*Note: if src->opcodes != dst->opcodes then we need to the opline according to src*/
1136                 zo->op1.u.jmp_addr = dst->opcodes + (zo->op1.u.jmp_addr - src->opcodes);
1137                 break;
1138             case ZEND_JMPZ:
1139             case ZEND_JMPNZ:
1140             case ZEND_JMPZ_EX:
1141             case ZEND_JMPNZ_EX:
1142                 zo->op2.u.jmp_addr = dst->opcodes + (zo->op2.u.jmp_addr - src->opcodes);
1143                 break;
1144             default:
1145                 break;
1146         }
1147     }
1148 }
1149 /* }}} */
1150 #endif
1151
1152 /* {{{ apc_copy_op_array */
1153 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)
1154 {
1155     int i;
1156     int local_dst_alloc = 0;
1157     apc_fileinfo_t fileinfo;
1158     char canon_path[MAXPATHLEN];
1159     char *fullpath = NULL;
1160 #ifdef ZEND_ENGINE_2
1161     apc_opflags_t * flags = NULL;
1162 #endif
1163
1164     assert(src != NULL);
1165
1166     if (!dst) {
1167         CHECK(dst = (zend_op_array*) allocate(sizeof(src[0])));
1168         local_dst_alloc = 1;
1169     }
1170
1171     if(APCG(apc_optimize_function)) {
1172         APCG(apc_optimize_function)(src TSRMLS_CC);
1173     }
1174     
1175     /* start with a bitwise copy of the array */
1176     memcpy(dst, src, sizeof(src[0]));
1177
1178     dst->function_name = NULL;
1179     dst->filename = NULL;
1180     dst->refcount = NULL;
1181     dst->opcodes = NULL;
1182     dst->brk_cont_array = NULL;
1183     dst->static_variables = NULL;
1184 #ifdef ZEND_ENGINE_2
1185     dst->try_catch_array = NULL;
1186     dst->arg_info = NULL;
1187     dst->doc_comment = NULL;
1188 #else
1189     dst->arg_types = NULL;
1190 #endif
1191 #ifdef ZEND_ENGINE_2_1
1192     dst->vars = NULL;
1193 #endif
1194
1195     /* copy the arg types array (if set) */
1196 #ifdef ZEND_ENGINE_2
1197     if (src->arg_info) {
1198         if(!(dst->arg_info = my_copy_arg_info_array(NULL,
1199                                                 src->arg_info,
1200                                                 src->num_args,
1201                                                 allocate,
1202                                                 deallocate))) {
1203             goto cleanup;
1204         }
1205     }
1206 #else    
1207     if (src->arg_types) {
1208         if(!(dst->arg_types = apc_xmemcpy(src->arg_types,
1209                         sizeof(src->arg_types[0]) * (src->arg_types[0]+1),
1210                         allocate))) {
1211             goto cleanup;
1212         }
1213     }
1214 #endif
1215
1216     if (src->function_name) {
1217         if(!(dst->function_name = apc_xstrdup(src->function_name, allocate))) {
1218             goto cleanup;
1219         }
1220     }
1221     if (src->filename) {
1222         if(!(dst->filename = apc_xstrdup(src->filename, allocate))) {
1223             goto cleanup;
1224         }
1225     }
1226
1227     if(!(dst->refcount = apc_xmemcpy(src->refcount,
1228                                       sizeof(src->refcount[0]),
1229                                       allocate))) {
1230         goto cleanup;
1231     }
1232
1233     /* deep-copy the opcodes */
1234     if(!(dst->opcodes = (zend_op*) allocate(sizeof(zend_op) * src->last))) {
1235         goto cleanup;
1236     }
1237
1238 #ifdef ZEND_ENGINE_2
1239     if(APCG(reserved_offset) != -1) {
1240         /* Insanity alert: the void* pointer is cast into an apc_opflags_t 
1241          * struct. apc_zend_init() checks to ensure that it fits in a void* */
1242         flags = (apc_opflags_t*) & (dst->reserved[APCG(reserved_offset)]);
1243         memset(flags, 0, sizeof(apc_opflags_t));
1244         /* assert(sizeof(apc_opflags_t) < sizeof(dst->reserved)); */
1245     }
1246 #endif
1247     
1248     for (i = 0; i < src->last; i++) {
1249 #ifdef ZEND_ENGINE_2
1250         zend_op *zo = &(src->opcodes[i]);
1251         /* a lot of files are merely constant arrays with no jumps */
1252         switch (zo->opcode) {
1253             case ZEND_JMP:
1254             case ZEND_JMPZ:
1255             case ZEND_JMPNZ:
1256             case ZEND_JMPZ_EX:
1257             case ZEND_JMPNZ_EX:
1258                 if(flags != NULL) {
1259                     flags->has_jumps = 1;
1260                 }
1261                 break;
1262 #ifdef ZEND_ENGINE_2
1263             /* auto_globals_jit was not in php-4.3.* */
1264             case ZEND_FETCH_R:
1265             case ZEND_FETCH_W:
1266             case ZEND_FETCH_IS:
1267             case ZEND_FETCH_FUNC_ARG:
1268                 if(PG(auto_globals_jit) && flags != NULL)
1269                 {
1270                      /* The fetch is only required if auto_globals_jit=1  */
1271                     if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL &&
1272                             zo->op1.op_type == IS_CONST && 
1273                             zo->op1.u.constant.type == IS_STRING) {
1274                         znode * varname = &zo->op1;
1275                         if (varname->u.constant.value.str.val[0] == '_') {
1276 #define SET_IF_AUTOGLOBAL(member) \
1277     if(!strcmp(varname->u.constant.value.str.val, #member)) \
1278         flags->member = 1 /* no ';' here */
1279                             SET_IF_AUTOGLOBAL(_GET);
1280                             else SET_IF_AUTOGLOBAL(_POST);
1281                             else SET_IF_AUTOGLOBAL(_COOKIE);
1282                             else SET_IF_AUTOGLOBAL(_SERVER);
1283                             else SET_IF_AUTOGLOBAL(_ENV);
1284                             else SET_IF_AUTOGLOBAL(_FILES);
1285                             else SET_IF_AUTOGLOBAL(_REQUEST);
1286                             else if(zend_is_auto_global(
1287                                             varname->u.constant.value.str.val,
1288                                             varname->u.constant.value.str.len
1289                                             TSRMLS_CC))
1290                             {
1291                                 flags->unknown_global = 1;
1292                             }
1293                         }
1294                     }
1295                 }
1296                 break;
1297 #endif
1298             case ZEND_RECV_INIT:
1299                 if(zo->op2.op_type == IS_CONST &&
1300                     zo->op2.u.constant.type == IS_CONSTANT_ARRAY) {
1301                     if(flags != NULL) {
1302                         flags->deep_copy = 1;
1303                     }
1304                 }
1305                 break;
1306             default:
1307                 if((zo->op1.op_type == IS_CONST &&
1308                     zo->op1.u.constant.type == IS_CONSTANT_ARRAY) ||
1309                     (zo->op2.op_type == IS_CONST &&
1310                         zo->op2.u.constant.type == IS_CONSTANT_ARRAY)) {
1311                     if(flags != NULL) {
1312                         flags->deep_copy = 1;
1313                     }
1314                 }
1315                 break;
1316         }
1317 #endif
1318         if(!(my_copy_zend_op(dst->opcodes+i, src->opcodes+i, allocate, deallocate))) {
1319             int ii;
1320             for(ii = i-1; ii>=0; ii--) {
1321                 my_destroy_zend_op(dst->opcodes+ii, deallocate);
1322             }
1323             goto  cleanup;
1324         }
1325 #ifdef ZEND_ENGINE_2
1326 /* This code breaks apc's rule#1 - cache what you compile */
1327         if(APCG(fpstat)==0) {
1328             if((zo->opcode == ZEND_INCLUDE_OR_EVAL) && 
1329                 (zo->op1.op_type == IS_CONST && zo->op1.u.constant.type == IS_STRING)) {
1330                 /* constant includes */
1331                 if(!IS_ABSOLUTE_PATH(Z_STRVAL_P(&zo->op1.u.constant),Z_STRLEN_P(&zo->op1.u.constant))) { 
1332                     if (apc_search_paths(Z_STRVAL_P(&zo->op1.u.constant), PG(include_path), &fileinfo) == 0) {
1333                         if((fullpath = realpath(fileinfo.fullpath, canon_path))) {
1334                             /* everything has to go through a realpath() */
1335                             zend_op *dzo = &(dst->opcodes[i]);
1336                             deallocate(dzo->op1.u.constant.value.str.val);
1337                             dzo->op1.u.constant.value.str.len = strlen(fullpath);
1338                             dzo->op1.u.constant.value.str.val = apc_xstrdup(fullpath, allocate);
1339                         }
1340                     }
1341                 }
1342             }
1343         }
1344 #endif 
1345     }
1346
1347 #ifdef ZEND_ENGINE_2
1348     if(flags == NULL || flags->has_jumps) {
1349         apc_fixup_op_array_jumps(dst,src);
1350     }
1351 #endif
1352
1353     /* copy the break-continue array */
1354     if (src->brk_cont_array) {
1355         if(!(dst->brk_cont_array =
1356             apc_xmemcpy(src->brk_cont_array,
1357                         sizeof(src->brk_cont_array[0]) * src->last_brk_cont,
1358                         allocate))) {
1359             goto cleanup_opcodes;
1360         }
1361     }
1362
1363     /* copy the table of static variables */
1364     if (src->static_variables) {
1365         if(!(dst->static_variables = my_copy_static_variables(src, allocate, deallocate))) {
1366             goto cleanup_opcodes;
1367         }
1368     }
1369     
1370 #ifdef ZEND_ENGINE_2
1371     if (src->try_catch_array) {
1372         if(!(dst->try_catch_array = 
1373                 apc_xmemcpy(src->try_catch_array,
1374                         sizeof(src->try_catch_array[0]) * src->last_try_catch,
1375                         allocate))) {
1376             goto cleanup_opcodes;
1377         }
1378     }
1379 #endif
1380
1381 #ifdef ZEND_ENGINE_2_1 /* PHP 5.1 */
1382     if (src->vars) {
1383         if(!(dst->vars = apc_xmemcpy(src->vars,
1384                             sizeof(src->vars[0]) * src->last_var,
1385                             allocate))) {
1386             goto cleanup_opcodes;
1387         }
1388         
1389         for(i = 0; i <  src->last_var; i++) dst->vars[i].name = NULL;
1390         
1391         for(i = 0; i <  src->last_var; i++) {
1392             if(!(dst->vars[i].name = apc_xmemcpy(src->vars[i].name,
1393                                 src->vars[i].name_len + 1,
1394                                 allocate))) {
1395                 dst->last_var = i;
1396                 goto cleanup_opcodes;
1397             }
1398         }
1399     }
1400 #endif
1401
1402 #ifdef ZEND_ENGINE_2
1403     if (src->doc_comment) {
1404         if (!(dst->doc_comment 
1405                 = apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) {
1406             goto cleanup_opcodes;
1407         }
1408     }
1409 #endif
1410
1411     return dst;
1412
1413 cleanup_opcodes:
1414     if(dst->opcodes) {
1415         for(i=0; i < src->last; i++) my_destroy_zend_op(dst->opcodes+i, deallocate);
1416     }
1417 cleanup:
1418     if(dst->function_name) deallocate(dst->function_name);
1419     if(dst->refcount) deallocate(dst->refcount);
1420     if(dst->filename) deallocate(dst->filename);
1421 #ifdef ZEND_ENGINE_2
1422     if(dst->arg_info) my_free_arg_info_array(dst->arg_info, dst->num_args, deallocate);
1423     if(dst->try_catch_array) deallocate(dst->try_catch_array);
1424     if(dst->doc_comment) deallocate(dst->doc_comment);
1425 #else
1426     if(dst->arg_types) deallocate(dst->arg_types);
1427 #endif
1428     if(dst->opcodes) deallocate(dst->opcodes);
1429     if(dst->brk_cont_array) deallocate(dst->brk_cont_array);
1430     if(dst->static_variables) my_free_hashtable(dst->static_variables, (ht_free_fun_t)my_free_zval_ptr, (apc_free_t)deallocate);
1431 #ifdef ZEND_ENGINE_2_1
1432     if (dst->vars) {
1433         for(i=0; i < dst->last_var; i++) {
1434             if(dst->vars[i].name) deallocate(dst->vars[i].name);    
1435         }
1436         deallocate(dst->vars);
1437     }
1438 #endif
1439     if(local_dst_alloc) deallocate(dst);
1440     return NULL;
1441 }
1442 /* }}} */
1443
1444 /* {{{ apc_copy_new_functions */
1445 apc_function_t* apc_copy_new_functions(int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC)
1446 {
1447     apc_function_t* array;
1448     int new_count;              /* number of new functions in table */
1449     int i;
1450
1451     new_count = zend_hash_num_elements(CG(function_table)) - old_count;
1452     assert(new_count >= 0);
1453
1454     CHECK(array =
1455         (apc_function_t*)
1456             allocate(sizeof(apc_function_t) * (new_count+1)));
1457
1458     if (new_count == 0) {
1459         array[0].function = NULL;
1460         return array;
1461     }
1462     
1463     /* Skip the first `old_count` functions in the table */
1464     zend_hash_internal_pointer_reset(CG(function_table));
1465     for (i = 0; i < old_count; i++) {
1466         zend_hash_move_forward(CG(function_table));
1467     }
1468
1469     /* Add the next `new_count` functions to our array */
1470     for (i = 0; i < new_count; i++) {
1471         char* key;
1472         uint key_size;
1473         zend_function* fun;
1474
1475         zend_hash_get_current_key_ex(CG(function_table),
1476                                      &key,
1477                                      &key_size,
1478                                      NULL,
1479                                      0,
1480                                      NULL);
1481
1482         zend_hash_get_current_data(CG(function_table), (void**) &fun);
1483
1484         if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) {
1485             int ii;
1486             for(ii=i-1; ii>=0; ii--) {
1487                 deallocate(array[ii].name);
1488                 my_free_function(array[ii].function, deallocate);
1489             }
1490             deallocate(array);
1491             return NULL;
1492         }
1493         array[i].name_len = (int) key_size-1;
1494         if(!(array[i].function = my_copy_function(NULL, fun, allocate, deallocate))) {
1495             int ii;
1496             deallocate(array[i].name);
1497             for(ii=i-1; ii>=0; ii--) {
1498                 deallocate(array[ii].name);
1499                 my_free_function(array[ii].function, deallocate);
1500             }
1501             deallocate(array);
1502             return NULL;
1503         }
1504         zend_hash_move_forward(CG(function_table));
1505     }
1506
1507     array[i].function = NULL;
1508     return array;
1509 }
1510 /* }}} */
1511
1512 /* {{{ apc_copy_new_classes */
1513 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)
1514 {
1515     apc_class_t* array;
1516     int new_count;              /* number of new classes in table */
1517     int i;
1518     
1519     new_count = zend_hash_num_elements(CG(class_table)) - old_count;
1520     assert(new_count >= 0);
1521
1522     CHECK(array =
1523         (apc_class_t*)
1524             allocate(sizeof(apc_class_t)*(new_count+1)));
1525     
1526     if (new_count == 0) {
1527         array[0].class_entry = NULL;
1528         return array;
1529     }
1530
1531     /* Skip the first `old_count` classes in the table */
1532     zend_hash_internal_pointer_reset(CG(class_table));
1533     for (i = 0; i < old_count; i++) {
1534         zend_hash_move_forward(CG(class_table));
1535     }
1536
1537     /* Add the next `new_count` classes to our array */
1538     for (i = 0; i < new_count; i++) {
1539         char* key;
1540         uint key_size;
1541         zend_class_entry* elem = NULL;
1542
1543         array[i].class_entry = NULL;
1544
1545         zend_hash_get_current_key_ex(CG(class_table),
1546                                      &key,
1547                                      &key_size,
1548                                      NULL,
1549                                      0,
1550                                      NULL);
1551
1552        zend_hash_get_current_data(CG(class_table), (void**) &elem);
1553   
1554         
1555 #ifdef ZEND_ENGINE_2
1556                 elem = *((zend_class_entry**)elem);
1557 #endif
1558         
1559         if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) {
1560             int ii;
1561
1562             for(ii=i-1; ii>=0; ii--) {
1563                 deallocate(array[ii].name);
1564                 my_destroy_class_entry(array[ii].class_entry, deallocate);
1565                 deallocate(array[ii].class_entry);
1566             }
1567             deallocate(array);
1568             return NULL;
1569         }
1570         array[i].name_len = (int) key_size-1;
1571         if(!(array[i].class_entry = my_copy_class_entry(NULL, elem, allocate, deallocate))) {
1572             int ii;
1573             
1574             deallocate(array[i].name);
1575             for(ii=i-1; ii>=0; ii--) {
1576                 deallocate(array[ii].name);
1577                 my_destroy_class_entry(array[ii].class_entry, deallocate);
1578                 deallocate(array[ii].class_entry);
1579             }
1580             deallocate(array);
1581             return NULL;
1582         }
1583
1584         /*
1585          * If the class has a pointer to its parent class, save the parent
1586          * name so that we can enable compile-time inheritance when we reload
1587          * the child class; otherwise, set the parent name to null and scan
1588          * the op_array to determine if this class inherits from some base
1589          * class at execution-time.
1590          */
1591
1592         if (elem->parent) {
1593             if(!(array[i].parent_name =
1594                 apc_xstrdup(elem->parent->name, allocate))) {
1595                 int ii;
1596                  
1597                 for(ii=i; ii>=0; ii--) {
1598                     deallocate(array[ii].name);
1599                     my_destroy_class_entry(array[ii].class_entry, deallocate);
1600                     deallocate(array[ii].class_entry);
1601                     if(ii==i) continue;
1602                     if(array[ii].parent_name) deallocate(array[ii].parent_name);
1603                 }
1604                 deallocate(array);
1605                 return NULL;
1606             }
1607             array[i].is_derived = 1;
1608         }
1609         else {
1610             array[i].parent_name = NULL;
1611             array[i].is_derived = is_derived_class(op_array, key, key_size);
1612         }
1613
1614         zend_hash_move_forward(CG(class_table));
1615     }
1616
1617     array[i].class_entry = NULL;
1618     return array;
1619 }
1620 /* }}} */
1621
1622 /* {{{ my_destroy_zval_ptr */
1623 static void my_destroy_zval_ptr(zval** src, apc_free_t deallocate)
1624 {
1625     assert(src != NULL);
1626     if(my_destroy_zval(src[0], deallocate) == SUCCESS) {
1627         deallocate(src[0]);
1628     }
1629 }
1630 /* }}} */
1631
1632 /* {{{ my_destroy_zval */
1633 static int my_destroy_zval(zval* src, apc_free_t deallocate)
1634 {
1635     zval **tmp;
1636     TSRMLS_FETCH();
1637
1638     switch (src->type & ~IS_CONSTANT_INDEX) {
1639     case IS_RESOURCE:
1640     case IS_BOOL:
1641     case IS_LONG:
1642     case IS_DOUBLE:
1643     case IS_NULL:
1644         break;
1645
1646     case IS_CONSTANT:
1647     case IS_STRING:
1648 #ifndef ZEND_ENGINE_2        
1649     case FLAG_IS_BC:
1650 #endif        
1651         deallocate(src->value.str.val);
1652         break;
1653     
1654     case IS_ARRAY:
1655     
1656         /* Maintain a list of zvals we've copied to properly handle recursive structures */
1657         if(APCG(copied_zvals)) {
1658             if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) {
1659                 Z_DELREF_PP(tmp);
1660                 return FAILURE;
1661             } 
1662             zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&src, sizeof(zval*), NULL);
1663         }
1664         /* fall through */
1665
1666     case IS_CONSTANT_ARRAY:
1667         my_free_hashtable(src->value.ht,
1668                           (ht_free_fun_t) my_free_zval_ptr,
1669                           deallocate);
1670         break;
1671
1672     case IS_OBJECT:
1673 #ifndef ZEND_ENGINE_2        
1674         my_destroy_class_entry(src->value.obj.ce, deallocate);
1675         deallocate(src->value.obj.ce);
1676         my_free_hashtable(src->value.obj.properties,
1677                           (ht_free_fun_t) my_free_zval_ptr,
1678                           deallocate);
1679 #endif        
1680         break;
1681
1682     default:
1683         assert(0);
1684     }
1685
1686     return SUCCESS;
1687 }
1688 /* }}} */
1689
1690 /* {{{ my_destroy_znode */
1691 static void my_destroy_znode(znode* src, apc_free_t deallocate)
1692 {
1693     if (src->op_type == IS_CONST) {
1694         my_destroy_zval(&src->u.constant, deallocate);
1695     }
1696 }
1697 /* }}} */
1698
1699 /* {{{ my_destroy_zend_op */
1700 static void my_destroy_zend_op(zend_op* src, apc_free_t deallocate)
1701 {
1702     my_destroy_znode(&src->result, deallocate);
1703     my_destroy_znode(&src->op1, deallocate);
1704     my_destroy_znode(&src->op2, deallocate);
1705 }
1706 /* }}} */
1707
1708 /* {{{ my_destroy_function */
1709 static void my_destroy_function(zend_function* src, apc_free_t deallocate)
1710 {
1711     assert(src != NULL);
1712
1713     switch (src->type) {
1714     case ZEND_INTERNAL_FUNCTION:
1715     case ZEND_OVERLOADED_FUNCTION:
1716         break;
1717         
1718     case ZEND_USER_FUNCTION:
1719     case ZEND_EVAL_CODE:
1720         my_destroy_op_array(&src->op_array, deallocate);
1721         break;
1722
1723     default:
1724         assert(0);
1725     }
1726 }
1727 /* }}} */
1728
1729 /* {{{ my_destroy_function_entry */
1730 static void my_destroy_function_entry(zend_function_entry* src, apc_free_t deallocate)
1731 {
1732     assert(src != NULL);
1733
1734     deallocate((char*)src->fname);
1735 #ifdef ZEND_ENGINE_2    
1736     if (src->arg_info) {
1737             my_free_arg_info_array((zend_arg_info*)src->arg_info, src->num_args, deallocate);
1738     }
1739 #else
1740     if (src->func_arg_types) {
1741         deallocate(src->func_arg_types);
1742     }
1743 #endif    
1744 }
1745 /* }}} */
1746
1747 #ifdef ZEND_ENGINE_2    
1748 /* {{{ my_destroy_property_info*/
1749 static void my_destroy_property_info(zend_property_info* src, apc_free_t deallocate)
1750 {
1751     assert(src != NULL);
1752
1753     deallocate(src->name);
1754 #if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0
1755     if(src->doc_comment) deallocate(src->doc_comment);
1756 #endif
1757 }
1758 /* }}} */
1759
1760 /* {{{ my_destroy_arg_info_array */
1761 static void my_destroy_arg_info_array(zend_arg_info* src, uint num_args, apc_free_t deallocate)
1762 {
1763     int i = 0;
1764     
1765     assert(src != NULL);
1766
1767     for(i=0; i < num_args; i++) {
1768         my_destroy_arg_info(&src[i], deallocate);
1769     }
1770 }
1771 /* }}} */
1772
1773 /* {{{ my_destroy_arg_info */
1774 static void my_destroy_arg_info(zend_arg_info* src, apc_free_t deallocate)
1775 {
1776     assert(src != NULL);
1777
1778     deallocate((char*)src->name);
1779     deallocate((char*)src->class_name);
1780 }
1781 /* }}} */
1782 #endif    
1783
1784 /* {{{ my_destroy_class_entry */
1785 static void my_destroy_class_entry(zend_class_entry* src, apc_free_t deallocate)
1786 {
1787     uint i;
1788
1789     assert(src != NULL);
1790
1791     deallocate(src->name);
1792 #ifndef ZEND_ENGINE_2    
1793     deallocate(src->refcount);
1794 #else
1795     if(src->doc_comment) deallocate(src->doc_comment);
1796     if(src->filename) deallocate(src->filename);
1797 #endif
1798
1799     my_destroy_hashtable(&src->function_table,
1800                          (ht_free_fun_t) my_free_function,
1801                          deallocate);
1802
1803     my_destroy_hashtable(&src->default_properties,
1804                          (ht_free_fun_t) my_free_zval_ptr,
1805                          deallocate);
1806
1807 #ifdef ZEND_ENGINE_2
1808     my_destroy_hashtable(&src->properties_info, 
1809                             (ht_free_fun_t) my_free_property_info,
1810                             deallocate);
1811     if(src->static_members) 
1812     {
1813         my_destroy_hashtable(src->static_members,
1814                          (ht_free_fun_t) my_free_zval_ptr,
1815                          deallocate);
1816         if(src->static_members != &(src->default_static_members))
1817         {
1818             deallocate(src->static_members);
1819         }
1820     }
1821
1822     my_destroy_hashtable(&src->constants_table, 
1823                             (ht_free_fun_t) my_free_zval_ptr,
1824                             deallocate);
1825 #endif
1826
1827     if (src->builtin_functions) {
1828         for (i = 0; src->builtin_functions[i].fname != NULL; i++) {
1829             my_destroy_function_entry((zend_function_entry*)(&src->builtin_functions[i]), deallocate);
1830         }
1831         deallocate((zend_function_entry*)src->builtin_functions);
1832     }
1833 }
1834 /* }}} */
1835
1836 /* {{{ my_destroy_hashtable */
1837 static void my_destroy_hashtable(HashTable* src, ht_free_fun_t free_fn, apc_free_t deallocate)
1838 {
1839     int i;
1840
1841     assert(src != NULL);
1842
1843     for (i = 0; i < src->nTableSize; i++) {
1844         Bucket* p = src->arBuckets[i];
1845         while (p != NULL) {
1846             Bucket* q = p;
1847             p = p->pNext;
1848             free_fn(q->pData, deallocate);
1849             deallocate(q);
1850         }
1851     }
1852
1853     deallocate(src->arBuckets);
1854 }
1855 /* }}} */
1856
1857 /* {{{ my_destroy_op_array */
1858 static void my_destroy_op_array(zend_op_array* src, apc_free_t deallocate)
1859 {
1860     int i;
1861
1862     assert(src != NULL);
1863
1864 #ifdef ZEND_ENGINE_2
1865     if (src->arg_info) {
1866         my_free_arg_info_array(src->arg_info, src->num_args, deallocate);
1867     }
1868 #else    
1869     if (src->arg_types) {
1870         deallocate(src->arg_types);
1871     }
1872 #endif
1873
1874     deallocate(src->function_name);
1875     deallocate(src->filename);
1876     deallocate(src->refcount);
1877
1878     for (i = 0; i < src->last; i++) {
1879         my_destroy_zend_op(src->opcodes + i, deallocate);
1880     }
1881     deallocate(src->opcodes);
1882
1883     if (src->brk_cont_array) {
1884         deallocate(src->brk_cont_array);
1885     }
1886
1887     if (src->static_variables) {
1888         my_free_hashtable(src->static_variables,
1889                           (ht_free_fun_t) my_free_zval_ptr,
1890                           deallocate);
1891     }
1892     
1893 #ifdef ZEND_ENGINE_2_1
1894     if (src->vars) {
1895         for(i=0; i < src->last_var; i++) {
1896             if(src->vars[i].name) deallocate(src->vars[i].name);    
1897         }
1898         deallocate(src->vars);
1899     }
1900 #endif
1901 #ifdef ZEND_ENGINE_2
1902     if(src->try_catch_array) {
1903         deallocate(src->try_catch_array);
1904     }
1905     if (src->doc_comment) {
1906         deallocate(src->doc_comment);
1907     }
1908 #endif
1909 }
1910 /* }}} */
1911
1912 /* {{{ my_free_zval_ptr */
1913 static void my_free_zval_ptr(zval** src, apc_free_t deallocate)
1914 {
1915     my_destroy_zval_ptr(src, deallocate);
1916     deallocate(src);
1917 }
1918 /* }}} */
1919
1920 #ifdef ZEND_ENGINE_2
1921 /* {{{ my_free_property_info */
1922 static void my_free_property_info(zend_property_info* src, apc_free_t deallocate)
1923 {
1924     my_destroy_property_info(src, deallocate);
1925     deallocate(src);
1926 }
1927 /* }}} */
1928
1929 /* {{{ my_free_arg_info_array */
1930 static void my_free_arg_info_array(zend_arg_info* src, uint num_args, apc_free_t deallocate)
1931 {
1932     my_destroy_arg_info_array(src, num_args, deallocate);
1933     deallocate(src);
1934 }
1935 /* }}} */
1936
1937 /* {{{ my_free_arg_info */
1938 static void my_free_arg_info(zend_arg_info* src, apc_free_t deallocate)
1939 {
1940     my_destroy_arg_info(src, deallocate);
1941     deallocate(src);
1942 }
1943 /* }}} */
1944 #endif
1945
1946 /* {{{ my_free_function */
1947 static void my_free_function(zend_function* src, apc_free_t deallocate)
1948 {
1949     my_destroy_function(src, deallocate);
1950     deallocate(src);
1951 }
1952 /* }}} */
1953
1954 /* {{{ my_free_hashtable */
1955 static void my_free_hashtable(HashTable* src, ht_free_fun_t free_fn, apc_free_t deallocate)
1956 {
1957     my_destroy_hashtable(src, free_fn, deallocate);
1958     deallocate(src);
1959 }
1960 /* }}} */
1961
1962 /* {{{ apc_free_op_array */
1963 void apc_free_op_array(zend_op_array* src, apc_free_t deallocate)
1964 {
1965     if (src != NULL) {
1966         my_destroy_op_array(src, deallocate);
1967         deallocate(src);
1968     }
1969 }
1970 /* }}} */
1971
1972 /* {{{ apc_free_functions */
1973 void apc_free_functions(apc_function_t* src, apc_free_t deallocate)
1974 {
1975     int i;
1976
1977     if (src != NULL) {
1978         for (i = 0; src[i].function != NULL; i++) {
1979             deallocate(src[i].name);
1980             my_destroy_function(src[i].function, deallocate);
1981             deallocate(src[i].function);
1982         }   
1983         deallocate(src);
1984     }   
1985 }
1986 /* }}} */
1987
1988 /* {{{ apc_free_classes */
1989 void apc_free_classes(apc_class_t* src, apc_free_t deallocate)
1990 {
1991     int i;
1992
1993     if (src != NULL) {
1994         for (i = 0; src[i].class_entry != NULL; i++) {
1995             deallocate(src[i].name);
1996             deallocate(src[i].parent_name);
1997             my_destroy_class_entry(src[i].class_entry, deallocate);
1998             deallocate(src[i].class_entry);
1999         }   
2000         deallocate(src);
2001     }   
2002 }
2003 /* }}} */
2004
2005 /* {{{ apc_free_zval */
2006 void apc_free_zval(zval* src, apc_free_t deallocate)
2007 {
2008     if (src != NULL) {
2009         if(my_destroy_zval(src, deallocate) == SUCCESS) {
2010             deallocate(src);
2011         }
2012     }
2013 }
2014 /* }}} */
2015
2016
2017 /* Used only by my_prepare_op_array_for_execution */
2018 #define APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION()                                                \
2019                          /* The fetch is only required if auto_globals_jit=1  */                \
2020                         if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL &&                            \
2021                             zo->op1.op_type == IS_CONST &&                                      \
2022                             zo->op1.u.constant.type == IS_STRING &&                             \
2023                             zo->op1.u.constant.value.str.val[0] == '_') {                       \
2024                                                                                                 \
2025                             znode* varname = &zo->op1;                                          \
2026                             (void)zend_is_auto_global(varname->u.constant.value.str.val,        \
2027                                                           varname->u.constant.value.str.len     \
2028                                                           TSRMLS_CC);                           \
2029                         }                                                                       \
2030
2031 /* {{{ my_prepare_op_array_for_execution */
2032 static int my_prepare_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC) 
2033 {
2034     /* combine my_fetch_global_vars and my_copy_data_exceptions.
2035      *   - Pre-fetch superglobals which would've been pre-fetched in parse phase.
2036      *   - If the opcode stream contain mutable data, ensure a copy.
2037      *   - Fixup array jumps in the same loop.
2038      */
2039     int i=src->last;
2040     zend_op *zo;
2041     zend_op *dzo;
2042 #ifdef ZEND_ENGINE_2
2043     apc_opflags_t * flags = APCG(reserved_offset) != -1 ? 
2044                                 (apc_opflags_t*) & (src->reserved[APCG(reserved_offset)]) : NULL;
2045     int needcopy = flags ? flags->deep_copy : 1;
2046     /* auto_globals_jit was not in php4 */
2047     int do_prepare_fetch_global = PG(auto_globals_jit) && (flags == NULL || flags->unknown_global);
2048
2049 #define FETCH_AUTOGLOBAL(member) do { \
2050     if(flags && flags->member == 1) { \
2051         zend_is_auto_global(#member,\
2052                             (sizeof(#member) - 1)\
2053                             TSRMLS_CC);\
2054     } \
2055 }while(0); 
2056             
2057     FETCH_AUTOGLOBAL(_GET);
2058     FETCH_AUTOGLOBAL(_POST);
2059     FETCH_AUTOGLOBAL(_COOKIE);
2060     FETCH_AUTOGLOBAL(_SERVER);
2061     FETCH_AUTOGLOBAL(_ENV);
2062     FETCH_AUTOGLOBAL(_FILES);
2063     FETCH_AUTOGLOBAL(_REQUEST);
2064
2065 #else
2066     int needcopy = 0;
2067     int do_prepare_fetch_global = 0;
2068     int j = 0;
2069
2070     for(j = 0; j < src->last; j++) {
2071         zo = &src->opcodes[j];
2072         
2073         if( ((zo->op1.op_type == IS_CONST &&
2074               zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) ||  
2075             ((zo->op2.op_type == IS_CONST &&
2076               zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) {
2077             needcopy = 1;
2078         }
2079     }
2080 #endif
2081     
2082     if(needcopy) {
2083
2084         dst->opcodes = (zend_op*) apc_xmemcpy(src->opcodes, 
2085                                     sizeof(zend_op) * src->last,
2086                                     apc_php_malloc);
2087         zo = src->opcodes;
2088         dzo = dst->opcodes;
2089         while(i > 0) {
2090
2091             if( ((zo->op1.op_type == IS_CONST &&
2092                   zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) ||  
2093                 ((zo->op2.op_type == IS_CONST &&
2094                   zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) {
2095
2096                 if(!(my_copy_zend_op(dzo, zo, apc_php_malloc, apc_php_free))) {
2097                     assert(0); /* emalloc failed or a bad constant array */
2098                 }
2099             }
2100             
2101 #ifdef ZEND_ENGINE_2
2102             switch(zo->opcode) {
2103                 case ZEND_JMP:
2104                     dzo->op1.u.jmp_addr = dst->opcodes + 
2105                                             (zo->op1.u.jmp_addr - src->opcodes);
2106                     break;
2107                 case ZEND_JMPZ:
2108                 case ZEND_JMPNZ:
2109                 case ZEND_JMPZ_EX:
2110                 case ZEND_JMPNZ_EX:
2111                     dzo->op2.u.jmp_addr = dst->opcodes + 
2112                                             (zo->op2.u.jmp_addr - src->opcodes);
2113                     break;
2114                 case ZEND_FETCH_R:
2115                 case ZEND_FETCH_W:
2116                 case ZEND_FETCH_IS:
2117                 case ZEND_FETCH_FUNC_ARG:
2118                     if(do_prepare_fetch_global)
2119                     {
2120                         APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION();
2121                     }
2122                     break;
2123                 default:
2124                     break;
2125             }
2126 #endif
2127             i--;
2128             zo++;
2129             dzo++;
2130         }
2131 #ifdef ZEND_ENGINE_2
2132     } else {  /* !needcopy */
2133         /* The fetch is only required if auto_globals_jit=1  */
2134         if(do_prepare_fetch_global)
2135         {
2136             zo = src->opcodes;
2137             while(i > 0) {
2138
2139                 if(zo->opcode == ZEND_FETCH_R || 
2140                    zo->opcode == ZEND_FETCH_W ||
2141                    zo->opcode == ZEND_FETCH_IS ||
2142                    zo->opcode == ZEND_FETCH_FUNC_ARG 
2143                   ) {
2144                     APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION();
2145                 }
2146
2147                 i--;
2148                 zo++;
2149             }
2150         }
2151 #endif
2152     }
2153     return 1;
2154 }
2155 /* }}} */
2156
2157 /* {{{ apc_copy_op_array_for_execution */
2158 zend_op_array* apc_copy_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC)
2159 {
2160     if(dst == NULL) {
2161         dst = (zend_op_array*) emalloc(sizeof(src[0]));
2162     }
2163     memcpy(dst, src, sizeof(src[0]));
2164     dst->static_variables = my_copy_static_variables(src, apc_php_malloc, apc_php_free);
2165
2166     dst->refcount = apc_xmemcpy(src->refcount,
2167                                       sizeof(src->refcount[0]),
2168                                       apc_php_malloc);
2169
2170     my_prepare_op_array_for_execution(dst,src TSRMLS_CC);
2171
2172     return dst;
2173 }
2174 /* }}} */
2175
2176 /* {{{ apc_copy_function_for_execution */
2177 zend_function* apc_copy_function_for_execution(zend_function* src)
2178 {
2179     zend_function* dst;
2180     TSRMLS_FETCH();
2181
2182     dst = (zend_function*) emalloc(sizeof(src[0]));
2183     memcpy(dst, src, sizeof(src[0]));
2184     apc_copy_op_array_for_execution(&(dst->op_array), &(src->op_array) TSRMLS_CC);
2185     return dst;
2186 }
2187 /* }}} */
2188
2189 /* {{{ apc_copy_function_for_execution_ex */
2190 zend_function* apc_copy_function_for_execution_ex(void *dummy, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate)
2191 {
2192     if(src->type==ZEND_INTERNAL_FUNCTION || src->type==ZEND_OVERLOADED_FUNCTION) return src;
2193     return apc_copy_function_for_execution(src);
2194 }
2195 /* }}} */
2196
2197 /* {{{ apc_copy_class_entry_for_execution */
2198 zend_class_entry* apc_copy_class_entry_for_execution(zend_class_entry* src, int is_derived)
2199 {
2200     zend_class_entry* dst = (zend_class_entry*) emalloc(sizeof(src[0]));
2201     memcpy(dst, src, sizeof(src[0]));
2202
2203 #ifdef ZEND_ENGINE_2
2204     if(src->num_interfaces)
2205     {
2206         /* These are slots to be populated later by ADD_INTERFACE insns */
2207         dst->interfaces = apc_php_malloc(
2208                             sizeof(zend_class_entry*) * src->num_interfaces);
2209         memset(dst->interfaces, 0, 
2210                             sizeof(zend_class_entry*) * src->num_interfaces);
2211     }
2212     else
2213     {
2214         /* assert(dst->interfaces == NULL); */
2215     }
2216 #endif
2217
2218 #ifndef ZEND_ENGINE_2    
2219     dst->refcount = apc_xmemcpy(src->refcount,
2220                                       sizeof(src->refcount[0]),
2221                                       apc_php_malloc);
2222 #endif        
2223
2224     /* Deep-copy the class properties, because they will be modified */
2225
2226     my_copy_hashtable(&dst->default_properties,
2227                       &src->default_properties,
2228                       (ht_copy_fun_t) my_copy_zval_ptr,
2229                       (ht_free_fun_t) my_free_zval_ptr,
2230                       1,
2231                       apc_php_malloc, apc_php_free);
2232
2233     /* For derived classes, we must also copy the function hashtable (although
2234      * we can merely bitwise copy the functions it contains) */
2235
2236     my_copy_hashtable(&dst->function_table,
2237                       &src->function_table,
2238                       (ht_copy_fun_t) apc_copy_function_for_execution_ex,
2239                       NULL,
2240                       0,
2241                       apc_php_malloc, apc_php_free);
2242 #ifdef ZEND_ENGINE_2
2243     my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function_for_execution, src, dst);
2244
2245     /* zend_do_inheritance merges properties_info.
2246      * Need only shallow copying as it doesn't hold the pointers.
2247      */
2248     my_copy_hashtable(&dst->properties_info,
2249                       &src->properties_info,
2250                       (ht_copy_fun_t) my_copy_property_info_for_execution,
2251                       NULL,
2252                       0,
2253                       apc_php_malloc, apc_php_free);
2254
2255 #ifdef ZEND_ENGINE_2_2
2256     /* php5.2 introduced a scope attribute for property info */
2257     my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst);
2258 #endif
2259
2260     /* if inheritance results in a hash_del, it might result in
2261      * a pefree() of the pointers here. Deep copying required. 
2262      */
2263
2264     my_copy_hashtable(&dst->constants_table,
2265                       &src->constants_table,
2266                       (ht_copy_fun_t) my_copy_zval_ptr,
2267                       NULL,
2268                       1,
2269                       apc_php_malloc, apc_php_free);
2270
2271     my_copy_hashtable(&dst->default_static_members,
2272                       &src->default_static_members,
2273                       (ht_copy_fun_t) my_copy_zval_ptr,
2274                       (ht_free_fun_t) my_free_zval_ptr,
2275                       1,
2276                       apc_php_malloc, apc_php_free);
2277
2278     if(src->static_members != &(src->default_static_members))
2279     {
2280         dst->static_members = my_copy_hashtable(NULL,
2281                           src->static_members,
2282                           (ht_copy_fun_t) my_copy_zval_ptr,
2283                           (ht_free_fun_t) my_free_zval_ptr,
2284                           1,
2285                           apc_php_malloc, apc_php_free);
2286     }
2287     else 
2288     {
2289         dst->static_members = &(dst->default_static_members);
2290     }
2291
2292 #endif
2293
2294     return dst;
2295 }
2296 /* }}} */
2297
2298 /* {{{ apc_free_class_entry_after_execution */
2299 void apc_free_class_entry_after_execution(zend_class_entry* src)
2300 {
2301 #ifdef ZEND_ENGINE_2
2302     if(src->num_interfaces > 0 && src->interfaces) {
2303         apc_php_free(src->interfaces);
2304         src->interfaces = NULL;
2305         src->num_interfaces = 0;
2306     }
2307     /* my_destroy_hashtable() does not play nice with refcounts */
2308
2309     zend_hash_clean(&src->default_static_members);
2310     if(src->static_members != &(src->default_static_members))
2311     {
2312         zend_hash_destroy(src->static_members);
2313         apc_php_free(src->static_members);
2314         src->static_members = NULL;
2315     }
2316     else
2317     {
2318         src->static_members = NULL;
2319     }
2320     zend_hash_clean(&src->default_properties);
2321     zend_hash_clean(&src->constants_table);
2322 #endif
2323
2324     /* TODO: more cleanup */
2325 }
2326 /* }}} */
2327
2328 #ifdef ZEND_ENGINE_2
2329
2330 /* {{{ my_fixup_function */
2331 static void my_fixup_function(Bucket *p, zend_class_entry *src, zend_class_entry *dst)
2332 {
2333     zend_function* zf = p->pData;
2334
2335     #define SET_IF_SAME_NAME(member) \
2336     do { \
2337         if(src->member && !strcmp(zf->common.function_name, src->member->common.function_name)) { \
2338             dst->member = zf; \
2339         } \
2340     } \
2341     while(0)
2342
2343     if(zf->common.scope == src)
2344     {
2345     
2346         /* Fixing up the default functions for objects here since
2347          * we need to compare with the newly allocated functions
2348          *
2349          * caveat: a sub-class method can have the same name as the
2350          * parent's constructor and create problems.
2351          */
2352         
2353         if(zf->common.fn_flags & ZEND_ACC_CTOR) dst->constructor = zf;
2354         else if(zf->common.fn_flags & ZEND_ACC_DTOR) dst->destructor = zf;
2355         else if(zf->common.fn_flags & ZEND_ACC_CLONE) dst->clone = zf;
2356         else
2357         {
2358             SET_IF_SAME_NAME(__get);
2359             SET_IF_SAME_NAME(__set);
2360             SET_IF_SAME_NAME(__unset);
2361             SET_IF_SAME_NAME(__isset);
2362             SET_IF_SAME_NAME(__call);
2363 #ifdef ZEND_ENGINE_2_2
2364             SET_IF_SAME_NAME(__tostring);
2365 #endif
2366         }
2367         zf->common.scope = dst;
2368     }
2369     else
2370     {
2371         /* no other function should reach here */
2372         assert(0);
2373     }
2374
2375     #undef SET_IF_SAME_NAME
2376 }
2377 /* }}} */
2378
2379 #ifdef ZEND_ENGINE_2_2
2380 /* {{{ my_fixup_property_info */
2381 static void my_fixup_property_info(Bucket *p, zend_class_entry *src, zend_class_entry *dst)
2382 {
2383     zend_property_info* property_info = (zend_property_info*)p->pData;
2384
2385     if(property_info->ce == src)
2386     {
2387         property_info->ce = dst;
2388     }
2389     else
2390     {
2391         assert(0); /* should never happen */
2392     }
2393 }
2394 /* }}} */
2395 #endif
2396
2397 /* {{{ my_fixup_hashtable */
2398 static void my_fixup_hashtable(HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst)
2399 {
2400     Bucket *p;
2401     
2402         uint i;
2403     
2404         for (i = 0; i < ht->nTableSize; i++) {
2405                 if(!ht->arBuckets) break;
2406         p = ht->arBuckets[i];
2407                 while (p != NULL) {
2408             fixup(p, src, dst);
2409                         p = p->pNext;
2410                 }
2411         }
2412 }
2413 /* }}} */
2414
2415 #endif
2416
2417 /* {{{ my_check_copy_function */
2418 static int my_check_copy_function(Bucket* p, va_list args)
2419 {
2420     zend_class_entry* src = va_arg(args, zend_class_entry*);
2421     zend_function* zf = (zend_function*)p->pData;
2422 #ifndef ZEND_ENGINE_2
2423     zend_class_entry* parent = src->parent;
2424     zend_function* parent_fn = NULL;
2425 #endif
2426
2427 #ifdef ZEND_ENGINE_2
2428     return (zf->common.scope == src);
2429 #else
2430         if (parent &&
2431         zend_hash_quick_find(&parent->function_table, p->arKey, 
2432             p->nKeyLength, p->h, (void **) &parent_fn)==SUCCESS) {
2433         
2434         if((parent_fn && zf) && 
2435                 (parent_fn->op_array.refcount == zf->op_array.refcount))
2436         {
2437             return 0;
2438         }
2439     }
2440     return 1;
2441 #endif 
2442 }
2443 /* }}} */
2444
2445 /* {{{ my_check_copy_default_property */
2446 static int my_check_copy_default_property(Bucket* p, va_list args)
2447 {
2448     zend_class_entry* src = va_arg(args, zend_class_entry*);
2449     zend_class_entry* parent = src->parent;
2450     zval ** child_prop = (zval**)p->pData;
2451     zval ** parent_prop = NULL;
2452
2453         if (parent &&
2454         zend_hash_quick_find(&parent->default_properties, p->arKey, 
2455             p->nKeyLength, p->h, (void **) &parent_prop)==SUCCESS) {
2456
2457         if((parent_prop && child_prop) && (*parent_prop) == (*child_prop))
2458         {
2459             return 0;
2460         }
2461     }
2462     
2463     /* possibly not in the parent */
2464     return 1;
2465 }
2466 /* }}} */
2467
2468 #ifdef ZEND_ENGINE_2
2469
2470 /* {{{ my_check_copy_property_info */
2471 static int my_check_copy_property_info(Bucket* p, va_list args)
2472 {
2473     zend_class_entry* src = va_arg(args, zend_class_entry*);
2474     zend_class_entry* parent = src->parent;
2475     zend_property_info* child_info = (zend_property_info*)p->pData;
2476     zend_property_info* parent_info = NULL;
2477
2478 #ifdef ZEND_ENGINE_2_2
2479     /* so much easier */
2480     return (child_info->ce == src);
2481 #endif
2482
2483         if (parent &&
2484         zend_hash_quick_find(&parent->properties_info, p->arKey, p->nKeyLength, 
2485             p->h, (void **) &parent_info)==SUCCESS) {
2486         if(parent_info->flags & ZEND_ACC_PRIVATE)
2487         {
2488             return 1;
2489         }
2490         if((parent_info->flags & ZEND_ACC_PPP_MASK) != 
2491             (child_info->flags & ZEND_ACC_PPP_MASK))
2492         {
2493             /* TODO: figure out whether ACC_CHANGED is more appropriate
2494              * here */
2495             return 1;
2496         }
2497         return 0;
2498     }
2499     
2500     /* property doesn't exist in parent, copy into cached child */
2501     return 1;
2502 }
2503 /* }}} */
2504
2505 /* {{{ my_check_copy_static_member */
2506 static int my_check_copy_static_member(Bucket* p, va_list args)
2507 {
2508     zend_class_entry* src = va_arg(args, zend_class_entry*);
2509     HashTable * ht = va_arg(args, HashTable*);
2510     zend_class_entry* parent = src->parent;
2511     HashTable * parent_ht = NULL;
2512     char * member_name;
2513     char * class_name = NULL;
2514
2515     zend_property_info *parent_info = NULL;
2516     zend_property_info *child_info = NULL;
2517     zval ** parent_prop = NULL;
2518     zval ** child_prop = (zval**)(p->pData);
2519
2520     if(!parent) {
2521         return 1;
2522     }
2523
2524     /* these do not need free'ing */
2525 #ifdef ZEND_ENGINE_2_2
2526     zend_unmangle_property_name(p->arKey, p->nKeyLength-1, &class_name, &member_name);
2527 #else
2528     zend_unmangle_property_name(p->arKey, &class_name, &member_name);
2529 #endif
2530
2531     /* please refer do_inherit_property_access_check in zend_compile.c
2532      * to understand why we lookup in properties_info.
2533      */
2534     if((zend_hash_find(&parent->properties_info, member_name, 
2535                         strlen(member_name)+1, (void**)&parent_info) == SUCCESS)
2536         &&
2537         (zend_hash_find(&src->properties_info, member_name,
2538                         strlen(member_name)+1, (void**)&child_info) == SUCCESS))
2539     {
2540         if(child_info->flags & ZEND_ACC_STATIC &&    
2541             (parent_info->flags & ZEND_ACC_PROTECTED &&
2542             child_info->flags & ZEND_ACC_PUBLIC))
2543         {
2544             /* Do not copy into static_members. zend_do_inheritance
2545              * will automatically insert a NULL value.
2546              * TODO: decrement refcount or fixup when copying out for exec ? 
2547              */ 
2548             return 0;
2549         }
2550         if(ht == &(src->default_static_members))
2551         {
2552             parent_ht = &parent->default_static_members;
2553         }
2554         else
2555         {
2556             parent_ht = parent->static_members;
2557         }
2558
2559         if(zend_hash_quick_find(parent_ht, p->arKey,
2560                        p->nKeyLength, p->h, (void**)&parent_prop) == SUCCESS)
2561         {
2562             /* they point to the same zval */
2563             if(*parent_prop == *child_prop)
2564             {
2565                 return 0;
2566             }
2567         }
2568     }
2569     
2570     return 1;
2571 }
2572 /* }}} */
2573 #endif
2574
2575 /* {{{ apc_register_optimizer(apc_optimize_function_t optimizer)
2576  *      register a optimizer callback function, returns the previous callback
2577  */
2578 apc_optimize_function_t apc_register_optimizer(apc_optimize_function_t optimizer TSRMLS_DC) {
2579     apc_optimize_function_t old_optimizer = APCG(apc_optimize_function);
2580     APCG(apc_optimize_function) = optimizer;
2581     return old_optimizer;
2582 }
2583
2584 /*
2585  * Local variables:
2586  * tab-width: 4
2587  * c-basic-offset: 4
2588  * End:
2589  * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
2590  * vim<600: expandtab sw=4 ts=4 sts=4
2591  */