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